Managing the IsEnabled property of a button - c#

I have a xaml window in my program that has a button called "Save", and a textBox. I also have a ViewModel for this window. Inside the ViewModel I have a string property for the textBox, and a bool property for IsEnabled on the button. I would like the button to only be enabled when there is text inside the textBox.
xaml:
<Button IsEnabled="{Binding SaveEnabled}" ... />
<TextBox Text="{Binding Name}" ... />
ViewModel properties:
//Property for Name
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChange(() => Name);
if (value == null)
{
_saveEnabled = false;
NotifyPropertyChange(() => SaveEnabled);
}
else
{
_saveEnabled = true;
NotifyPropertyChange(() => SaveEnabled);
}
}
}
//Prop for Save Button -- IsEnabled
public bool SaveEnabled
{
get { return _saveEnabled; }
set
{
_saveEnabled = value;
NotifyPropertyChange(() => SaveEnabled);
}
}
I think my main question here is, where do I put the code concerning this problem? As you can see above, I've tried to put it into the setter of the Name property, but it comes back with no success.

You can just do:
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged(() => Name);
NotifyPropertyChanged(() => SaveEnabled);
}
}
public bool SaveEnabled
{
get { return !string.IsNullOrEmpty(_name); }
}
EDIT: Add this to your xaml:
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}">...</TextBox>

Use ICommands that are used in MVVM:
private ICommand _commandSave;
public ICommand CommandSave
{
get { return _commandSave ?? (_commandSave = new SimpleCommand<object, object>(CanSave, ExecuteSave)); }
}
private bool CanSave(object param)
{
return !string.IsNullOrEmpty(Name);
}
private void ExecuteSave(object param)
{
}
And then use the following in the XAML Code
<TextBox Command="{Binding CommandSave}" ... />
Depending on the Framework that you use the command class works differen. For a generic implementation I suggest Relay Command.

Related

Navigation button dependent on conditions from other ViewModel

This website has greatly benefited me, but now I have to ask a question of my own.
I am new to C# and MVVM applications..
Now I am building an app with different views, one view is the navigation view which should show other views depending on the navigation buttons. These buttons depend on the entered value in a view.
Example:
There is a navigationView containing a ContentControl and two buttons (nextStep and previousStep). I have put several textboxes in a view (parameterView) and associated model (parameterViewModel) which is displayed in the ContentControl. When all parameters are entered, the user may, by means of a button (nextStep mentioned before), go to the next step/view (checkDataView).
Now the button (in navigationView) must therefore be visible when all parameters are filled in parameterView, and hidden when one parameter is not filled in. The nextStep button should activate another page in the ContentControl.
I can navigate with checkboxes or radio buttons, but only without dependence on values ​​in another viewModel.
What should I do to get the dependency of parameters in another viewModel?
My NavigationView ContentControl and buttons are defined as:
<ContentControl Grid.Row="1"
Grid.ColumnSpan="5"
Content="{Binding CurrentItemView}" />
<Button Grid.Column="0"
Grid.Row="2"
Content="Next Step"
Style="{StaticResource SubMenuButton}"
Visibility="{Binding PreviousStepCommandVisibility}"
Command="{Binding PreviousStepCommand}"/>
<Button Grid.Column="3"
Grid.Row="2"
Content="Previous Step"
Style="{StaticResource SubMenuButton}"
Visibility="{Binding NextStepCommandVisibility}"
Command="{Binding NextStepCommand}"/>
My ViewModel of above View:
namespace SomeApp.MVVM.ViewModel
{
class GenerateMenuViewModel : BaseViewModel
{
public RelayCommand PreviousStepCommand { get; set; }
public RelayCommand NextStepCommand { get; set; }
private Visibility _previousStepCommandVisibility;
public Visibility PreviousStepCommandVisibility
{
get { return _previousStepCommandVisibility; }
set { _previousStepCommandVisibility = value; }
}
private Visibility _nextStepCommandVisibility;
public Visibility NextStepCommandVisibility
{
get { return _nextStepCommandVisibility; }
set { _nextStepCommandVisibility = value; }
}
public SomethingViewModel SomethingVM { get; set; }
private object _currentItemView;
public object CurrentItemView
{
get { return _currentItemView; }
set
{
_currentItemView = value;
OnPropertyChanged();
}
}
public GenerateMenuViewModel()
{
SomethingVM = new SomethingViewModel();
CurrentItemView = SomethingVM;
}
}
}
TextBoxes in View2, which values give dependence to the navigation buttons, are defined as:
<TextBox Text="{Binding Paramater1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Paramater2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Paramater3, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
The ViewModel which belongs to the above View:
namespace SomeApp.MVVM.ViewModel
{
class View1ViewModel : BaseViewModel
{
public View1ViewModel()
{
}
private string _parameter1;
public string Parameter1
{
get { return _parameter1; }
set { _parameter1 = value; OnPropertyChanged(); }
}
private string _parameter2;
public string Parameter2
{
get { return _parameter2; }
set { _parameter2 = value; OnPropertyChanged(); }
}
private string _parameter3;
public string Parameter3
{
get { return _parameter3; }
set { _parameter3 = value; OnPropertyChanged(); }
}
}
}
The simple way is to add a property in viewModel to indicate that all the parameters are validated and succeeded.
class View1
{
public bool Isvalid { get => !validationResults.Values.Contains(false); }
Dictionary<string, bool> validationResults = new Dictionary<string, bool>
{
{ nameof(Val1), false }
};
string val1 = "";
public string Val1
{
get => val1; set
{
val1 = value;
OnPropertyChanged();
validationResults[nameof(Val1)] = !string.IsNullOrEmpty(value); //Validation goes here
}
}

Custom View with Bindable Property not binding properly on Xamarin.Forms SAP

I have a checkbox that's supposed to fire the IsEnabled event of a button. But somehow the command that's supposed to do that never gets properly binded and therefore executed.
This is the bindable property in CheckBox.xaml.cs (the control):
public static readonly BindableProperty CheckBoxCommandProperty =
BindableProperty.Create<CheckBox, ICommand>(
checkbox =>
checkbox.CheckBoxCommand,
null,
propertyChanged: (bindable, oldValue, newValue) =>
{
CheckBox checkbox = (CheckBox)bindable;
EventHandler<bool> eventHandler = checkbox.CheckedChanged;
if (eventHandler != null)
{
eventHandler(checkbox, checkbox.IsChecked);
}
});
public event EventHandler<bool> CheckedChanged;
public ICommand CheckBoxCommand
{
get { return (ICommand)GetValue(CheckBoxCommandProperty); }
set { SetValue(CheckBoxCommandProperty, value); }
}
And this is what I have on the ViewModel:
public ICommand Next_OnClick { get; set; }
public ICommand OnCheckBoxTapChanged { get; set; }
public TermsAndConditionsViewModel()
{
Next_OnClick = new Command(NextClicked);
OnCheckBoxTapChanged = new Command(CheckBoxTapped);
}
private void CheckBoxTapped()
{
if (IsCheckedChanged)
{
Next_IsEnabled = true;
}
else
{
Next_IsEnabled = false;
}
}
The CheckBoxTapped method never gets executed and therefore the button property I wanna set never changes.
Thanks in advance folks!
What you can do is to divide the solution of this problem into several levels:
First, create a bindable property for the checkbox and call it is Checked.
I have written a snippit for it but did not really compile it so you might want to modify it if it did not work
public static readonly BindableProperty IsCheckedProperty =
BindableProperty.Create<CheckBox, bool> (w => w.IsChecked, false);
public bool IsChecked{
get { return GetValue (FooProperty); }
set { SetValue (FooProperty, value); }
}
Second, in the view model make a bool property that has a change notification
private bool _isChecked;
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
_isChecked = value;
RaisePropertyChanged("IsChecked");
}
}
Third, bind your bindable property of the check box "isChecked" to the one in the view model in your xaml:
<StackLayout>
<Checkbox IsChecked = "{Binding IsChecked}"/>
</StackLayout>
Fourth, Buttons in Xaml with MVVM are bound to commands. and in these commands there is a bool property for them that represents "CanExecute" which basically enables or disables the button. so what you have to do in the Xaml is to bind the command of the button to the command in the View Model (lets call it ClickCommand). And ClickCommand's CanExecute actually is just a method that returns the value of "IsChecked".
This will make us modify the setter of the "IsChecked" property so every time it changes it should notify the command to check the CanExecute property.
So the final code will be something like
public TermsAndConditionsViewModel()
{
NextCommand = new Command(ExecuteNextCommand,CanExecuteNextCommand);
OnCheckBoxTapChanged = new Command(CheckBoxTapped);
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked;}
set
{
_isChecked = value;
NextCommand.ChangeCanExecute(); //this will actually notify the command to enable/disable
RaisePropertyChanged("IsChecked");
}
}
public Command NextCommand {get;set;} // this type is available in Xamarin.Forms library
private bool CanExecuteNextCommand()
{
return IsChecked;
}
private void ExecuteNextCommand()
{
// your execution when the button is pressed
}
And the Xaml will be like
<StackLayout>
<Checkbox IsChecked = "{Binding IsChecked}"/>
<Button Text= "Next" Command = "{Binding NextCommand}"/>
</StackLayout>
Solved it using Xamarin.Forms.Behavior. It allows multiple bindings to controls.
For example>
<Entry Placeholder="Enter password"
Text= "{Binding TxtFirstPasswordInput}"
IsPassword="true">
<b:Interaction.Behaviors>
<b:BehaviorCollection>
<b:EventToCommand EventName="Unfocused" Command="{Binding entry_Finished}"/>
<b:EventToCommand EventName="Focused" Command="{Binding entry_Focused}"/>
</b:BehaviorCollection>
</b:Interaction.Behaviors>
</Entry>
And on the ViewModel>
public PasswordInputViewModel()
{
entry_Finished = new Command(validateAndContinue);//unfocused
entry_Focused = new Command(entryFocused); //focused
}

CanExecute() not enabling button when condition is met

I have a very simple application with a TextBox and a Button. When the text entered into the TextBox exceeds 5 characters in length, the button will be enabled. Here is the code for my ViewModel:
private string _text { get; set; }
public string Text
{
get { return _text; }
set
{
_text = value;
OnPropertyChanged("Text");
}
}
private ICommand _buttonCommand;
public ICommand ButtonCommand
{
get
{
if (_buttonCommand == null)
{
_buttonCommand = new RelayCommand(
param => this.ButtonCommandExecute(),
param => this.ButtonCommandCanExecute()
);
}
return _buttonCommand;
}
}
private bool ButtonCommandCanExecute()
{
if (this.Text.Length < 5)
{
return false;
}
else
{
return true;
}
}
private void ButtonCommandExecute()
{
this.Text = "Text changed";
}
public MainWindowViewModel()
{
//
}
The TextBox and Button are bound using this XAML:
<Button Content="Button" HorizontalAlignment="Left"
Margin="185,132,0,0" VerticalAlignment="Top" Width="120"
Command="{Binding Path=ButtonCommand}" />
<TextBox HorizontalAlignment="Left" Height="23"
Margin="185,109,0,0" TextWrapping="Wrap"
Text="{Binding Path=Text, Mode=TwoWay}" VerticalAlignment="Top" Width="120"/>
The DataContext appears to be set correctly, but here it is just because I am a WPF beginner:
private MainWindowViewModel view_model;
public MainWindow()
{
InitializeComponent();
view_model = new MainWindowViewModel();
this.DataContext = view_model;
}
When I type into the TextBox, the Button never enables.
Some implementations of ICommand interface have special method to notify whether "CanExecute" has changed. RelayCommand class (MVVM Light) has such method.
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
OnPropertyChanged("Text");
// There is a special RelayCommand method to notify "CanExecute" changed.
// After this call, the "CanExecute" state is "re-evaluated" automatically by binding using CanExecute Func passed into RelayCommand constructor.
_buttonCommand.RaiseCanExecuteChanged();
}
}
private RelayCommand _buttonCommand;
public ICommand ButtonCommand
{
get
{
if (_buttonCommand == null)
{
_buttonCommand = new RelayCommand(
param => this.ButtonCommandExecute(),
param => this.ButtonCommandCanExecute()
);
}
return _buttonCommand;
}
}
This question can be useful: What is CanExecuteChanged for?
Actually you have to make Bool Property to bind to the IsEnabled property of the Button Control. And set this property to true when your text in Textbox is more than five character - you have to do this in Setter of Text property Because this is what being called when you type in your TextBox.
Basic About Commands :- These are basically to Report the e.g Clicks events to the C# code say your Viewmodel/Page.cs . So that you can perform some Tasks. It is not related to anything about Enabling and disabling of button.
Follow the Code :-
private string _text { get; set; }
public string Text
{
get { return _text; }
set
{
_text = value;
if(_text.Length > 5)
// Enable button here
// and command does not enable Buttons they are basically report the clicks events.
IsButtonEnabled = true;
OnPropertyChanged("Text");
}
}
For Enabling Button Create Bool type property Called IsButtonEnabled and bind this property to your Button in Xaml.
private bool _IsButtonEnabled { get; set; }
public bool IsButtonEnabled
{
get { return _IsButtonEnabled ; }
set
{
_IsButtonEnabled = value;
OnPropertyChanged("IsButtonEnabled");
}
}
In Xaml :-
<Button Content="Button" HorizontalAlignment="Left"
IsEnabled="{Binding IsButtonEnabled}"
Margin="185,132,0,0" VerticalAlignment="Top" Width="120"
Command="{Binding Path=ButtonCommand}" />
Try this Little modification of your code and tell me if it Works:
private string _text { get; set; }
public string Text
{
get { return _text; }
set
{
_text = value;
OnPropertyChanged("Text");
ButtonCommandCanExecute();
}
}
private ICommand _buttonCommand;
public ICommand ButtonCommand
{
get
{
if (_buttonCommand == null)
{
_buttonCommand = new RelayCommand(
param => this.ButtonCommandExecute(),
param => this.ButtonCommandCanExecute()
);
}
return _buttonCommand;
}
}
private bool ButtonCommandCanExecute()
{
if (this.Text.Length < 5)
{
return false;
}
else
{
return true;
}
}
private void ButtonCommandExecute()
{
this.Text = "Text changed";
}
public MainWindowViewModel()
{
//
}

Creating Text boxes issue?

I have posted a previous question but there was little help so I tried to start coding it and looking up some more on my own and I'm stuck on some code. I'm trying to follow MVVM
I have created a Class called Standard which looks like this:
namespace MVVModel
{
public class Standard
{
string _title;
string _question;
public string Title
{
get { return _title; }
set { _title = value; }
}
public string Question
{
get { return _question; }
set { _question = value; }
}
}
}
Then I created ViewModel class which looks like this:
namespace MVVModel
{
class ViewModel
{
ObservableCollection<Standard> _title = new ObservableCollection<Standard>();
ObservableCollection<Standard> _question = new ObservableCollection<Standard>();
public ViewModel()
{
}
public ObservableCollection<Standard> Title
{
get
{
return _title;
}
set
{
_title = value;
}
}
public ObservableCollection<Standard> Question
{
get
{
return _question;
}
set
{
_question = value;
}
}
}
}
Here is my XAML:
<Grid>
<Button x:Name="btnTitle" Content="Title" HorizontalAlignment="Left" Margin="691,22,0,0" VerticalAlignment="Top" Width="75"/>
<Button x:Name="btnQuestion" Content="Question" HorizontalAlignment="Left" Margin="797,22,0,0" VerticalAlignment="Top" Width="75" Command="{Binding AddTitle}"/>
<ItemsControl ItemsSource="{Binding Question}" Margin="0,86,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
I just want to create a textbox dynamically but nothing it showing, any help?
I have mentioned in my previous answer about implementing INotifyPropertyChanged.
Why you again need a collection of Questions and Titles in your viewModel, this already exists in the Standard class.
You need a collection of Standard class in your main ViewModel. Thats what I get from your question if I have understood it correctly.
Here is the implementation for INotifyPropertyChanged
public class Standard : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void NotifyOfPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
protected void NotifyOfPropertyChanged<TProperty>(Expression<Func<TProperty> property)
{
NotifyOfPropertyChanged(property.GetMemberInfo().Name);
}
string _title;
ObservableCollection<string> _questions;
public string Title
{
get { return _title; }
set {
_title = value;
NotifyOfPropertyChanged(()=>Title);
}
}
public ObservableCollection<string> Questions
{
get { return _questions; }
set {
_questions = value;
NotifyOfPropertyChanged(()=>Questions);
}
}
}
You must follow a couple of steps to accomplish this task.
First you need to bind the collection of Standard to the Grid, in place of Question.
Second you need to bind a property of the previous class to the textbox.
Ex:
<DataTemplate>
<TextBox Text="{Binding Question"} />
</DataTemplate>
Edit:
I would like to mention this article to help you:
http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick-Start-Tutorial

How to update UI in MVVMLight RelayCommand scenario?

Here is a simple screen with one textblock which is "" initially, a button called "Set Text" which sets the text to the textblock and another button called "Clear text" which always clears the text in the textblock. This is how the XAML looks like.
<StackPanel>
<TextBlock Text="{Binding DisplayText, Mode=TwoWay}"></TextBlock>
<Button Content="Set Text" Command="{Binding SetTextCommand}"></Button>
<Button Content="Clear Text" Command="{Binding CancelCommand}"
IsEnabled="{Binding CanCancel, Mode=TwoWay}"/>
</StackPanel>
Here is my ViewModel code.
public class Page1VM : ViewModelBase
{
public RelayCommand SetTextCommand { get; private set; }
public RelayCommand CancelCommand { get; private set; }
public Page1VM()
{
SetTextCommand = new RelayCommand(HandleSetText, CanExecute);
CancelCommand = new RelayCommand(HandleClearButtonClick, CanExecuteCancel);
}
private void HandleSetText(string number)
{
DisplayText = number;
}
private string _displayText="";
public string DisplayText
{
get { return _displayText; }
set
{
_displayText = value;
RaisePropertyChanged("DisplayText");
RaisePropertyChanged("CanCancel");
}
}
private bool _canCancel;
public bool CanCancel
{
get
{
if (DisplayText == "")
{
return false;
}
else
{
return true;
}
}
set
{
_canCancel = value;
RaisePropertyChanged("CanCancel");
}
}
private bool CanExecute()
{
return true;
}
private bool CanExecuteCancel()
{
if (DisplayText == "")
{
return false;
}
else
{
return true;
}
}
private void HandleClearButtonClick()
{
DisplayText = "";
}
private void HandleSetText()
{
DisplayText = "Hello";
}
}
The problem : When the page is loaded, the "Clear text" button is disabled which is expected and works fine as intended.
When i click on "Set Text", i set a text to a textblock by setting a text value to property named DisplayText and also call RaisePropertyChanged("CanCancel"); but even after that my "Clear Text" button is not enabled. What can be the reason behind it ? My textblock shows the text value but the "clear text" button is still not enabled.
There's a bit mixing up going on in your example, as far as I can tell: You basically don't use the built-in 'CanExecute' mechanism of 'RelayCommand', but rebuild it yourself while still defining the CanExecute method of the 'RealyCommand'. The idea of 'CanExecute' is to automatically disbale controls whose command can't execute, so you don't need to do it manually. Returning 'true' in an 'CanExecute' method doesn't really make sense, as you don't necessarily need to have a CanExecute delegate in your RelayCommand (... = new RelayCommand(ExecuteCommand); is fine). Your scenario doesn't work because you're not calling 'RaisCanExecuteChanged()' on 'CancelCommand'.
Try the following implementation, I've removed the redundancies and inserted the missing 'RaiseCanExecuteChanged()'. See the comments for explanations:
<StackPanel>
<TextBlock Text="{Binding DisplayText, Mode=TwoWay}"></TextBlock>
<Button Content="Set Text" Command="{Binding SetTextCommand}"></Button>
<Button Content="Clear Text" Command="{Binding CancelCommand}" />
</StackPanel>
And use this simplified ViewModel:
public class Page1VM : ViewModelBase
{
public RelayCommand SetTextCommand { get; private set; }
public RelayCommand CancelCommand { get; private set; }
public Page1VM()
{
SetTextCommand = new RelayCommand(ExecuteSetText);
CancelCommand = new RelayCommand(ExecuteCancel, CanExecuteCancel);
}
private string _displayText="";
public string DisplayText
{
get { return _displayText; }
set
{
_displayText = value;
RaisePropertyChanged("DisplayText");
RaisePropertyChanged("CanCancel");
// Raise the CanExecuteChanged event of CancelCommand
// This makes the UI reevaluate the CanExecuteCancel
// Set a breakpoint in CanExecuteCancel method to make
// sure it is hit when changing the text
CancelCommand.RaiseCanExecuteChanged();
}
}
private bool CanExecuteCancel()
{
// You can simplify the statement like this:
return DisplayText != "";
}
private void ExecuteCancel()
{
DisplayText = "";
}
private void ExecuteSetText()
{
DisplayText = "Hello";
}
}

Categories