H Ravenscroft
H Ravenscroft

Reputation: 11

How to preserve two way binded value when changing both datacontext and items source of WPF ComboBox

Background

The application has a list view binded to a list of "Jobs", of which the properties are editable by some comboboxes (with two way binding). The data context of the comboboxes is changed to the currently selected job via the SelectionChanged event of the list view.

The combobox value is bound to a property of Job. The itemssource of the job is changed to completely different list upon DataContextChanged.

Layout of widgets

Issue

The binded property linked to the combobox of Job is set to null when changing the DataContext.

By clicking through the list of Jobs, all the properties bound to any combobox are set to null.

Assumed Problem

I may be incorrect in this assumtion...

As the datacontext is switched, either the old selected Job or the new selected Job is set to null as the itemsource does nto contain the value stores in on of the either the new or old selected Job.

Debugging Attempts (Edit)

I've noticed the the value of the property of the Job was set to null before the SelectedItem of the list of jobs was changed.

Question

How can the value of the binded property of Job be preserved when switching the datacontext of the ComboBox to an itemsource that does not contain SelectedValue?

Relevant Code (Abridged - Binding to other widgets works as expected)

<ComboBox x:Name="ContactField" Grid.Column="1"  Grid.Row="3" Margin="2,1" Grid.ColumnSpan="3" DisplayMemberPath="Value" SelectedValuePath="Id" SelectedValue="{Binding Contact,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" SelectionChanged="ContactField_SelectionChanged"/>

Code Behind - Changing Items Source

private void UpdateCustomerDependancies() 
{

    imprintDataSetTableAdapters.CustomerContactsTableTableAdapter rpcdtta = new imprintDataSetTableAdapters.CustomerContactsTableTableAdapter();
    IList<ComboData> customers = new List<ComboData>();

    if (LeftFieldPanel.DataContext != null) //Where LeftFieldPanel contains all comboboxes
    {
        Job currentJob = (Job)LeftFieldPanel.DataContext;

        foreach (DataRow item in rpcdtta.GetDataBy(currentJob.CustomerCode).Rows)
        {
            customers.Add(new ComboData() { Id = item.ItemArray[0].ToString(), Value = item.ItemArray[1].ToString() });
        }
        ContactField.ItemsSource = customers;

    }
}

Code Behind - Changing Data Context

private void jobTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        if (jobTree.SelectedItem.GetType() == typeof(Job))
        {
            DelGrid.Visibility = Visibility.Hidden;


            Job j = (Job)jobTree.SelectedItem;


            MessageBox.Show(j.Contact);

            LeftFieldPanel.DataContext = j; //Switch datacontext
            RightFieldPanel.DataContext = j; //switchdata context

        }


    }

Upvotes: 0

Views: 258

Answers (1)

H Ravenscroft
H Ravenscroft

Reputation: 11

According to this answer, and my own research, it is not possible to simply bind and hope for this best. Therefore, my workaorund to this solution is the following. I have generalised the code to make it applicable to other circumstances.

1) Upon the change of datacontext, append new items to the list, without removing old items:

private void ListView1_SelectedItemChanged(...) {
    List<ComboData> comboitems = ((IList<ComboData>)ComboBox1.ItemsSource).ToList(); 
    //Add new items to comboitems here
    ComboBox1.ItemsSource = comboitems; //Rebind
}

2) Upon opening the dropdown of the combobox, get the itemsource and remove old items only

private void ComboBox1_DropDownOpened(...) {
    List<ComboData> comboitems = ((IList<ComboData>)ComboBox1.ItemsSource).ToList(); 
    //Remove old items from comboitems here
    ComboBox1.ItemsSource = comboitems; //Rebind
}

Apologies if this is not clear enough, I felt that posting the real code would make it rather difficult to understand out of the context.

This has solved my own issue.

Alternate Solution

See above and add items without remove old ones. Then replace the entire itemssource only on a dropdownopened event.

Upvotes: 1

Related Questions