Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top