error binding isEnabled to button in xaml - c#

I am new to xaml, WPFs, C# and the MVVM paradigm. I have started with an app based on this example project, in the selected excerpts i want to disable the authenticate button from the LoginPageViewModel after the authenticate button has been clicked(There is no point clicking the button if you are authenticated). I have got command binding working, as well as text control binding between the view and ViewModel. my LoginPageViewModel is based on a abstract class that inherits from INotifyPropertyChanged
The setter AuthenticateButtonEnabled is working, but it is not binding to the isEnabled proprerty on the form. My question is, what could I have missed, and How can i trace the binding between a View and a ViewModel?
the LoginPageView.xaml button:
<Button x:Name="authenticateButton" Content="{x:Static res:Strings.LoginPage_authenticateButton_content}"
Grid.Column="2" Margin="53,4,0,10"
Grid.Row="2" FontSize="16"
IsEnabled="{Binding Path=AuthenticateButtonEnabled}"
Command="{Binding Path=AuthenticateCommand}" HorizontalAlignment="Left" Width="87"/>
the viewModel
private String _username;
private String _responseTextBlock;
private String _linkTextBlockURI;
private String _linkTextBlockText;
private bool _authenticateButtonEnabled;
...
private async void Authenticate()
{
ResponseTextBlock = Strings.LoginPage_responseBlock_content_checking;#this works!
AuthenticateButtonEnabled = false;
return;
}
....
public bool AuthenticateButtonEnabled
{
get { return _authenticateButtonEnabled; }
set { _authenticateButtonEnabled = value; OnPropertyChanged("AuthenticateButtonEnabled"); }
}
// this is in the abstract class.
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}

If you want to have both: command and AuthenticateButtonEnabled, then simply check for this property in CanExecute delegate and vise-versa in property setter update command.
Here is implementation with DelegateCommand and some improvements which you may find useful:
bool _isAuthenticateButtonEnabled;
public bool IsAuthenticateButtonEnabled
{
get { return _isAuthenticateButtonEnabled; }
set
{
_isAuthenticateButtonEnabled = value;
OnPropertyChanged();
AuthenticateCommand.Update();
}
}
// the base could class could actually implement this
void OnPropertyChanged([CallerMemberName] string property) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
public DelegateCommand AuthenticateCommand { get; }
// view model constructor
public ViewModel()
{
AuthenticateCommand = new DelegateCommand(o =>
{
... // some actions when command is executed
}, o =>
{
bool somecondition = ...; // some condition to disable button, e.q. when executing command
return somecondition && IsAuthenticateButtonEnabled;
});
}
This will let you to have both: property to enable/disable button, which can be used in binding (to another control, e.g. CheckBox.IsChecked) and command which can have independent condition to disable button when command shouldn't be executed (typically in async command delegate, when it performs a long running command, but for this you may want to check this answer.).

if you bind the command Property of the Button to an ICommand Property in your Viewmodel, then you do NOT need to handle the IsEnabled Property of the Button because its handled by the CanExecute Method of the ICommand implementation.
google for RelayCommand or DelegateCommand

Thanks to the posters for your help, I wanted to share the working solution for others. I used the DelegateCommand, but had to change some parts in the loginPageViewModel to make it work: I also updated the xaml so that the controls were all inactive after a successful authentication.
the loginPage xaml:
<Label x:Name="usernameLabel" Content="{x:Static res:Strings.LoginPage_usernameLabel_content}" HorizontalAlignment="Left" Margin="10,4,0,0" Grid.Row="0" VerticalAlignment="Top" Width="130" FontSize="16" Height="36" Grid.Column="1"/>
<TextBox x:Name="usernameTextBox" Grid.Column="2" Grid.Row="0" TextWrapping="Wrap"
Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding AuthenticateButtonEnabled}"
Margin="10,5,0,6" FontSize="16" HorizontalAlignment="Left" Width="130" TextChanged="usernameTextBox_TextChanged"/>
<Label x:Name="passwordLabel" Content="{x:Static res:Strings.LoginPage_passwordLabel_content}" Margin="10,5,0,0" Grid.Row="1" VerticalAlignment="Top" FontSize="16" Height="36" Grid.RowSpan="2" HorizontalAlignment="Left" Width="130" Grid.Column="1"/>
<PasswordBox x:Name="passwordBox" Grid.Column="2" Margin="10,0,0,9"
PasswordChanged="PasswordBox_PasswordChanged"
IsEnabled="{Binding AuthenticateButtonEnabled}"
Grid.Row="1" FontSize="16" HorizontalAlignment="Left" Width="130"/>
<Button x:Name="authenticateButton" Content="{x:Static res:Strings.LoginPage_authenticateButton_content}"
Grid.Column="2" Margin="53,4,0,10"
Grid.Row="2" FontSize="16"
IsEnabled="{Binding AuthenticateButtonEnabled}"
Command="{Binding Path=AuthenticateCommand}" HorizontalAlignment="Left" Width="87"/>
the loginPageViewModel:
....
private bool _authenticateButtonEnabled;
private DelegateCommand _authenticateCommand;
public bool AuthenticateButtonEnabled {
get { return _authenticateButtonEnabled; }
set
{
_authenticateButtonEnabled = value;
DynamicOnPropertyChanged(); // this is so named to not content with onPropertyChanged defined elsewhere.
AuthenticateCommand.Update();
}
}
...
public DelegateCommand AuthenticateCommand
{
get {
if (_authenticateCommand == null)
{
_authenticateCommand = new DelegateCommand(Authenticate, AuthenticateEnded);
}
return _authenticateCommand;
}
}
private bool AuthenticateEnded(object obj) {
return _authenticateButtonEnabled;
}
private async void Authenticate(object obj)
{
AuthenticateButtonEnabled = false;
ResponseTextBlock = Strings.LoginPage_responseBlock_content_checking;
i3SoftHttpClient _httpClient = new i3SoftHttpClient();
i3SoftUser _i3SoftUser;
AuthenticateCommand.CanExecute(false);
....
// if authentication does not succeed - turn the buttons back on.
AuthenticateCommand.CanExecute(true);
}
and to the Delegate command class i added:
public void Update()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}

Related

Clearing textboxes

I'm making an input page and I'm trying to implement a reset button. After a click on the button, the UI should be empty again.
I thought that entering an empty string would deal with this. In the code it seems to work and the value does get changed to "" but in the UI the typed text stays visible (so it doesn't show the empty "" string). I also tried with string.Empty as suggested in here but that also doesn't seem to work.
Am I missing something here? I'm kinda new to programming so if I did something horribly wrong, don't laugh too hard ;)
I'm using an MVVM pattern and Fody Weaver to deal with the property changed part of the code.
The UI / XAML
<TextBlock Text="Naam:"
Grid.Column="0"
Style="{StaticResource InputInputBlock}"
/>
<TextBox Foreground="White"
Grid.Column="1"
Text="{Binding Name, Mode=TwoWay}"
Style="{StaticResource InputInputBox}"
/>
<Button Content="Reset"
Height="50"
Width="150"
Grid.Column="0"
Grid.Row="2"
VerticalAlignment="Top"
HorizontalAlignment="Center"
Style="{StaticResource FlatButton}"
Command="{Binding ResetCommand}"
/>
The view model
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
}
}
public AddStakeholderViewModel()
{
ResetCommand = new RelayCommand(() => ResetForm());
}
private void ResetForm()
{
Name = " ";
}
You can implement the INotifyPropertyChanged interface in your class. This works for me:
public class Person : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Name");
}
}
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
XAML:
<TextBox Foreground="White"
Grid.Column="1"
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Style="{StaticResource InputInputBox}"
/>
MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = newPerson;
}
Person newPerson = new Person();
private void button_Click(object sender, RoutedEventArgs e)
{
newPerson.Name = "";
}
}

Binding not updating as expected

I have a pre-loader screen that essentially says "please wait" as I have server-side computation being processed for several seconds.
I have a value converter that should update and get rid of the loader screen once the server-side computation has been processed and stored.
WPF Portion
<Window.Resources>
<Client:BoolToVisibilityConverter x:Key="loadConverter"/>
</Window.Resources>
.
.
.
<Border Panel.ZIndex="1000" BorderBrush="Yellow" BorderThickness="1" Visibility="{Binding OverlayVisibility, Converter={StaticResource loadConverter}, Mode=TwoWay}" Background="#80000000" Margin="0,0,0,-25.6">
<Grid>
<TextBlock Panel.ZIndex="100" Margin="0" TextWrapping="Wrap" Text="Loading Passive Seismic Nodes..." HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="21" FontWeight="Bold" Foreground="#FFF"/>
<TextBlock Panel.ZIndex="100" Margin="11,136,12,75.2" TextWrapping="Wrap" Text="Please Wait..." HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="14" FontWeight="Bold" Foreground="#FFF"/>
</Grid>
</Border
I have an OverlayVisibility property in this class that is a boolean value to help toggle the preloader screen.
Portion of the Client Class
public void LoadRoles()
{
foreach (var roleName in ChefServer.GetCookbookNames())
{
Cookbooks.Add(new Cookbook() { CookbookName = roleName });
}
//This isn't making the preloader disappear
uiContext.Send((_ => { overlayVisibility = false; }), null);
Console.WriteLine("Done!"); //This gets called successfully
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
//This function gets called when WPF form loads
public void Loader()
{
uiContext = SynchronizationContext.Current; //Declared at top in namespace
OverlayVisibility = true; //Make preloader screen show at boot
}
#region Props
private bool overlayVisibility;
public bool OverlayVisibility
{
get { return overlayVisibility; }
set
{
overlayVisibility = value;
OnPropertyChanged("OverlayVisibility");
}
}
#endregion
You're setting overlayVisibility (the field), not OverlayVisibility (the property).
Therefore, you never actually raise PropertyChanged, and WPF never finds out.
Are you sure you have set up DataContext correctly? try adding the following line to your c'tor if you have not set it up yet
this.DataContext = this;

Enable Disable Button base on validation in WPF

I have a WPF Form, that has a text box and a button. I am validating the text box to have only characters. Validation works fine, But I need to disable the button if there are validation error and enable them if there are no validation errors.
Below is my code:
<TextBox Name="tbProductName" Grid.Column="1" HorizontalAlignment="Left" Height="25" Margin="4,9,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top" Width="213"
Text="{Binding Path = ProductCode, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True,NotifyOnValidationError=True}">
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder x:Name="textBox"/>
<TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red"/>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
<Button Name ="btnDownload" Content="Download" Grid.Column="2" HorizontalAlignment="Left" Margin="10,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="87" Height="24" Click="btnDownload_Click"/>
public class ViewModel: System.ComponentModel.INotifyDataErrorInfo
{
private readonly Dictionary<string, ICollection<string>> _validationErrors = new Dictionary<string, ICollection<string>>();
private readonly Model _productCode = new Model();
public string ProductCode
{
get { return _productCode.ProductCode; }
set
{
_productCode.ProductCode = value;
ValidateModelProperty(value, "ProductCode");
}
}
protected void ValidateModelProperty(object value, string propertyName)
{
if (_validationErrors.ContainsKey(propertyName))
_validationErrors.Remove(propertyName);
PropertyInfo propertyInfo = _productCode.GetType().GetProperty(propertyName);
IList<string> validationErrors =
(from validationAttribute in propertyInfo.GetCustomAttributes(true).OfType<ValidationAttribute>()
where !validationAttribute.IsValid(value)
select validationAttribute.FormatErrorMessage(string.Empty))
.ToList();
_validationErrors.Add(propertyName, validationErrors);
RaiseErrorsChanged(propertyName);
}
/* Raise the ErrorsChanged for all properties explicitly */
protected void ValidateModel()
{
_validationErrors.Clear();
ICollection<ValidationResult> validationResults = new List<ValidationResult>();
ValidationContext validationContext = new ValidationContext(_productCode, null, null);
if (!Validator.TryValidateObject(_productCode, validationContext, validationResults, true))
{
foreach (ValidationResult validationResult in validationResults)
{
string property = validationResult.MemberNames.ElementAt(0);
if (_validationErrors.ContainsKey(property))
{
_validationErrors[property].Add(validationResult.ErrorMessage);
}
else
{
_validationErrors.Add(property, new List<string> { validationResult.ErrorMessage });
}
}
}
/* Raise the ErrorsChanged for all properties explicitly */
RaiseErrorsChanged("ProductCode");
}
#region INotifyDataErrorInfo members
public event EventHandler<System.ComponentModel.DataErrorsChangedEventArgs> ErrorsChanged;
private void RaiseErrorsChanged(string propertyName)
{
if (ErrorsChanged != null)
ErrorsChanged(this, new System.ComponentModel.DataErrorsChangedEventArgs(propertyName));
}
public System.Collections.IEnumerable GetErrors(string propertyName)
{
if (string.IsNullOrEmpty(propertyName)
|| !_validationErrors.ContainsKey(propertyName))
return null;
return _validationErrors[propertyName];
}
public bool HasErrors
{
get { return _validationErrors.Count > 0; }
}
#endregion
}
public class Model
{
[Required(ErrorMessage = "You must enter a product code to download.")]
[RegularExpression(#"^[a-zA-Z]+$", ErrorMessage = "The Product Code must only contain letters (a-z, A-Z).")]
public string ProductCode { get; set; }
}
How can I set the isEnabled property of the button to Validation.HasErrors?
Ideally your button would bind it's "Command" property to a public ICommand viewmodel property.
The CanExecute method would be evaluated, returning true or false. The button would be enabled/disabled accordingly.
You can read more about ICommand, along with an implementation of the interface here.
Below are the changes required, assuming your using the RelayCommand (See Fig. 3) implementation described in the aforementioned article.
Register the command
private readonly ICommand _downloadCommand = new RelayCommand(OnDownload, CanDownload);
Used for binding:
public ICommand DownloadCommand { get { return _downloadCommand; } }
Methods to invoke when the command is executed:
private void OnDownload(object parameter) { ... Do your download code here ... }
private bool CanDownload(object parameter) { return HasErrors == false; }
Update your XAML binding:
<Button Content="Download" Grid.Column="2" HorizontalAlignment="Left" Margin="10,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="87" Height="24" Command="{Binding DownloadCommand}" />
I would say use the same technique for the Binding you used for the rest of your code!!
In your xaml
<Button Name ="btnDownload" IsEnabled="{Binding Path= BtnIsEnabled} click="btnDownload_Click"/>
In your code
public bool BtnIsEnabled
{
get { return this._BtnIsEnabled; }
set
{
this._BtnIsEnabled = value;
base.OnPropertyChanged("BtnIsEnabled");
}
}
private bool _BtnIsEnabled;
and then where ever you want to disable your Button go something like
BtnIsEnabled = Validation.HasErrors ? false : true ;
Obviously this is a pseudo scribble. Unless you're after something very specific that I can't seem to find out. Seems you used binding in your code already and I would guess you know how to.

How to call Command from MVVM binding

I have below code to bind ListBox data using MVVM. I would like to implment the Command from MVVM, data is binded completely and I don't know why it doesn't work with the Command. I don't receive the message when clicking on the button.
ViewModel
public class BookmarkViewModel : INotifyPropertyChanged
{
public BookmarkViewModel()
{
DataSource ds = new DataSource();
deleteBookmark = new Command(executeCommand) { Enabled = true };
_bk = ds.getBookmarkDetail();
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
List<BookmarkDetail> _bk;
public List<BookmarkDetail> Bookmarks
{
get { return _bk; }
set
{
if (_bk != value)
{
_bk = value;
OnPropertyChanged("Bookmarks");
}
}
}
private Command deleteBookmark;
public Command DeleteBookmark
{
get
{
return deleteBookmark;
}
set
{
deleteBookmark = value;
}
}
void executeCommand()
{
System.Windows.MessageBox.Show(_bk[0].SuraName);
}
public class Command : ICommand
{
private readonly Action executeAction;
private bool enabled;
public bool Enabled
{
get
{
return enabled;
}
set
{
if (enabled != value)
{
enabled = value;
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
}
}
public Command(Action executeAction)
{
this.executeAction = executeAction;
}
public bool CanExecute(object parameter)
{
return enabled;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
executeAction();
}
}
}
and XAML binding
<ListBox x:Name="lsbBookmarks" FontFamily="./Fonts/ScheherazadeRegOT.ttf#Scheherazade"
FlowDirection="RightToLeft"
Style="{StaticResource ListBoxStyle1}"
ItemsSource="{Binding Bookmarks}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="60"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.Column="0"
HorizontalAlignment="Stretch">
<TextBlock Padding="20,0,10,0" HorizontalAlignment="Stretch">
<Run FontSize="50" Text="{Binding ArabicText.ArabicAyaNumber}"
FontFamily="./Fonts/KPGQPC.otf#KFGQPC Uthmanic Script HAFS"
Foreground="Blue"/> <Run FontSize="30" Text="{Binding ArabicText.Aya}"/>
</TextBlock>
</StackPanel>
<Button Grid.Column="1" Tag="{Binding ArabicText.ArabicTextID}"
VerticalAlignment="Center"
Height="60" Width="50" HorizontalAlignment="Right"
Content="X" BorderBrush="Red"
Background="Red" BorderThickness="0"
Padding="0" Command="{Binding DeleteBookmark}"></Button>
</Grid>
<Line X1="0" X2="1" Y1="0" Y2="0" Stretch="Fill" VerticalAlignment="Bottom"
StrokeThickness="1" Stroke="LightGray" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Any Ideas, How to implement the Command using MVVM?
Thanks!
If I were you I would:
Move the Command implementation to a separate file or declare it outside the scope of the BookmarkViewModel class.
Use the second option as ig2r suggested. Your binding would look like this:Command="{Binding DataContext.DeleteBookmark, ElementName=lsbBookmarks}" You can also use any other ElementName other than lsbBookmarks that's defined as a parent of the ListBox.
It appears that your DeleteBookmark command is exposed as a property on the BookmarkViewModel class, whereas the actual data context within the DataTemplate used to render individual ListBox items will be an instance of BookmarkDetail. Since BookmarkDetail does not declare a DeleteBookmark command, the binding fails.
To correct this, either:
Define and expose the DeleteBookmark command on the BookmarkDetail class, or
Extend your command binding to tell the binding system where to look for the delete command, e.g., Command="{Binding DataContext.DeleteBookmark, ElementName=lsbBookmarks}" (untested).

How call a general method with data binding?

I would like to understand how to correctly use MVVM and data binding when we are working with many properties.
<Window x:Class="WpfApplication1.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">
<Grid>
<TextBox Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="textBox1" VerticalAlignment="Top" Width="463" Text="{Binding OriginalText, UpdateSourceTrigger=PropertyChanged}" />
<Label Height="28" HorizontalAlignment="Left" Margin="12,242,0,0" Name="label1" VerticalAlignment="Top" Width="463" Content="{Binding ModifiedText}"/>
<CheckBox Content="Upper" Height="16" HorizontalAlignment="Left" Margin="12,41,0,0" Name="checkBox1" VerticalAlignment="Top" />
<CheckBox Content="Underline" Height="16" HorizontalAlignment="Left" Margin="12,63,0,0" Name="checkBox2" VerticalAlignment="Top" />
<CheckBox Content="Bold" Height="16" HorizontalAlignment="Left" Margin="12,85,0,0" Name="checkBox3" VerticalAlignment="Top" />
<CheckBox Content="Shadow" Height="16" HorizontalAlignment="Left" Margin="12,107,0,0" Name="checkBox4" VerticalAlignment="Top" />
<CheckBox Content="Red" Height="16" HorizontalAlignment="Left" Margin="12,129,0,0" Name="checkBox5" VerticalAlignment="Top" />
<CheckBox Content="Scary" Height="16" HorizontalAlignment="Left" Margin="12,151,0,0" Name="checkBox6" VerticalAlignment="Top" />
<CheckBox Content="Remove first letter" Height="16" HorizontalAlignment="Left" Margin="12,173,0,0" Name="checkBox7" VerticalAlignment="Top" />
<CheckBox Content="Remove last letter" Height="16" HorizontalAlignment="Left" Margin="12,195,0,0" Name="checkBox8" VerticalAlignment="Top" />
</Grid>
I have a OriginalText TextBox and a ModifiedText Label. When I check a box I would like to directly apply the modification without having to click a button. How should I do that?
In my ViewModel I created all the properties that are binded to the XAML CheckBox.
private string _originalText = string.Empty;
public string OriginalText
{
get { return _originalText; }
set
{
_originalText = value;
NotifyPropertyChanged("OriginalText");
}
}
private string _modifiedText;
public string ModifiedText
{
get { return _originalText; }
set
{
_originalText = value;
NotifyPropertyChanged("ModifiedText");
}
}
private bool upper;
public bool Upper
{
get { return upper; }
set
{
upper = value;
NotifyPropertyChanged("Upper");
// Should I notify something else here or call a refresh method?
}
}
private bool removeFirstLetter;
public bool RemoveFirstLetter
{
get { return removeFirstLetter; }
set
{
removeFirstLetter = value;
NotifyPropertyChanged("RemoveFirstLetter");
// Should I notify something else here or call a refresh method?
}
}
// ...
Then I created a Work method in the same ViewModel class at this moment. I ll move this method into the business later.
private void Work()
{
string result = _originalText;
if (Upper)
result = result.ToUpper();
if (removeFirstLetter)
result = result.Substring(1, result.Length);
// if ...
ModifiedText = result;
}
My question is when, where should I call the work method? Should I call it in each setter or getter? I dont like the idea. I do something wrong...
Thank you.
In your particular case, you should create a Boolean property using the INotifyPropertyChanged interface. Now bind this property to your "IsChecked" check box property. By calling your Work() method inside the setter, every time the check box is "ticked" the setter will trigger each time.
The answer to your question is very simple: Use Commands.
Commands are MVVM's way to realize the binding to a method in your ViewModel. The implementation of Commands follows a very standard pattern. You will find plenty of information over the Internet here is just a short sketch:
Commands implemented in your ViewModel have to be of type ICommand and every Command has to come along with to methods in your code one responsible for executing the actual method and the other one for checking if the execution is currently possible.
These methods have to be named CanExecute and Execute respectively. It is commonly the case to facilitate the use of several Commands with a small helping class called DelegateCommand which provides delegates for the previously mentioned methods.
Take this class as it is without any modifications:
public class DelegateCommand<T> : ICommand {
private Predicate<T> canExecute;
private Action<T> execute;
public event EventHandler CanExecuteChanged;
public DelegateCommand (Predicate<T> canExecute, Action<T> execute) {
this.canExecute = canExecute;
this.execute = execute;
}
public bool CanExecute (object param) {
return canExecute((T)param);
}
public void Execute (object param) {
execute((T)param);
}
public void CanExecuteChangedRaised () {
CanExecuteChanged(this, new EventArgs());
}
}
Then your Command declarations are of type DelegateCommand rather than of type ICommand. See the following example to illustrate and you will get the idea:
Supose you have a method foo() in your ViewModel you want to be called with a click to a button:
class ViewModel {
// ...
public DelegateCommand<object> FooCommand { get; set; }
public ViewModel () {
FooCommand = new DelegateCommand<object>(CanExecuteFooCommand, ExecuteFooCommand);
}
public bool CanExecuteFooCommand (object param) {
return true;
}
public void ExecuteFooCommand (object param) {
foo();
}
// ...
}
Supposing you have set your ViewModel as the controls DataContext via it's DataContext property the only thing left to do is to bind the FooCommand to your button like this:
That's it!
APPENDIX (referring to comment):
In order to have some action take place without actually hitting the Button you would simply have to track any changed in the UI with your ViewModel and react accordingly - that's what MVVM is about: Track the data from the UI modify or process them and populate them back to the UI.
To react on a TextBox Text change create a corresponding string property in your ViewModel and track whether the new ioncoming value from the View is different to the current textBox text:
private string _text;
public string Text {
get { return _text; }
set {
// the text in the TextBox is about to change.
if (!_text.Equals(value))
{
doSomething();
}
_text = value;
FirePropertyChanged("Text");
}
}
For doing the same with your CheckBox you can apply ICommand as described above since CheckBox is derived from Button and is therefor offering the Command property.

Categories

Resources