I've got a view
<UserControl x:Class="Modules.NavigationMenu.Views.NavigationMenuView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<StackPanel>
<Button Command="{Binding InspectionCommand}">Inspection</Button>
<Button Command="{Binding HandheldCommand}">Handheld</Button>
</StackPanel>
</UserControl>
and a simple view model - but the commands won't seem to fire - can anyone point me in the right direction please?
public class NavigationMenuViewModel : INavigationMenuViewModel
{
public INavigationMenuView View { get; set; }
public NavigationMenuViewModel(IEventAggregator eventAggregator, INavigationMenuView view)
{
View = view;
HandheldCommand = new DelegateCommand<object>(LaunchHandheld);
InspectionCommand = new DelegateCommand<object>(LaunchInspection);
}
private void LaunchInspection(object obj)
{
MessageBox.Show("Inspection Clicked");
}
private void LaunchHandheld(object obj)
{
MessageBox.Show("Handheld Clicked");
}
public DelegateCommand<object> HandheldCommand { get; set; }
public DelegateCommand<object> InspectionCommand { get; set; }
}
My Module just looks like ...
public class NavigationMenuModule : IModule
{
private readonly IUnityContainer _container;
private readonly IRegionManager _regionManager;
public NavigationMenuModule(IUnityContainer container, IRegionManager regionManager)
{
_container = container;
_regionManager = regionManager;
}
#region Implementation of IModule
public void Initialize()
{
RegisterViewsAndServices();
var viewModel = _container.Resolve<INavigationMenuViewModel>();
_regionManager.Regions[RegionNames.MainMenu].Add(viewModel.View);
}
#endregion
#region Protected Methods
protected void RegisterViewsAndServices()
{
_container.RegisterType<INavigationMenuView, NavigationMenuView>();
_container.RegisterType<INavigationMenuViewModel, NavigationMenuViewModel>();
}
#endregion
}
Are you sure that the command binding is working? If you run the app and look in the debug output panel, do you see any binding warnings? Perhaps the DataContext of your UserControl isn't set to your NavigationMenuViewModel.
Related
So i have WPF application with main windoes and 2 UserControls:
HomeView.xaml
OptionsView.xaml
View Model
public class ApplicationViewModel : INotifyPropertyChanged
{
#region Fields
private ICommand changePageCommand;
private ICommand addFilesCommand;
private IPageViewModel _currentPageViewModel;
private List<IPageViewModel> _pageViewModels;
#endregion
public ApplicationViewModel()
{
// Add available pages
PageViewModels.Add(new HomeViewModel() { IsSelected = true });
PageViewModels.Add(new OptionsViewModel() { IsSelected = false });
// Set starting page
CurrentPageViewModel = PageViewModels[0];
}
#region Properties / Commands
public ICommand ChangePageCommand
{
get
{
if (changePageCommand == null)
{
changePageCommand = new RelayCommand(
p => ChangeViewModel((IPageViewModel)p),
p => p is IPageViewModel);
}
return changePageCommand;
}
}
public List<IPageViewModel> PageViewModels
{
get
{
if (_pageViewModels == null)
_pageViewModels = new List<IPageViewModel>();
return _pageViewModels;
}
}
public IPageViewModel CurrentPageViewModel
{
get
{
return _currentPageViewModel;
}
set
{
if (_currentPageViewModel != value)
{
_currentPageViewModel = value;
OnPropertyChanged("CurrentPageViewModel");
}
}
}
#endregion
#region Methods
private void ChangeViewModel(IPageViewModel viewModel)
{
if (!PageViewModels.Contains(viewModel))
PageViewModels.Add(viewModel);
CurrentPageViewModel = PageViewModels.FirstOrDefault(vm => vm == viewModel);
}
#endregion
}
Whan application start
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow app = new MainWindow();
ApplicationViewModel context = new ApplicationViewModel();
app.DataContext = context;
app.Show();
}
}
Windows respurces
<Window.Resources>
<DataTemplate DataType="{x:Type home:HomeViewModel}">
<home:HomeView />
</DataTemplate>
<DataTemplate DataType="{x:Type options:OptionsViewModel}">
<options:OptionsView />
</DataTemplate>
</Window.Resources>
And inside HomeView.xaml i have simple button:
<Button Command="{Binding DataContext.AddFilesCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding}"/>
And i want to add simple Click command, something.
So i try to add this ICommand:
public ICommand AddFilesCommand
{
}
Any suggestions how to add this kind on command that will execute after each Button Click ?
This can be done a lot easier. I would create a class to implement commands easily:
using System;
using System.Windows.Input;
namespace YourNameSpace
{
public class RelayCommand : ICommand
{
private Action Action;
public Command(Action _action)
{
Action = _action;
}
public event EventHandler CanExecuteChanged = (sender, e) => { };
public bool CanExecute(object parameter) => true;
public void Execute(object parameter)
{
Action();
}
}
}
Then create the command (you don't need private ICommand for this):
public ICommand AddFileCommand { get; set; }
And use it like this (in the constructor):
AddFileCommand = new RelayCommand(()=>
{
MethodToExecute();
});
XAML:
<Button Command="{Binding AddFileCommand}"/>
This way your code will be easier to see trough.
I'm trying to use a UserControl I did, but currently, I've an error:
No parameterless constructor defined for this object
The thing is... There is only one constructor, and it has no parameters.
Here is some code:
<DockPanel x:Class="Xms.SomePackage.NewLicenseWizard"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:wizard="clr-namespace:my.wizard.namepsace;assembly=My.Common.Assembly"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" LastChildFill="True">
<wizard:Wizard> <-- Error is here -->
<wizard:WizardPage>
<wizard:WizardPage.ContentElement>
<TextBlock>Page one</TextBlock>
</wizard:WizardPage.ContentElement>
</wizard:WizardPage>
</wizard:Wizard>
</DockPanel>
The Wizard UserControl looks like this:
[ContentProperty("Pages")]
public partial class Wizard : UserControl, IView
{
#region Dependency Properties
//All the Dependencies Properties)]
#endregion
#region Public Properties
internal WizardViewModel ViewModel
{
get { return DataContext as WizardViewModel; }
set { DataContext = value; }
}
#endregion
#region Constructor
public Wizard()
{
Pages = new ObservableCollection<WizardPage>();
InitializeComponent();
ViewModelLocationProvider.AutoWireViewModelChanged(this);
//[Some other initialization]
}
#endregion
}
I just removed the DependencyProperties.
So why is it thinking that I've no parameterless constructor?
(at runtime I've no issues)
EDIT
Here is the ViewModel bound to the Wizard UserControl:
internal class WizardViewModel : XmsViewModelBase
{
#region public Properties
public InteractionRequest<CloseWindowRequest> CloseWindowRequest { get; private set; }
public DelegateCommand GoToPreviousPageCommand { get; set; }
public DelegateCommand GoToNextPageCommand { get; set; }
public DelegateCommand FinishCommand { get; set; }
public DelegateCommand CancelCommand { get; set; }
public ObservableCollection<WizardPage> Pages
{
get { return GetValue<ObservableCollection<WizardPage>>(); }
set { SetValue(value); }
}
public WizardPage CurrentPage
{
get { return GetValue<WizardPage>(); }
set
{
SetValue(value);
HandleCurrentPageChanged();
}
}
public Boolean IsLastPage
{
get { return GetValue<Boolean>(); }
set { SetValue(value); }
}
#endregion
#region Constructor
public WizardViewModel()
{
Pages = new ObservableCollection<WizardPage>();
//+Initialization of commands
}
#endregion
#region Private Methods
// All the methods related to the commands
#endregion
}
While exploring DI/IOC with Unity with WPF, I came across a question and need your feedback. Please consider the following scenario...
================================================================
public interface IDataServices
{
string GetData();
}
================================================================
public class CopyTextDataServices : IDataServices
{
public string GetData()
{
return "copy text from CopyTextDataServices";
}
}
================================================================
public class TextDataServices : IDataServices
{
public string GetData()
{
return "I am injected by setter property injection";
}
}
================================================================
public interface ITextViewModel
{
string LabelContnet { get; set; }
}
================================================================
public class TextViewModel : ITextViewModel
{
public TextViewModel()
{
LabelContnet = "This is from view model";
}
public string LabelContnet { get; set; }
}
================================================================
public partial class MainWindow : Window
{
public MainWindow(ITextViewModel textViewModel)
{
InitializeComponent();
Loaded += MainWindow_Loaded;
DataContext = textViewModel;
}
[Dependency]
public IDataServices Services { get; set; }
containing the event data.</param>
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
LabelLeft.Content = Services.GetData();
}
}
================================================================
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
container.RegisterType<IDataServices, TextDataServices>();
container.RegisterType<IDataServices, CopyTextDataServices>();
container.RegisterType<ITextViewModel, TextViewModel>();
var window = container.Resolve<MainWindow>();
window.Show();
}
}
================================================================
<Window x:Class="TestAppWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" FontSize="20">
<StackPanel>
<Label Content="{Binding Path=LabelContnet,FallbackValue=Left}" HorizontalAlignment="Left" Name="LabelLeft" />
<Label Content="{Binding Path=LabelContnet,FallbackValue=Right}" HorizontalAlignment="Left" Name="LabelRight" />
</StackPanel>
</Window>
===================================================================
Now the result of this appears in the labels is
copy text from CopyTextDataServices
This is from view model
But I want to know if I want to get data from TextDataServices, how can I do that?
The problem is in this line:
container.RegisterType<IDataServices, TextDataServices>();
// This overwrites the previous mapping.
// All dependencies to IDataServices will use CopyTextDataServices.
container.RegisterType<IDataServices, CopyTextDataServices>();
If you want to have both IDataServices, you'll need to register one or both as named instances.
container.RegisterType<IDataServices, TextDataServices>("TextDataServicesName");
container.RegisterType<IDataServices, CopyTextDataServices>("CopyTextDataServicesName");
In your control:
[Dependency("TextDataServicesName")]
public IDataServices Services { get; set; }
I have a usercontrol:
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Name="TextBlock1" Text="Text for"></TextBlock>
</Grid>
It has a cs file:
public partial class MyUserControl: UserControl
{
public string MyProperty { get; set; }
public MyUserControl()
{
InitializeComponent();
DoSomething();
}
private void DoSomething()
{
TextBlock1.Text = TextBlock1.Text + MyProperty;
}
}
I'm trying to add this usercontrol programatically to another user control:
UserControls.MyUserControl myUserControl = new UserControls.MyUserControl();
myUserControl.MyProperty = "Something";
MyStackPanel.Children.Add(myUserControl);
I thought I've done something like this before without any issues, but in this case MyProperty is always null. What did I do wrong?
Your code is fine as-is except for when you are calling DoSomething(). You should avoid GUI interaction in the constructor of a control for many reasons (not the least of which is that your property is not yet set as pointed out by Mark Hall). I do not however recommend adding different constructors just to take initial properties.
You simply want to defer that call to the Loaded event of the control.
Assuming you set the event in the Xaml like this:
<navigation:Page x:Class="MyUserControl"
...
Loaded="Page_Loaded">
Your code would look like:
public partial class MyUserControl: UserControl
{
public string MyProperty { get; set; }
public MyUserControl()
{
InitializeComponent();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
DoSomething();
}
private void DoSomething()
{
TextBlock1.Text = TextBlock1.Text + MyProperty;
}
}
If you want to set the Loaded event in code it would just look like:
public partial class MyUserControl: UserControl
{
public string MyProperty { get; set; }
public MyUserControl()
{
InitializeComponent();
Loaded += Page_Loaded;
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
DoSomething();
}
private void DoSomething()
{
TextBlock1.Text = TextBlock1.Text + MyProperty;
}
}
The it is just down to you setting all your desired properties after construction, but before the page is loaded as you are doing already.
UserControls.MyUserControl myUserControl = new UserControls.MyUserControl();
myUserControl.MyProperty = "Something";
MyStackPanel.Children.Add(myUserControl); // This makes it visible when Loaded event is called
What is happening is that you are calling DoSomething in your contructor before you have set MyProperty so MyProperty is equal to null. If you want the data to be there when the UserControl is created you can set it in the Constructor.
i.e.
public partial class MyUserControl : UserControl
{
public string MyProperty { get; set; }
public MyUserControl() //Default Constructor
{
InitializeComponent();
}
public MyUserControl(string MyData)
{
InitializeComponent();
MyProperty = MyData;
DoSomething();
}
private void DoSomething()
{
TextBlock1.Text = TextBlock1.Text + MyProperty;
}
}
and create it like this.
MyUserControl myUserControl = new MyUserControl(" Something");
MyStackPanel.Children.Add(myUserControl);
I'm trying to get Lazy to work for a collection in my ViewModel that I'm binding to. The collection loads through MEF fine, but never gets displayed in the bound UI.
Here's the UI:
<Window x:Class="TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<ItemsControl ItemsSource="{Binding MyList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding ItemTitle}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel>
</Window>
The code-behind class:
public partial class TestWindow : Window
{
public TestWindow()
{
InitializeComponent();
this.DataContext = new TestVM();
}
}
The ViewModel:
public class TestVM : INotifyPropertyChanged, IPartImportsSatisfiedNotification
{
public TestVM()
{
//I'm using a static class to initiate the import
CompositionInitializer.SatisfyImports(this);
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
[ImportMany(typeof(MyItemBase))]
public Lazy<MyItemBase>[] MyList { get; set; }
public void OnImportsSatisfied()
{
this.PropertyChanged(this, new PropertyChangedEventArgs("MyList"));
}
}
The base class for the items, and some inherited test classes:
[InheritedExport]
public class MyItemBase
{
public MyItemBase()
{
}
public string ItemTitle{ get; set; }
}
public class MyItem1: MyItemBase
{
public MyItem1()
{
this.ItemTitle = "Item 1";
}
}
public class MyItem2: MyItemBase
{
public MyItem2()
{
this.ItemTitle = "Item 2";
}
}
This works IF I just remove the Lazy loading. However, I'll need to apply some export attributes later, which means going to Lazy.
the problem is that you want bind to a list of MyItembase object, but your actual binding is to a lazy arrray of MyItembase objects.(as long as you never call .Value for your lazy item nothing will happen)
i my projects i use a private lazy collection for mef and a normal ObservableCollection for wpf. btw i would prefer Constructor injection for your Mef import
public class TestVM : INotifyPropertyChanged, IPartImportsSatisfiedNotification
{
public TestVM()
{
//I'm using a static class to initiate the import
CompositionInitializer.SatisfyImports(this);
this.MyList = new ObservableCollection();
foreach(var lazyitem in _mefList)
{
this.MyList.Add(lazyitem.Value);
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public ObservbableCollection<MyItemBase> MyList{ get; set; }
[ImportMany(typeof(MyItemBase))]
private IEnumarable<Lazy<MyItemBase>> _mefList { get; set; }
public void OnImportsSatisfied()
{
//this.PropertyChanged(this, new PropertyChangedEventArgs("MyList"));
}
}