WPF databinding IsEnabled Property - wpf

So I am learning WPF right now, and want to do a simple databind between a bool value, and whether or not a MenuItem is enabled or not.
I have coded it like this:
<MenuItem Name="miSaveFile" Header="Save" Click="miSaveFile_Click"
IsEnabled="{Binding}" />
And in the .cs file I set:
miSaveFile.DataContext = dataChanged;
For some reason the MenuItem doesn't seem to be properly reflecting the state of dataChanged.
What am I missing?

You are better off binding to an object than to a primitive type. This object is often referred to as the "model" for your view.
WPF uses the INotifyPropertyChanged interface for the model (or often view-model) to notify the view that the model has changed states.
So you will first want to define a data class as the model that implements the INotifyPropertyChanged interface and fires the PropertyChanged event whenever a property is changed.
When you set a binding, you have 5 main elements on the binding to worry about. The binding has a source object, a source path on the source object, a target object, a target property on the target object, and an optional converter.
If you do not specify the source, it defaults to the DataContext of the control the binding is set on. There are other options for setting the source. Here is a Microsoft article on setting the source. You can then set the path of a property to pull out of the source for the binding. In your case, the source is a boolean and there is no path because the binding is using the whole source object.
The target is always the control that you set the binding on, and the target property is the property on this control that you are binding to. In this case, MenuItem and IsEnabled.
A converter can optionally convert the source value into a value that is compatible with the target property. You can use any object for a converter that implements IValueConverter or IMultiValueConverter (for MutliBindings).
In your case, I would first create a model that implements INotifyPropertyChanged. Next, I would assign the DataContext of the menu to an instance of the model. Then I would set the binding to:
IsEnabled="{Binding Path=EnableFlag}"
(Where EnableFlag is a boolean property in the model that you want to menu to bind to)
If you set up the INotifyPropertyChanged interface correctly, the menu item will be enabled/disabled whenever you change this property on the model.

For a MenuItem, would it not be a better approach to use the Command model rather than Click and IsEnabled properties?
After InitialiseComponent():
this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, fileSaveExecuted, fileSaveCanExecute));
Additional methods:
/* here is where you set e.CanExecute true for enabled: */
private void fileSaveCanExecute(object x, CanExecuteRoutedCommandEventArgs e)) { e.CanExecute = ...; e.Handled = true; }
/* here is where you act on the command: */
private void fileSaveExecuted(object sender, ExecutedRoutedEventArgs e) { ... }
XAML:
<MenuItem Header="_Save" Command="Save"/>

How does the UI know when the dataChanged variable has actually changed?
I normally bind to a property on an object, and let that class implement INotifyPropertyChanged. The UI is then "automagically" updated whenever the PropertyChanged event is invoked.
So I would have
<MenuItem Name="miSaveFile" Header="Save" Click="miSaveFile_Click"
IsEnabled="{Binding DataChanged}"</MenuItem>
and then set miSaveFile.DataContext = myObject.DataChanged
(myObject can be this if you are using the codebehind)
Edit: I just made a quick test. If you set the data context directly to the DataChanged property, an subscription to the PropertyChanged event on the owner object is not added. But the solution I suggest works.

Related

How to apply Metadata from a ViewModel to the View in WPF with MVVM

I have a viewModel with properties like the following and a set of specific attributes used throughout the viewmodels.
public class MyViewModel : BaseModel
{
[StringLength(50), Required]
[SetLockedForExistingEntities]
public string FirstName { get ... set ... }
public bool IsInNewMode { get; }
}
Now I want to apply such metaData in a view in a consistent way. Like... If bound, set TextBox maxlength from the MaxLengthAttribute. If SetLockedForExistingEntitiesAttribute is set, disable the control in case viewModel is not in some 'New' Mode etc..
Is that doable/a good idea to do with a custom MarkupExtension that replaces "Binding" for VM Bindings? Or would it be better to use a Behavior (applied via attached property) which tries to apply anything it can from the bound ViewModel property to the control it is attached to?
Usage would be like
(A) Attached dependencyproperty that reads the binding from TextBox.Text and applies behaviors
<TextBox Text="{Binding Model.FirstName, ValidatesOnDataErrors=True}" "bb:MyBindingHelper.ApplyViewModelBehaviors="True" />
(B) Custom MarkupExtension that does all in one
<TextBox Text="{BindingWithModelBasedBehaviors Model.FirstName}" />
You could write a markup extension that gets the property from the datacontext and reads attributes.
That would be kind of complicated but you can get the property name of properties where the source changed event was raised.
That looks rather like validation to me.
You could implement inotifydataerrorinfo in a base viewmodel and write code there that validates properties using attributes.
That's how the code in this works:
https://gallery.technet.microsoft.com/scriptcenter/WPF-Entity-Framework-MVVM-78cdc204
That works by the view telling the viewmodel which property's value just passed to the viewmodel.
You can extend the method you use for raising property changed to pass the property name to the validation.
Or you could even do the check from a method called in the property setter before you set the value on a property and not set the value if the new one fails validation.
As a specific property fails validation in a particular way you could run an action.
The production code version of that app I linked also has a dictionary of predicates used as well as attributes. They could have code in them references and sets other viewmodel properties.

Bind window model value to User Control Dependency Property

I have a simple user control that has One Dependency Property (the control is the model of itself)
The property is not directly bound to anything inside the user control, but I need to Bind its value to the Model of the window (or user control or whatever) where I put my user control.
If I set manually the User control Property Value, the property is modified correctly so I can assume the dependency property in the user control is working.
If I set the value to the Property binding it to my window model like this
<lctrl:InfoIconControl Grid.Row="0" Name="InfoIconTest" IconType="{Binding Path=IconTypeValue}"/>
Where IconTypeValue is a property of the window model, when I set the value of the window model property it does not change inside my user control. I presume I did something wrong but at the moment I have no clue.
Two possibilties come to mind as likely:
Your "model" (you mean viewmodel?) does not implement INotifyPropertyChanged and/or you're not firing the PropertyChanged when IconTypeValue changes its value.
You've done something like this.DataContext = this inside your UserControl and now the Binding is not working because it is looking for the IconTypeValue property inside your control, instead of looking for it in the "model".
Solution to option 1 is easy: implement the interface and make sure you fire the event when the property changes.
Solution to option 2 is simply removing any setting of DataContext inside your UserControl, and instead rely on relative Bindings (RelativeSource, ElementName, etc.) in your control's XAML. Or if you gotta set the DataContext of something, do NOT set the UserControl's one. Instead, set the DataContext of a container INSIDE the UserControl.
In your case, since you're using a viewmodel for your UserControl, using it as DataContext makes sense. But if you wanna support binding to the DependencyProperties of your UserControl, you're then gonna have to set your viewmodel as DataContext of something else... For instance, the first Grid in your XAML.
Just name the Grid:
<Grid x:Name="LayoutRoot">
And set your viewmodel as its DataContext:
InfoIconControlModel mModel;
public InfoIconControl()
{
InitializeComponent();
mModel = new InfoIconControlModel();
LayoutRoot.DataContext = mModel; // this.DataContext = mModel; <-- DON'T DO THIS
}
After that, the Bindings will begin to work. But you've made another typical mistake: you're only calling SetIcon from the CLR setter of your propertty.
public InfoIconType IconType
{
get
{
return (InfoIconType)this.GetValue(IconTypeProperty);
}
set
{
this.SetValue(IconTypeProperty, value);
this.SetIcon(); // <-- This won't work with Binding
}
}
Instead, you must also call it from the DependencyPropertyChanged callback (that you had already defined, on the other hand):
/// <summary>
/// Icon Type dependency Property
/// </summary>
public static readonly DependencyProperty IconTypeProperty = DependencyProperty.Register(
FLD_IconType, typeof(InfoIconType), typeof(InfoIconControl), new PropertyMetadata(InfoIconType.ICPlus, IconTypePropertyChanged));
///<summary>
///
///</summary>
private static void IconTypePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
InfoIconControl ic = sender as InfoIconControl;
ic.SetIcon(); // <-- This will work with Binding
}

Update a WPF label using Binding

On my wpf application, I’m trying to update a label whenever a certain variable changes in my ViewModel. Do you know why the label won’t update?
Here’s what I have at the moment:
xaml
<Label Name="myLabel" Content="{Binding myState}" />
myViewModel.cs
in the constructor:
_MyObject.myEvent += myNewEvent;
and later...
private void myNewEvent(object sender, myArgs e)
{
myState = someStringVariable;
}
My guess is that your ViewModel doesn't implement INotifyPropertyChanged.
If you want the UI to reflect changes of properties in the ViewModel, the Viewmodel must implement INotifyPropertyChanged and raise a PropertyChanged event in the property's setter whenever the value is changed.
For more information:
How to: Implement the INotifyPropertyChanged Interface
Likely, you need to set set the DataContext of the element that contains this label to the instance of your type which I assume _MyObject represents. If you've done that already, it should update whenever you change the property value.

Set UpdateSourceTrigger to Explicit in ShowDialog (WPF MVVM)

I saw this example - Binding.UpdateSourceTrigger Property
in the example the UpdateSourceTrigger set to Explicit and then in the view code he call to UpdateSource of the TextBox name.
But if i use the MVVM dp i dont want to have names to my controls and source properties are in the VM and not in the view so what is the right way to bind controls to VM properties and set the UpdateSourceTrigger to explicit?
I want to do this because in my case its ShowDialog window and I want that the source will update only if the user click "ok"
Thanks in advance!
If you are using MVVM truely then your OK button click must be handled by some Command. This command must be coming from your ViewModel. The Expliticly bound properties must be coming from your ViewModel again. So whats stopping you.
Do not use Explicit binding but use OneWay binding.
In you button, bind a command and bind a command parameter to the OneWay bound Dependency property.
In your Command's Execute handler (which must be some method from your ViewModel), change the ViewModel's property with the parameter coming.
Raise the NotifyPropertyChanged for that property from your ViewModel.
E.g.
Assume I need to update a TextBox's Text back into my model on OK button click.
So for that I have a EmployeeViewModel class that has EmployeeName property in it. The property is has a getter and a setter. The setter raises property changed notification. The view model also has another property of type ICommand named SaveNameCommand that return a command for me to execute.
EmployeeViewModel is the data context type of my view. Myview has a TextBox (named as x:Name="EmployeeNameTxBx") OneWay bound to the EmployeeName and a Button as OK. I bind Button.Command property to EmployeeViewModel.SaveNameCommand property and Button.CommandParameter is bound to EmployeeNameTxBx.Text property.
<StackPanel>
<TextBox x:Name="EmployeeNameTxBx"
Text="{Binding EmployeeName, Mode=OneWay}" />
<Button Content="OK"
Command="{Binding SaveNameCommand}"
CommandParameter="{Bidning Text, ElementName=EmployeeNameTxBx}" />
</StackPanel>
Inside my EmployeeViewModel I have OnSaveNameCommandExecute(object param) method to execute my SaveNameCommand.
In this perform this code...
var text = (string)param;
this.EmployeeName = text;
This way ONLY OK button click, updates the TextBox's text back into EmployeeName property of the model.
EDIT
Looking at your comments below, I see that you are trying to implement Validation on a UI. Now this changes things a little bit.
IDataErrorInfo and related validation works ONLY IF your input controls (such as TextBoxes) are TwoWay bound. Yes thats how it is intended. So now you may ask "Does this mean the whole concept of NOT ALLOWING invalid data to pass to model is futile in MVVM if we use IDataErrorInfo"?
Not actually!
See MVVM does not enforce a rule that ONLY valid data should come back. It accept invalid data and that is how IDataErrorInfo works and raises error notfications. The point is ViewModel is a mere softcopy of your View so it can be dirty. What it should make sure is that this dirtiness is not committed to your external interfaces such as services or data base.
Such invalid data flow should be restricted by the ViewModel by testing the invalid data. And that data will come if we have TwoWay binding enabled. So considering that you are implementing IDataErrorInfo then you need to have TwoWay bindings which is perfectly allowed in MVVM.
Approach 1:
What if I wan to explicitly validate certain items on the UI on button click?
For this use a delayed validation trick. In your ViewModel have a flag called isValidating. Set it false by default.
In your IDataErrorInfo.this property skip the validation by checking isValidating flag...
string IDataErrorInfo.this[string columnName]
{
get
{
if (!isValidating) return string.Empty;
string result = string.Empty;
bool value = false;
if (columnName == "EmployeeName")
{
if (string.IsNullOrEmpty(AccountType))
{
result = "EmployeeName cannot be empty!";
value = true;
}
}
return result;
}
}
Then in your OK command executed handler, check employee name and then raise property change notification events for the same property ...
private void OnSaveNameCommandExecute(object param)
{
isValidating = true;
this.NotifyPropertyChanged("EmployeeName");
isValidating = false;
}
This triggers the validation ONLY when you click OK. Remember that EmployeeName will HAVE to contain invalid data for the validation to work.
Approach 2:
What if I want to explicitly update bindings without TwoWay mode in MVVM?
Then you will have to use Attached Behavior. The behavior will attach to the OK button and will accept list of all items that need their bindings refreshed.
<Button Content="OK">
<local:SpecialBindingBehavior.DependentControls>
<MultiBinding Converter="{StaticResource ListMaker}">
<Binding ElementName="EmployeeNameTxBx" />
<Binding ElementName="EmployeeSalaryTxBx" />
....
<MultiBinding>
</local:SpecialBindingBehavior.DependentControls>
</Button>
The ListMaker is a IMultiValueConverter that simply converts values into a list...
Convert(object[] values, ...)
{
return values.ToList();
}
In your SpecialBindingBehavior have a DependentControls property changed handler...
private static void OnDependentControlsChanged(
DependencyObject depObj,
DependencyPropertyChangedEventArgs e)
{
var button = sender as Button;
if (button != null && e.NewValue is IList)
{
button.Click
+= new RoutedEventHandler(
(object s, RoutedEventArgs args) =>
{
foreach(var element in (IList)e.NewValue)
{
var bndExp
= ((TextBox)element).GetBindingExpression(
((TextBox)element).Textproperty);
bndExp.UpdateSource();
}
});
}
}
But I will still suggest you use my previous pure MVVM based **Approach 1.
This is an old question but still I want to provide an alternative approach for other users who stumble upon this question...
In my viewmodels, I do not expose the model properties directly in the get/set Property methods. I use internal variables for all the properties. Then I bind all the properties two-way. So I can do all the validation as "usual" because only the internal variables are changed. In the view model constructor, I have the model object as parameter and I set the internal variables to the values of my model. Now when I click on the "Save" Button (-> Save Command fires in my view model fires) and there are no errors, I set all the properties of my model to the values of the correspondng internal variable. If I click on the "Canel/Undo"-Button (-> Cancel-Command in my view model fires), I set the internal variables to the values of my untouched model (using the setters of the view model properties so that NotifyPropertyChanged is called and the view shows the changes=old values).
Yet another approach would be to implement Memento-Support in the model, so before you start editing you call a function in the model to save the current values, and if you cancel editing you call a function to restore those values...that way you would have the undo/cancel support everywhere an not just in one view model...
I've implemented both methods in different projects and both work fine, it depends on the requirements of the project...

Mainaining radio selection after item source change

Setup:
I have a combo-box, it's itemsource bound to an ObservableCollection<T> of a custom class, one property is a List<myenum>.
I have an itemscontrol which is databound to the combo-box's selected item List<myenum> property.
The itemscontrol datatemplate creates a list of radiobuttons, each representing the individual enum values in the list.
The Desire:
When I change the value in the combo-box the itemscontrol source is updated. What I want to occur, is if a radio button in the new itemscontrol source is the same as the selected radiobuton in the previous list (before it was updated), this to be checked.
Current Idea:
Asign a Checked event to the radio buttons, which maintains a myenum property in the window class which can be compared against. Make the IsChecked property of the radiobox bind to a converter and compare against the myenum property. To achieve this, I have made the window class extend from IValueConverter, this way the converter function has access to the myenum property.
Issue:
I don't know how to get the IsChecked binding to use the window as the converter. I have tried using relative source in the converter part of the binding, but that doesn't work
IsChecked="{Binding Converter={RelativeSource={RelativeSource Self}}}"
Preferred Answers:
Assistance on correcting the binding syntax if it's possible this way.
Ideas of a more appropriate way of achieving what I'd like.
I also do not know how to use the window as a value converter in xaml. Instead create a standalone value converter class with a public property for the enum type. Next, in the constructor of the window, get a reference to the instance of the value converter and store it in a private member.
XAML:
<local:MyValueConverter x:Key="ConvertSomething" />
Code behind:
private MyValueConverter _myValueConverter;
public Window1()
{
InitializeComponent();
_myValueConverter = FindResource("ConvertSomething") as MyValueConverter;
}
private void RadioButton_Checked(object sender, RoutedEventArgs e)
{
// You can access _myValueConverter here and set its public enum property.
}

Resources