WPF MVVM: ICommand Binding to controls - wpf

I've totally lost in the command binding that is used in MVVM. How should I bind my object to the window and/or its command to the control to get method called on the Button Click?
Here is a CustomerViewModel class:
public class CustomerViewModel : ViewModelBase
{
RelayCommand _saveCommand;
public ICommand SaveCommand
{
get
{
if (_saveCommand == null)
{
_saveCommand = new RelayCommand(param => this.Save(), param => this.CanSave);
NotifyPropertyChanged("SaveCommand");
}
return _saveCommand;
}
}
public void Save()
{
...
}
public bool CanSave { get { return true; } }
...
ViewModelBase implements the INotifyPropertyChanged interface
Here is how Button is bound to the command:
<Button Content="Save" Margin="3" Command="{Binding DataContext.Save}" />
An instance of the CustomerViewModel is assigned to the DataContext of the window that contains a Button.
The given example is not working: I've put break point into the Save method but execution doesn't pass to the method. I've saw a lot of examples (on the stackoverflow too), but can't figure out how binding should be specified.
Please advise, any help will be appreciated.
Thanks.
P.S. Probably I need to specify RelativeSource in the Button binding... something like this:
Command="{Binding Path=DataContext.Save, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
but which type should be specified for ancestor?

What you are trying to do is to bind directly to the Save method. This is not how to do it.
Assuming that you have set the DataContext of your View to an instance of CustomerViewModel, this is how you bind to the SaveCommand:
<Button Content="Save" Margin="3" Command="{Binding SaveCommand}" />
You do not have to call NotifyPropertyChanged("SaveCommand");.

Related

RichTextBox two-way binding to a flag in wpf

How to bind RichTextBox to a flag (true/false value). For example i want the flag to be true if the text in the RTB is edited. And also the binding should be two-way.
You have to use two things to solve your problem.
You have to add System.Windows.Interactivity reference and use this link in your xaml:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Here is an xaml (view part) example:
<Window ...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
...
<RichTextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding TextChangedCommand}"
x:Name="textChangedCommand" />
</i:EventTrigger>
</i:Interaction.Triggers>
</RichTextBox>
...
After this, you have to use an ICommand implementation in your ViewModel:
Here is a simple example to use in "ViewModel" part:
public partial class MainWindow : Window
{
RelayCommand _textChangedCommand;
public RelayCommand TextChangedCommand
{
get
{
if (_textChangedCommand == null)
_textChangedCommand = new RelayCommand(() => IsEdited = true);
return _textChangedCommand;
}
}
private bool _isEdited;
public bool IsEdited
{
get
{
return _isEdited;
}
set
{
_isEdited = value;
}
}
public MainWindow()
{
InitializeComponent();
//Use the following code if you want to use xaml.cs as ViewModel but It is not recommended, it is not correct MVVM pattern only simple example.
DataContext = this;
}
}
There are several way to implement the ICommand interface.
Here you can find one with description.

How can I identify which button was clicked using MVVM Pattern in WPF?

I am using MVVM Pattern. I have two buttons. on Click I need to identify which button was clicked. How I can bind buttons in XAMl so that I can identify which button was clicked.
If you really use MVVM then bind Command of each button to corresponding ICommand in your view-model. It will be two different commands so you don't need to do any special actions to distinguish one button from another.
XAML:
<Button Content="FirstButton"
Command="{Binding Path=FirstCommand, Mode=OneTime}"/>
<Button Content="SecondButton"
Command="{Binding Path=SecondCommand, Mode=OneTime}"/>
View-Model:
public sealed class ViewModel : INotifyPropertyChanged
{
// ...
public ICommand FirstCommand { get; }
public ICommand SecondCommand { get; }
// ...
}
If you want to use the same Command for multiple buttons, you can use CommandParameter.
<Button Content="buttonContent1" Command="{Binding ButtonClickCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Content}"/>
And in your command delegate method you can use something like this:
private void ButtonClickCommandHandler(object parameter)
{
switch(parameter.ToString())
{
case buttonContent1:
...
case buttonContent2:
...
}
}
Here button is identified by its content of course you can change it to some other property like Tag

Binding To Command Exposed By User Control

I tried to implement something earlier this afternoon and have not been able to get it working the way I want.
If I have some XAML like the following (I know it is not complete but it is for illustrative purposes only)...
<Window>
<StackPanel>
<Button />
<UserControl1>
</StackPanel>
</Window>
...and UserControl1 exposes a command property (currently, I am using a RelayCommand for this), how do I bind the Button in the Window to this command. I tried to expose a property in the Window that is bound to the command property in UserControl1 so that I could turn around and re-bind this to the Button but the property in the Window is always null. This pattern seems to work for another property (an integer value). Is there a better way to do this? Is there something with the command that prevents this from occuring?
(1) define your command as a DepenedecyProperty and implement it .
(2) bind the Command property in the button the the MyCommand Property in your UserControl.
public Class UserControl1 : UserControl
{
public UserControl1()
{
MyCommad = new RelayCommand
(
() => { // do some stuff in execute delegate},
() => { return true ;}
);
}
public bool MyCommand
{
get { return (ICommand)GetValue(MyCommandProperty); }
set { SetValue(MyCommandProperty, value); }
}
public static readonly DependencyProperty MyCommandProperty =
DependencyProperty.Register("MyCommand", typeof(ICommand),new FrameworkPropertyMetadata(null));
}
xaml :
<Window>
<StackPanel>
<Button Command={Binding ElementName=control1,Path=MyCommand,Mode=OneWay/>
<UserControl1 x:Name="control1" />
</StackPanel>
</Window>

Process CommandParameter via WPF MVVM

I`m quite begginer at WPF.
I have checkBox and I want that every check changes will excecute a command that gets IsChecked parameter and do some action.
I have the next code in my XAML file:
At my viewModel I have the next code:
private ICommand _addSelectedItemsCommand;
public ICommand AddSelectedItemsCommand
{
get
{
if (_addSelectedItemsCommand == null)
{
_addSelectedItemsCommand = new RelayCommand(param => this.AddSelectedItems());
}
return _addSelectedItemsCommand;
}
}
private void AddSelectedItems()
{
Do something...
}
But for "Do somthing" I need IsChecked parameter, How can i get it?
Thanks
In Your ViewModel RelayCommand Look Like
private RelayCommand<string> AddSelectedItemsCommand{get;set;}
And in your ViewModel Constructor code look like
AddSelectedItemsCommand=new RelayCommand<string>(AddSelectedItemsMethod);
void AddSelectedItemsMethod(string AddItem)
{
Your Code Goes Here.
}
You should use InvokeCommandAction class. You can find it in Expression Blend SDK or you can simply add this NuGet package to your project.
<CheckBox
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<ei:InvokeCommandAction Command="{Binding AddSelectedItemsCommand}" CommandParameter="..." />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>

How to Workaround Referencing View's Controls

I'm using Galasoft's Light MVVM for my Siverlight project.
I have setup everything as instructed: the ViewModel is bound to View's DataContext;
I have a canvas named inkCanvas in the View.
When the ViewModel gets the updated project data, I need to reference inkCanvas to create a CanvasRender instance public CanvasRender(Canvas canvas, ProjectData pdata).
The problem is in MVVM, the ViewModel knows nothing about View, so how can I reference a control (inkCanvas) in View?
P.S. (Edited): The workaround I made is: when I pass the project data to the ViewModel, I also pass the inkCanvas from View's code-behind. hmmm, now my code-behind is not clean.
Per the comments above, one way to do this is to extend Canvas and keep the reference to CanvasRender inside that class.
public class MyCanvas : Canvas
{
private CanvasRender _canvasRender;
private ProjectData _data;
public ProjectData Data
{
get { return _data; }
set
{
_data = value;
_canvasRender = new CanvasRender(this, _data);
}
}
public MyCanvas() : base()
{
}
}
You'd probably want to also make ProjectData a Dependency Property so that it's bindable.
This allows you to maintain the MVVM pattern, because now you can write in XAML:
<local:MyCanvas ProjectData="{Binding ViewModel.ProjectData}" />
In MVVM Pattern, you won't reference a Control directly in ViewModel. In MVVM, all is "binding". You inkCanvas will be binding to a property in your ViewModel.
public class MyViewModel : INotifyPropertyChanged
{
private readonly StrokeCollection _mystrokes;
public MyViewModel ()
{
_mystrokes= new StrokeCollection();
(_mystrokesas INotifyCollectionChanged).CollectionChanged += delegate
{
//the strokes have changed
};
}
public event PropertyChangedEventHandler PropertyChanged;
public StrokeCollection MyStrokes
{
get
{
return _mystrokes;
}
}
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And XAML:
<InkCanvas Strokes="{Binding MyStrokes}"/>
Edit :
Maybe the workaround for your case is to use EventToCommand : this allow tobind an UI event to an ICommand directly in XAML ( and use Args to pass a ref to the inkCancas)
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand Command="{Binding Mode=OneWay, Path=LoadedCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
If your going to use the EventToCommand approach (which you tried in another answer), then instead of using the PassEventArgsToCommand property use the CommandParameter property and bind it to your Canvas.
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand Command="{Binding Path=CanvasLoadedCommand}"
CommandParameter="{Binding ElementName=inkCanvas}" />
</i:EventTrigger>
</i:Interaction.Triggers>
Then in your ViewModel:
public class ViewModel
{
private Canvas m_canvas;
public RelayCommand<Canvas> CanvasLoadedCommand { get; private set; }
public ViewModel()
{
CanvasLoadedCommand = new RelayCommand<Canvas>(canvas =>
{
m_canvas = canvas;
});
}
}
So as soon as your canvas is loaded, you should then have a reference to it saved in your view model.

Resources