2
votes

I'm developing a WPF application and I'm struggling a little bit to understand some of the details of DataContext as it applies to binding. My application uses a business object which is defined like this:

public class MyBusinessObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, e);
        }
    }

    // enumerations for some properties
    public enum MyEnumValues
    {
        [Description("New York")]
        NewYork,
        [Description("Chicago")]
        Chicago,
        [Description("Los Angeles")]
        LosAngeles
    }

    // an example property
    private string _myPropertyName;
    public string MyPropertyName
    {
        get { return _myPropertyName; }
        set
        {
            if (_myPropertyName == value)
            {
                return;
            }

            _myPropertyName = value;
            OnPropertyChanged(new PropertyChangedEventArgs("MyPropertyName"));
        }
    }

    // another example property
    private MyEnumValues _myEnumPropertyName;
    public MyEnumValues MyEnumPropertyName
    {
        get { return _myEnumPropertyName; }
        set
        {
            if (_myEnumPropertyName== value)
            {
                return;
            }

            _myEnumPropertyName= value;
            OnPropertyChanged(new PropertyChangedEventArgs("MyEnumPropertyName"));
        }
    }

    // example list property of type Widget
    public List<Widget> MyWidgets { get; set; }

    // constructor
    public MyBusinessObject()
    {
        // initialize list of widgets
        MyWidgets = new List<Widget>();

        // add 10 widgets to the list
        for (int i = 1; i <= 10; i++)
        {
           MyWidgets.Add(new Widget());
        }

        // set default settings
        this.MyPropertyName = string.empty;
    }
}

As you can see, I have some properties that are declared in this class one of which is a list of Widgets. The Widget class itself also implements INotifyPropertyChanged and exposes about 30 properties.

My UI has a combobox which is bound to my list of Widgets like this:

MyBusinessObject myBusinessObject = new MyBusinessObject();

public MainWindow()
{
    InitializeComponent();

    this.DataContext = myBusinessObject;

    selectedWidgetComboBox.ItemsSource = myBusinessObject.MyWidgets;
    selectedWidgetComboBox.DisplayMemberPath = "WidgetName";
    selectedWidgetComboBox.SelectedValuePath = "WidgetName";
}

The majority of the controls on my UI are used to display the properties of a Widget. When my user selects a Widget from the combobox, I want these controls to display the properties for the selected Widget. I'm currently achieving this behavior by updating my window's DataContext in the SelectionChanged event handler of my combobox like this:

private void selectedWidgetComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    this.DataContext = selectedWidgetComboBox.SelectedItem;
}

This allows me to bind my controls to the appropriate Widget property like this:

<TextBox Text="{Binding WidgetColor}"></TextBox>

However, not all of the controls in my UI are used to display Widget properties. Some of the controls need to display the properties from MyBusinessObject (for example: MyPropertyName defined above). In this scenario, I can't simply say:

<TextBox Text="{Binding MyPropertyName}"></TextBox>

...because the DataContext of the window is pointing to the selected Widget instead of MyBusinessObject. Can anyone tell me how I set the DataContext for a specific control (in XAML) to reference the fact that MyPropertyName is a property of MyBusinessObject? Thank you!

2
Did you consider adding an object property to the Widget class? And manipulating it when the selection is changed?Gayot Fow

2 Answers

3
votes

Instead of changing the DataContext of your window, you should add a property to your MyBusinessObject class like this one:

private Widget _selectedWidget;
public Widget SelectedWidget
{
    get { return _selectedWidget; }
    set
    {
        if (_selectedWidget == value)
        {
            return;
        }

        _selectedWidget = value;
        OnPropertyChanged(new PropertyChangedEventArgs("SelectedWidget"));
    }
}

Then bind SelectedWidget to the SelectedItem property of your combobox. Anywhere that you need to use the widget's properties you can do this:

<TextBox Text="{Binding Path=SelectedWidget.WidgetColor}"></TextBox>
2
votes

try

<TextBox Text="{Binding MyBusinessObject.MyPropertyName}"></TextBox>

this works if MyBusinessObject is the datacontext of the textbox and MyPropertyName is a property of MyBusinessObject

Also, Here is a good article to clarify binding

hope this helps

EDIT 1:

use a relative binding like this:

text="{Binding DataContext.MyPropertyName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TypeOfControl}}}"

So the relatve binding allows you to look up the visual tree to another UI element and use its datacontext. I would consider wrapping your window's contents in a grid. and wet your windows datacontext to the businessobject and the grids datacontext to the widget. That way you can always use the parent window's datacontext through the realtive source binding.

so use the following if your window's datacontext is your business object

text="{Binding DataContext.MyPropertyName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"