Enable Button on RelayCommand WPF - c#

Button is not getting enable on Command Enable, doEnable method. Click method is disabling button.
Button IsEnabled is Bind with ViewModel public property IsEnable, which is setting true on doEnable.
Kindly advise what is wrong in below Code
XAML:
<Button Content="{Binding DataText}" Height="30" Width="80" Command="{Binding Enable}" Click="ButtonBase_OnClick" IsEnabled="{Binding IsEnable}" ></Button>
Window2.cs:
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
DataContext = new ButtonEnableViewModel();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
if(sender is Button btn)
btn.IsEnabled = false;
}
}
ButtonEnableViewModel.cs
class ButtonEnableViewModel : INotifyPropertyChanged
{
public ButtonEnableViewModel()
{
IsEnable = true;
DataText = "Click Here";
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChange(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
private RelayCommand _enableCmd;
public RelayCommand Enable => _enableCmd ?? (_enableCmd = new RelayCommand(doEnable));
public bool IsEnable { get; set; }
public string DataText { get; set; }
protected void doEnable(object obj)
{
IsEnable = true;
DataText = "Clicked";
OnPropertyChange(nameof(IsEnable));
OnPropertyChange(nameof(DataText));
}
}

You should generally use the CanExecute method of the command to disable the Button. Most implementations of the ICommand interface accepts a Predicate<object> that you can use to tell the command when to enable the command/button.
In this case you might simply remove the event handler from the code-behind though and just handle the command in the view model:
<Button Content="{Binding DataText}" Height="30" Width="80" Command="{Binding Enable}" IsEnabled="{Binding IsEnable}" />
This should work since you set the IsEnable property in the Execute method of the command and raise the PropertyChanged event. You generally don't handle Click events in the code-behind of the view when you bind to a command of a view model.
If you use the CanExecute method of the command, you don't need to bind to the IsEnable property:
<Button Content="{Binding DataText}" Height="30" Width="80" Command="{Binding Enable}" />
View Model:
private RelayCommand _enableCmd;
public RelayCommand Enable => _enableCmd ?? (_enableCmd = new RelayCommand(doEnable, x => _isEnabled));
private bool _isEnabled;
public string DataText { get; set; }
protected void doEnable(object obj)
{
_isEnabled = true;
Enable.RaiseCanExecuteChanged();
DataText = "Clicked";
OnPropertyChange(nameof(DataText));
}

Related

Updating textbox from Button click C#

I have the following textbox
<TextBox Grid.Column="1"
Grid.Row="1"
Name="groupAddressBox"
Width ="80"
Text="{Binding Path=GroupAddress, Converter={StaticResource groupAddressConverter}}"/>
When I change the text manually, it's all good.
But when I try to do this via a button
private void Test_Click(object sender, RoutedEventArgs e)
{
groupAddressBox.Text = "0/0/1";
}
Although the text changes, the source is not updated, and when I click on ok, it recognizes the value that was there before the change.
I cannot upgrade the source straight away, so I prefer to do this this way.
Is there something that can help me force the source upgrade via this way?
Based on your question, I tried to create a Simple Example of MVVM Pattern with very basic functionality. Please do necessary change to XAML and CS file as I took the highlighted code only.
Helper Classes
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
public class CommandHandler : ICommand
{
public event EventHandler CanExecuteChanged { add { } remove { } }
private Action<object> action;
private bool canExecute;
public CommandHandler(Action<object> action, bool canExecute)
{
this.action = action;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute;
}
public void Execute(object parameter)
{
action(parameter);
}
}
ViewModel
public class ViewModel : ViewModelBase
{
private string groupAddress;
public string GroupAddress
{
get
{
return groupAddress;
}
set
{
if(value != groupAddress)
{
groupAddress = value;
OnPropertyChanged("GroupAddress");
}
}
}
public ViewModel()
{
}
private ICommand clickCommand;
public ICommand ClickCommand
{
get
{
return clickCommand ?? (clickCommand = new CommandHandler(() => MyAction(), true));
}
}
public void MyAction()
{
GroupAddress = "New Group Address";
}
}
Window Xaml
<TextBox Grid.Column="1" Grid.Row="1" Width ="80"
Text="{Binding GroupAddress, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Push" Style="{StaticResource TransparentButtonStyle}"
Margin="5" Command="{Binding ClickCommand}"/>
Window Xaml cs
ViewModel vm = new ViewModel();
this.DataContext = vm;

How to check all checkbox C#

i have a few checkboxes an I want funcionality to check or uncheck all checkbox. Here is a class .cs and xaml code. How to add funcionality for check or uncheck all?
public static event PropertyChangedEventHandler IsCheckedChanged;
private bool isChecked;
public WorkStep(string name)
{
Name = name;
IsChecked = true;
}
public WorkStep(string name, bool isChecked)
{
Name = name;
IsChecked = isChecked;
}
public string Name
{
get;
}
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
OnIsCheckedChanged();
}
}
private void OnIsCheckedChanged()
{
PropertyChangedEventHandler handler = IsCheckedChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs("IsChecked"));
}
and xaml:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
I would use the INotifyPropertyChanged interface like shown in the class below:
public class myClass : INotifyPropertyChanged
{
private bool _IsChecked;
public bool IsChecked
{
get { return _IsChecked; }
set
{
_IsChecked = value;
OnPropertyChanged("IsChecked");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Then I would bind the property IsChecked to all of my checkboxes like shown below in the xaml:
<Grid>
<CheckBox x:Name="checkBox1" IsChecked="{Binding IsChecked,UpdateSourceTrigger=PropertyChanged}" Content="CheckBox" HorizontalAlignment="Left" Margin="116,90,0,0" VerticalAlignment="Top"/>
<CheckBox x:Name="checkBox2" IsChecked="{Binding IsChecked,UpdateSourceTrigger=PropertyChanged}" Content="CheckBox" HorizontalAlignment="Left" Margin="116,126,0,0" VerticalAlignment="Top"/>
<CheckBox x:Name="checkBox3" IsChecked="{Binding IsChecked,UpdateSourceTrigger=PropertyChanged}" Content="CheckBox" HorizontalAlignment="Left" Margin="116,164,0,0" VerticalAlignment="Top"/>
<Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="380,235,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
</Grid>
In the MainWindow class make a new instance of the class where the IsChecked property is located (in my case myClass MyClass = new myClass()). Then add the newly created instance MyClass to the DataContext of your MainWindow (DataContext = MyClass;). I use a button control in this example to check and uncheck all of my checkboxes. If the value of the property IsChecked is true all of the checkboxes are checked and if it's false all of the checkboxes are unchecked. The MainWindow class is shown below :
public MainWindow()
{
InitializeComponent();
DataContext = MyClass;
}
myClass MyClass = new myClass();
private void button_Click(object sender, RoutedEventArgs e)
{
if(MyClass.IsChecked)
MyClass.IsChecked = false;
else
MyClass.IsChecked = true;
}

Binding xaml property visibility to ViewModel, control with button

I have a StackLayout property in xmal like shown below:
<StackLayout x:Name="_infoView"
Margin="0,10,0,10"
BackgroundColor="Black"
IsVisible="{Binding State}"/>
and a binding bool variable in ViewModel
private Boolean _state = true;
public Boolean State
{
get { return _state; }
set { }
}
I have a button in my xmal and would like to control the visibility of my StackLayout, So I did something like this:
<Button x:Name="CloseButton"
Grid.Row="0"
Grid.Column="3"
Command="{Binding CloseWindowCommand}"/>
and in ViewModel
CloseWindowCommand = new Command(CloseWindowTapped, CanCloseWindowTapped);
public ICommand CloseWindowCommand { get; set; }
public void CloseWindowTapped()
{
State = false;
}
public bool CanCloseWindowTapped()
{
return true;
}
I'd assume, by tap on the CloseButton, my StackLayout will gone... but it is not working
ViewModel should implement INotifyPropertyChanged interface for informing View about changes.
public class ViewModel : INotifyPropertyChanged
{
// Implementing INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName);
}
// In the setter of property raise event to inform view about changes
private Boolean _state = true;
public Boolean State
{
get
{
return _state;
}
set
{
_state = value;
RaisePropertyChanged();
}
}
}

Tooltip on button click while button is disabled

Have a navigation command which needs to display a tooltip on click, while disabled, so that the user knows why it's disabled. The problem I'm having is I have no idea how to pass the TouchDown event from my xaml file to my viewmodel. Is there some way to bind this rather than creating an event in the command.xaml.cs?
Command is structured as follows. I have a single CommandButton.xaml and CommandButton.xaml.cs while everything to set up the button is handled by the VM (text, image, command executed etc) code as an example below.
<Button Focusable="True" Name="Btn1" Command="{Binding CommandToExecute}" Tag="{Binding Text}" Foreground="{x:Null}" Style="{DynamicResource ButtonStyle}" ToolTipService.ShowOnDisabled="true" TouchDown="Btn1_OnTouchDown" >
<Button.ToolTip>
<StackPanel>
<TextBlock>Test</TextBlock>
<TextBlock>Load stencil, or not your choice.</TextBlock>
</StackPanel>
</Button.ToolTip>
<shellModule:AutoGreyableImage Source="{Binding Image}" />
</Button>
As for the code behind, I have that split with the majority of the handler stuff in a base command class as follows.
public abstract class BaseCommand : BindableBase
{
protected IModuleManager ModuleManager { get; set; }
protected IRegionManager RegionManager { get; set; }
protected BaseCommand(IRegionManager regionManager, IModuleManager moduleManager, string pageName = null)
{
RegionManager = regionManager;
ModuleManager = moduleManager;
Text = GetButtonText(pageName + "_BtnTxt");
Image = (ImageSource)Application.Current.FindResource(pageName + "_BtnImg");
}
private string _text;
private ImageSource _image;
public ICommand CommandToExecute => new DelegateCommand<object>(Command, Evaluate);
protected abstract void Command(object obj);
protected virtual bool Evaluate(object obj)
{
return false;
}
public string Text
{
get { return _text; }
set { SetProperty(ref _text, value); }
}
public ImageSource Image
{
get { return _image; }
set { SetProperty(ref _image, value); }
}
protected string GetButtonText(string key)
{
string uiString;
var locExtension = new LocTextExtension
{
Key = "Resources",
ResourceIdentifierKey = key
};
locExtension.ResolveLocalizedValue(out uiString);
return uiString;
}
}
and then the command specific stuff in the viewmodel.
public class Page1CommandViewModel : BaseCommand, IPage1CommandViewModel
{
public Page1CommandViewModel(IRegionManager regionManager, IModuleManager moduleManager) : base( regionManager, moduleManager, PageNames.Page1 )
{
}
protected override void Command(object obj)
{
Task.Run(() =>
{
ModuleManager.LoadModule(ModuleNames.Page1Module);
RegionManager.RequestNavigate(RegionNames.ContentRegion, new Uri(PageNames.Page1, UriKind.Relative));
});
}
}
If anyone could point me in the right direction it'd be greatly appreciated.
Maybe instead of disabling the button, re-point the button to a different method, which would then display your error/tooltip message. (You could then pass in the string stating the reason for the inactivity in your method paramaters/variables.)
I would also advise you change the class/visual properties of the button so that it looks disabled.
After much googling, I've come up with a solution myself, thanks in part to comments others had made leading me in the right direction. Wrapped up my button in a contentControl, and instead have applied the tooltip to this.
<ContentControl MouseDown="ContentControl_MouseDown">
<ContentControl.ToolTip>
<ToolTip Placement="Mouse" Content="Testing" />
</ContentControl.ToolTip>
<Button Focusable="True" x:Name="Btn1" Command="{Binding CommandToExecute}" Tag="{Binding Text}" Foreground="{x:Null}" Style="{DynamicResource ButtonStyle}" ToolTipService.ShowOnDisabled="true">
<shellModule:AutoGreyableImage Source="{Binding Image}" />
</Button>
</ContentControl>
And on the button.xaml.cs put in events to handle timings of the button click etc.
Timer Timer { get; set; }
ToolTip toolTip { get; set; }
public CommandButton()
{
InitializeComponent();
Timer = new Timer {Interval = 3000};
Timer.Elapsed += OnTimerElapsed;
}
private void CloseToolTip()
{
if (toolTip != null)
{
toolTip.IsOpen = false;
}
}
private void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
Timer.Stop();
Application.Current.Dispatcher.BeginInvoke((Action)CloseToolTip, DispatcherPriority.Send);
}
private void ContentControl_MouseDown(object sender, MouseButtonEventArgs e)
{
toolTip = ((ToolTip)((Control)sender).ToolTip);
toolTip.IsOpen = true;
Timer.Start();
}
timers taken from the following location.
https://social.msdn.microsoft.com/Forums/vstudio/en-US/9e3eb4ab-ed0f-40ad-ad47-a1fff8e0fe8d/tooltips-in-wpf-touch-applications?forum=wpf
This allows the button to be disabled, and the tooltip to still display on click. All I need to do now is wrap up the tooltip contents in a binding and disable the tooltip on hover (not required) and it's all solved.
Leaving question open for the time being however, as a better solution may present itself.

WPF MVVM button command that gets textbox value

I'm having a great trouble with understanding how button command works. I have something like this
{Binding TxtBox} gets value from model, let's say it's "aaa". I would like click the button and the value should appear in the second textbox (the one with {Binding TxtBox2}).
This is my xaml:
<TextBox Text="{Binding TxtBox, Source={StaticResource viewModel}}" />
<TextBox Text="{Binding TxtBox2, Source={StaticResource viewModel}}" />
<Button Command="{Binding ClickCommand}"/>
This is my ViewModel:
public class CommandHandler : ICommand
{
private Action _action;
private bool _canExecute;
public CommandHandler(Action action, bool canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action();
}
}
Do I really need this CommandHandler class? I copied the code from the net.
public string TxtBox
{
get { return Model.TxtBoxValue; }
set { Model.TxtBoxValue = value; }
}
public string TxtBox2 { get; set; }
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute)); // I believe that when the button is clicked MyAction() is triggered, right?
}
}
private bool _canExecute = true;
public void MyAction()
{
this.TxtBox2 = this.TxtBox; // should something like this work? Because right now it doesn't
}
The second textbox's binding never gets notified that it's bound property is changed. When you set this.TxtBox2 you should fire the propertychanged event for that property so the binding will be updated.
See think link for everything on bindings
I don't know if you are using prism as mvvm framework but that comes with the DelegateCommand class. I don't think there is a simple/lightweight implementation in the .net framework. See this link for the mvvm framework and the delegate command
The View reacts to binding changes through PropertyChanged events, of which you have none. Have anything that binds to the View implement INotifyPropertyChanged and then fire events when props change, and you're all set for your bindings to work (one way or two way).
Change your model to look like this and it should work for you.
public class MyViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged = null;
protected virtual void RaisePropertyChanged(string propName)
{
if(PropertyChanged != null)
{
Task.Run(() => PropertyChanged(this, new PropertyChangedEventArgs(propName)));
}
}
#endregion
public string TxtBox
{
get { return Model.TxtBoxValue; }
set
{
Model.TxtBoxValue = value;
RaisePropertyChanged("TxtBox");
}
}
// presuming TxtBox2Value is in Model...else use a field
public string TxtBox2
{
get { return Model.TxtBox2Value; }
set
{
Model.TxtBox2Value = value;
RaisePropertyChanged("TxtBox2");
}
}
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute)); // I believe that when the button is clicked MyAction() is triggered, right?
}
}
private bool _canExecute = true;
public void MyAction()
{
this.TxtBox2 = this.TxtBox; // should something like this work? Because right now it doesn't
}
}
IMO - it is better to have your Model implement INotifyPropertyChanged and then bind directly to it rather than wrap it in your ViewModel. If Model : INotifyPropertyChanged, then your ViewModel now looks like this:
public class MyViewModel
{
// fire prop changed event here if this model will be swapped out after the ctor...otherwise don't worry about it
public Model Model { get; set; }
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute));
}
}
private bool _canExecute = true;
public void MyAction()
{
Model = new Model();
Model.TxtBox2 = "Some new value";
}
}
...and your xaml changes to this:
<TextBox Text="{Binding Model.TxtBox, Source={StaticResource viewModel}}" />
<TextBox Text="{Binding Model.TxtBox2, Source={StaticResource viewModel}}" />
<Button Command="{Binding ClickCommand}"/>

Categories