ICommand with MVVM in WPF - wpf

I am new to MVVM and WPF, trying to use ICommand in WPF and MVVM. Below is the code.
Can someone please help to know why the below code is not working, means nothing happens on button click.
Appreciate your help.
View
<Grid>
<Button Height="40" Width="200" Name="button1" Command="{Binding Path=Click}">Click Me</Button>
</Grid>
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow mainWindow = new MainWindow();
MainWindowViewModel vm = new MainWindowViewModel();
mainWindow.DataContext = vm;
}
}
MainWindowViewModel.cs
namespace TestWPFApplication.ViewModel
{
public class MainWindowViewModel
{
private ICommand _click;
public ICommand Click
{
get
{
if (_click == null)
{
_click = new CommandTest();
}
return _click;
}
set
{
_click = value;
}
}
private class CommandTest : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show("Hi! Test");
}
}
}
}

It looks like your OnStartup method is instantiating a MainWindow and never showing it. You probably have the StartupUri set in XAML which is creating a different MainWindow with the data context not set.
You could remove the StartupUri and call mainWindow.Show(). Alternatively, you could get rid of the OnStartup method and set up the data context in the main window's constructor.

You don't need to initialize this Window in OnStartup.
In MainWindow constructor after Initialize create instance of ViewModel and it should work.

Related

DataContext of usercontrol in WPF

I'm new to WPF and I'm trying to start a little project with a maximum of good practice. I'm using MVVM and dependency injection.
I have a concern which seems to be easy to understand but i can't find an answer (at this step, DataContext is not very clear for me).
The UserControlView of type UserControl contains just a button for testing.
This is the app class :
public App()
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton<MainWindow>();
services.AddSingleton<UserControlViewModel>();
services.AddSingleton<UserControlView>();
_serviceProvider = services.BuildServiceProvider();
}
The user control is included in the Main windows like that :
<Grid>
<views:UserControlView/>
</Grid>
Now, in the OnStartup overrided method :
protected override void OnStartup(StartupEventArgs e)
{
MainWindow = _serviceProvider.GetRequiredService<MainWindow>();
MainWindow.DataContext = _serviceProvider.GetRequiredService<PaymentMeansViewModel>();
MainWindow.Show();
}
Like that it works, my button is correctly binded to the command.
But what is strange for me is that I have to set the 'UserControlViewModel' as the DataContext of the Main Window.
Isn'it possible to bind it to the 'UserControlView', something like :
protected override void OnStartup(StartupEventArgs e)
{
MainWindow = _serviceProvider.GetRequiredService<MainWindow>();
UserControlView testUC = _serviceProvider.GetRequiredService<UserControlView>();
testUC.DataContext = _serviceProvider.GetRequiredService<UserControlViewModel>();
MainWindow.Show();
}
Thanks for help.
Finally I did it.
I think (I hope I'm right) that I understood.
First of all, let's begin with the basic.
A view must have a viewmodel to bind the properties. A usercontrol is a kind of view "encapsulated" in a view. Therefore a usercontrol must have its own viewmodel and the view must have its own viewmodel.
The datacontext of the MainWindow is set in the app onstartup method :
MainWindow = new MainWindow()
{
DataContext = new MainWindowViewModel()
};
MainWindow must implement INotifyPropertyChanged. All view models must implement this interface. We can create a base class which will be derived in the view models :
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string? propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The DataContext of the usercontrol must be explicit in the xaml of the MainWindow:
<Grid>
<views:UserControlView DataContext="{Binding CurrentViewModel}"/>
</Grid>
"CurrentViewModel" is a DataContext, then it's a ViewModel, and as it is binded, it must be a property of the MainViewModel.
public class MainWindowViewModel : ViewModelBase
{
public ViewModelBase CurrentViewModel { get; }
public MainWindowViewModel()
{
CurrentViewModel=new UserControlViewModel();
}
}
Hope it can help.

How can i fire an event in window and handle it in UserControl? C# WPF

I'm using my usercontrol in window. I want to do something in usercontrol when window's StateChanged event fires.
I want to send statechanged event to usercontrol.
How can i do this ?
Option 1:
Define a public method in the UserControl code behind that can be called to notify the event occurance
Handle the StateChanged event in the Window code behind and call the defined method in the UserControl
Option 2:
Implement an interface for the StateChanged event and implement this interface in your Window
Implement a DependencyProperty with interface as type in the UserControl
Bind the property to your Window when you instantiate the UserControl
Register to the StateChanged event on property changed in the code behind of UserControl
Some code for demonstration how to implement and use option 2:
public interface IStateChanged
{
event EventHandler StateChanged;
}
public partial class MainWindow : Window, IStateChanged
{
public MainWindow()
{
InitializeComponent();
}
}
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}
public IStateChanged StateChangedHost
{
get { return (IStateChanged)GetValue(StateChangedHostProperty); }
set { SetValue(StateChangedHostProperty, value); }
}
// Using a DependencyProperty as the backing store for StateChangedHost. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StateChangedHostProperty =
DependencyProperty.Register("StateChangedHost", typeof(IStateChanged), typeof(MyUserControl), new FrameworkPropertyMetadata(null, StateChangedHost_PropertyChanged));
private static void StateChangedHost_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var self = d as MyUserControl;
if (e.OldValue != null)
{
((IStateChanged)e.OldValue).StateChanged -= self.NotifyStateChanged;
}
if (e.NewValue != null)
{
((IStateChanged)e.NewValue).StateChanged += self.NotifyStateChanged;
}
}
private void NotifyStateChanged(object sender, EventArgs e)
{
// implementation logic on StateChanged event
}
}
<Window [...]>
<Grid>
<local:MyUserControl StateChangedHost="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}}"/>
</Grid>
</Window>
Add a method in the User Control to notify it
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MainWindow_StateChanged(object sender, EventArgs e)
{
myUserControl.StateChanged();
}
}
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}
public StateChanged()
{
...
}
}
<Window [...]>
<Grid>
<local:MyUserControl x:Name="myUserControl"/>
</Grid>
</Window>
This should work
public partial class MyUserControl : UserControl
{
public MyUserControl ()
{
InitializeComponent();
DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(Window.WindowStateProperty, typeof(Window));
dpd.AddValueChanged(Application.Current.MainWindow, (s, e) =>
{
//your code
});
}
}
Basically it tells the user control to observe WindowsStateProperty and any time that state changes it will run
I think, that's not the right approach.
my code would look like this:
public class MyUserControl : UserControl{
event EventHandler ParentWindowStateChanged;
public void RaiseParentWindowStateChanged(Window sender){
this.ParentWindowStateChanged?.Invoke(sender, EventArgs.Empty);
}
}
on Window.StateChanged you can call myUserControl. RaiseParentWindowStateChanged(this).
in constructor of UserControl you can add handler for event ParentWindowStateChanged like
MyUserControl(){
this. ParentWindowStateChanged += (sender, args) => {
// do something
};
Regards
Steffen

MVVM Light pass parameters to child view model

I am new to MVVM and WPF.
I am using MVVM Light to make an application which contains a DataGrid within a window, which has a view model (MainViewModel) and another window for adding and editing records in the DataGrid, that also has its own view model (EditViewModel).
What I am worried about is the approach I am using to open the Add/Edit window from the MainViewModel. In the MainViewModel I have a property SelectedItem, which is bound to the SelectedItem property of the DataGrid and an IsEdit boolean property that indicates if the Add/Edit window should be launched in Add or Edit mode.
When the Add/Edit window gets opened in edit mode, in the constructor of its view model I have the following line:
MainViewModel mainViewModel = ServiceLocator.Current.GetInstance<MainViewModel>();
That obviously retrieves the current instance of the MainViewModel, which works perfectly fine, but I am not really sure it is the best way to do this.
Also if I have more than one instances of the Main window, that use the same MainViewModel instance and I open an instance of the Add/Edit window from both of them, the Add/Edit windows are going to get data from the same instance of the MainViewModel which may be a problem.
If I try to create a new instance of MainViewModel for each MainWindow I open, then I don't know how to pass the instance of the currently used MainViewModel to the EditViewModel.
I hope I made clear what I need to do. Tell me if I have missed something and I will add it:)
Thanks in advance
Hi if I havent misunderstood your problem incorrect you can do it this way:
Since i need IsRequired dependency Property in both MainView and EditView i created a class that extends Window class
public class ExtendedWindow:Window
{
public static readonly DependencyProperty IsRequiredProperty = DependencyProperty.Register("IsRequired", typeof(bool), typeof(ExtendedWindow));
public bool IsRequired
{
get { return (bool)GetValue(IsRequiredProperty); }
set { SetValue(IsRequiredProperty, value); }
}
}
MainView and ViewModel
public partial class MainWindow:ExtendedWindow
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
EditView editView = new EditView();
**((EditViewModel)editView.DataContext).IsRequired = this.IsRequired;**
editView.Show();
}
}
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
IsRequired = true;
}
private bool isRequired;
public bool IsRequired
{
get { return isRequired; }
set { isRequired = value; Notify("IsRequired"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void Notify(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
EditView and ViewModel
public partial class EditView:ExtendedWindow
{
public EditView()
{
InitializeComponent();
DataContext = new EditViewModel();
}
}
public class EditViewModel : INotifyPropertyChanged
{
private bool isRequired;
public bool IsRequired
{
get { return isRequired; }
set { isRequired = value; Notify("IsRequired"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void Notify(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
This is just kind of dummy but can give you idea how you can do it. I have tried it in dummy and its working fine.

Problems with custom WPF Commands using MVVM

I have tried two different methods of commanding in WPF from the ViewModel and come up short. The top method works great if I put it into the View however I was told this is bad practice. The second method I was told is the proper way to do custom commanding in MVVM however I get stuck on how to actually call/bind the command from the View.
View Model:
class MainViewModel : INotifyPropertyChanged
{
#region INPC
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
public readonly static RoutedUICommand myCommand;
static MainViewModel()
{
myCommand = new RoutedUICommand("customCommand","My Command",typeof(MainViewModel));
}
private void ExecutemyCommand(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("textBox1 cleared");
}
private void myCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
}
On my View the code I have this which gives me an error
<Window x:Class="ConfigManager2.View.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:con="clr-namespace:ConfigManager2.Converters"
xmlns:vm="clr-namespace:ConfigManager2.ViewModel"
xmlns:local="clr-namespace:ConfigManager2.View"
.
.
.
<Window.CommandBindings>
<CommandBinding
Command="{x:Static vm:MainViewModel.myCommand}"
CanExecute="myCommandCanExecute"
Executed="ExecutemyCommand" />
</Window.CommandBindings>
.
.
.
<Button Content="COMMAND ME" Height="50px" Command="{x:Static vm:MainViewModel.myCommand}" />
The Error I am getting is 'ConfigManager2.View.MainView' does not contain a definition for 'ExecutemyCommand' and no extension method 'ExecutemyCommand' accepting a first argument of type 'ConfigManager2.View.MainView' could be found (are you missing a using directive or an assembly reference?)
I tried another method using ICommand and got stumped on how to bind this to the above button from the XAML
ViewModel:
public ICommand ClearCommand { get; private set; }
public MainViewModel()
{
ClearCommand= new ClearCommand(this);
}
class ClearCommand : ICommand
{
private MainViewModel viewModel;
public ClearCommand(MainViewModel viewModel)
{
this.viewModel = viewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
viewModel.vmTextBox1 = String.Empty;
MessageBox.Show("Textbox1 Cleared");
}
}
With the ICommand version (which I would prefer) you can bind directly to the command:
<Button Command="{Binding ClearCommand}"/>
There is no Window.CommandBinding necessary. This works, if an instance of the MainViewModel is set as the window's or button's DataContext.

Broken binding with Prism, Silverlight and ViewFirst approach

The problem we are having is that we cannot get binding to work in our
prism silverlight application when using the view-model first
approach. The view first approach work fine. We have gone over the
official documentation and various web sites, but have still not
resolved the issue. Below is the code for both the view-model first,
and the view first approach. Are we missing something? Read about it on my blog http://silvercasts.blogspot.com
View-Model first approach:
Bootstrapper:
internal void RegisterLoginRegionAndView()
{
IRegionManager regionManager = Container.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion(ShellRegionNames.MainRegion,
() => Container.Resolve<IViewModel>().View);
}
ViewModel:
public ViewModel(IView view)
{
View = view;
View.SetModel(this);
User = new User();
User.Username = "TestUser";
}
ViewModel Interface:
public interface IViewModel
{
IView View { get; set; }
}
View Interface:
public interface IView
{
void SetModel(IViewModel model);
}
View Xaml:
<TextBox x:Name="Username" TextWrapping="Wrap" Text="{Binding User.Username}" />
View Code Behind:
public void SetModel(IViewModel viewModel)
{
this.DataContext = viewModel;
}
View first approach
Bootstrapper:
regionManager.RegisterViewWithRegion(ShellRegionNames.MainRegion, typeof(IView));
ViewModel:
public ViewModel()
{
User = new User();
User.Username = "TestUser";
}
View Code Behind:
public View(IViewModel viewModel)
{
InitializeComponent();
this.DataContext = viewModel;
}
Your implementation of SetModel on your view needs to be as follows:
public void MyUserControl : UserControl, IView
{
//...
public void SetModel(IViewModel vm)
{
this.DataContext = vm;
}
}
If that's not there, it needs to be (you haven't posted your implementation of SetModel, but this would be the source of the issue in this case).
If this is not the issue, it's likely because your ViewModel does not implement INotifyPropertyChanged. I usually use a base ViewModel that does this:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And then all of my ViewModels derive from that:
public class MyViewModel : ViewModelBase
{
private User _user;
public User User
{
get { return _user; }
set
{
_user = value;
OnPropertyChanged("User");
}
}
}
Note: in your case the "User" object should probably also be a ViewModel and also raise OnPropertyChanged for the Username property.
Hope this helps.
The obvious difference to me is that you set the DataContext in the "view first" approach, but not in the "view model first" approach. I'm not sure if Prism sets the DataContext for you (I'd guess that you're assuming that it does) but try setting the DataContext manually to see if this is the problem. In your ViewModel constructor you call View.SetModel(this) - does that call set the DataContext?
The problem was that I was using the SetModel method before the data object was instanced. Moving it like this:
public ViewModel(IView view)
{
View = view;
User = new User();
User.Username = "TestUser";
View.SetModel(this);
}
solved the problem.

Resources