C# WPF Databinding

Databinding in General

If it doesn't work check:

  • You can just bind to a property. Check that you are not binding on a field

Databinding for ContextMenus in General

ContextMenus in WPF are somehow special. They exist outside the visual tree of the parent control and therefore they are unable to find for example the root element by name. In the following example the binding doesn't work:

<Window name="Window" x:Class="Example" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <TextBlock name="Text">
        <TextBlock.ContextMenu>
            <Menu Name="ContextMenu">
                <MenuItem Command="Commands:ExampleCommands.ShowMessageBox" CommandParameter="{Binding ElementName=root, Path=MyObject.MyProperty}" Name="Test" /;>
            </Menu>
       </TextBlock.ContextMenu>
    </TextBlock>
</Window>

But after you use the following code in the Initialization in the Code behind file it does work:


public Example() {
    InitializeWindow();

    ContextMenu.DataContext = this;
    NameScope.SetNameScope(ContextMenu, NameScope.GetNameScope(this));
}

Databinding Elements in a ComboBox

Define the class you want to show in the ComboBox:

using System;
using System.Collections.Generic;

namespace Model {
    public class Object {
        public string Name {
            get;
            set;
        }
    }
}

Create a collection of the objects that you want to present in the ComboBox

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace MyNamespace {
    public class MainWindowModel {
        public ObservableCollection<Object> ObjectList {
            get;
            set;
        }

        public MainWindowModel() {
            ObjectList = new ObservableCollection<Object>
                                         {
                                             new Object("Table"),
                                             new Object("Chair")
                                             })
                                         };
        }

    }
}

Define the view where we want to show the the ComboBox

<Window x:Class="MyNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" 
        Height="436" 
        Width="616" 
        Name="root">

            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>

                <TextBlock Grid.Row="0" Grid.Column="0">First class</TextBlock>
                <ComboBox Grid.Row="0" Grid.Column="1" ItemsSource="{Binding ObjectList}" DisplayMemberPath="Name" />
            </Grid>
</Window>

Set the correct DataContext in the views code behind:

using System.Windows;
using Model;

namespace MyNamespace {
    public partial class MainWindow : Window {
        public MainWindow() {
            DataContext = new MainWindowModel();

            InitializeComponent();
        }
    }
}

Commands: Reevaluate CanExecute

If you change something so that a command would be enabled it is possible that WPF doesn't reevaluate the state. It is possible however to force the system to reevalute:

CommandManager.InvalidateRequerySuggested();

Binding RadioButtons to an Enumeration

Source: See MVVM - Binding Multiple Radio Buttons To a single Enum Property in WPF or WPF radio buttons and enumeration values

Remember the following pitfall: If you enum is inside another class you need to use the + operator like

<Button CommandParameter="{x:Static local:MyOuterType+MyEnumName.MyEnumValue}".../>

Two-way binding pitfall

If using two-way binding you need to specify a path or xpath. Therefore

{Binding}

will not work but

{Binding Path=.}

will.

NotifyCollecitonChangedAction pitfall

When listening for the NotifyCollectionChanged action of a collection one might think to catch every (regular) case when implementing the Add and Remove action. But the action Reset happens also quite frequently for example when calling Clear on a list

Call a method everytime a DependencyProperty changes

You can always listen to the general handler but sometimes the code gets more readable if a dedicated method is called for some property changes. This can be done with the following code:

        private void RegisterForNotification(string propertyName, object source, PropertyChangedCallback callback) {
            Binding b = new Binding(propertyName) {
                Source = source
            };

            DependencyProperty prop = DependencyProperty.RegisterAttached(Guid.NewGuid() + propertyName, typeof(object),
                                                                          GetType(), new PropertyMetadata(callback));
            BindingOperations.SetBinding(this, prop, b);
        }

Here is an example how to call it:

RegisterForNotification("Text", txtUserName, UserNameChanged);

Read-only Dependency property

It is not enough to make the setter of a dependency property private and implementing it like regular read-only properties. The problem is that the value could be overwritten for example by animations. Here is the correct implementation:

        private static readonly DependencyPropertyKey SelectedItemsCountPropertyKey =
        DependencyProperty.RegisterReadOnly("SelectedItemsCount", typeof(int), typeof(TheControlImplementingTheProperty), new FrameworkPropertyMetadata(0));

        public static readonly DependencyProperty SelectedItemsCountProperty = SelectedItemsCountPropertyKey.DependencyProperty;

        public int SelectedItemsCount {
            get {
                return (int)GetValue(SelectedItemsCountProperty);
            }
            private set {
                SetValue(SelectedItemsCountPropertyKey, value);
            }
        }

Add new comment