ComboBox SelectedValue Binding Breaking for Child Details

Topics: UI Architecture
Sep 27, 2011 at 12:26 PM

I'm using Silverlight 4 and the latest version of Caliburn and I'm seeing a situation where databinding for the SelectedValue of a ComboBox initially works but then breaks after some user interaction.

My data has a parent/child relationship which is presented in the form of two ListBox controls, the first one lists all of the possible parents, and as the selected parent changes the list of children is repopulated with the children associated with the selected parent.  Below the ListBoxes details of the selected child are displayed.  One of the details is a type value that can be one of three possible values.  I want the SelectedItem in the ComboBox to correspond with the Type property on the selected child, and this works great when the form initially loads, as the selected child changes the SelectedItem in the ComboBox changes along with it, however, as soon as the selected parent changes and the list of children is regenerated that binding breaks, but bindings to TextBlocks and other non-list based controls continue to work correctly.

I'm not sure what else I could do to make sure that the binding sticks. 

I've put together a small sample demonstrating my issue. 

Here's the View:

<UserControl
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             xmlns:local="clr-namespace:ComboBoxDemo" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             mc:Ignorable="d" 
             x:Class="ComboBoxDemo.ShellView"
             >

	<UserControl.Resources>
		<local:ShellViewModel x:Key="ShellViewModelDataSource" d:IsDataSource="True"/>
    </UserControl.Resources>

	<Grid Background="White" DataContext="{Binding Source={StaticResource ShellViewModelDataSource}}">
		<StackPanel>
			<TextBlock Text="Hello Caliburn Micro!"
				VerticalAlignment="Center"
				HorizontalAlignment="Center"
				FontSize="20" />
			<StackPanel Orientation="Horizontal">
				<StackPanel Orientation="Vertical" Margin="5,0">
					<TextBlock Text="Parents" />
					<ListBox x:Name="ParentList"
						ItemsSource="{Binding ParentList, Mode=TwoWay}"
                        SelectedItem="{Binding SelectedParent, Mode=TwoWay}" />
				</StackPanel>
				<StackPanel Orientation="Vertical"
					        Margin="5,0,0,5">
					<TextBlock Text="Children" />
					<ListBox x:Name="ChildList"
						ItemsSource="{Binding ChildList}"
                             SelectedItem="{Binding SelectedChild, Mode=TwoWay}" />
				</StackPanel>
			</StackPanel>
			<StackPanel Orientation="Horizontal">
				<TextBlock Text="Child Name:"/>
				<TextBlock Text="{Binding SelectedChild.Name}" />
                <TextBlock Text="The Child's Type value is: " Margin="10,0"/>
                <TextBlock Text="{Binding SelectedChild.Type}" />
			</StackPanel>
			<StackPanel Orientation="Horizontal">
				<TextBlock Text="Type: " />
				<ComboBox x:Name="Types"
					      ItemsSource="{Binding Types}"
					      DisplayMemberPath="Key"
					      SelectedValuePath="Value"
					      SelectedValue="{Binding SelectedChild.Type, Mode=TwoWay}" 
                          Width="70" />
                
                <!--<StackPanel>
                    <ListBox ItemsSource="{Binding Types}"
                             DisplayMemberPath="Key"
                             SelectedValuePath="Value"
                             SelectedValue="{Binding SelectedChild.Type, Mode=TwoWay}">
                        
                    </ListBox>
                </StackPanel>-->
			</StackPanel>
		</StackPanel>
	</Grid>

</UserControl>
And the View Model
namespace ComboBoxDemo {
    using System.ComponentModel.Composition;
    using Caliburn.Micro;
    using System.Collections.Generic;
    using System.Linq;

    [Export(typeof(IShell))]
    public class ShellViewModel : PropertyChangedBase 
    {
        private BindableCollection<Parent> _parentList;
        private Parent _selectedParent;
        private BindableCollection<Child> _childList;
        private Child _selectedChild;
        private Dictionary<string, int> _types;

        public BindableCollection<Parent> ParentList
        {
            get { return _parentList; }
            set
            {
                _parentList = value;
                NotifyOfPropertyChange(() => ParentList);
            }
        }

        public Parent SelectedParent
        {
            get { return _selectedParent; }
            set
            {
                _selectedParent = value;
                NotifyOfPropertyChange(() => SelectedParent);

                if (_selectedParent != null)
                {
                    CreateChildren(SelectedParent.Id);
                    NotifyOfPropertyChange(() => ChildList);
                }                
            }
        }

        public BindableCollection<Child> ChildList
        {
            get { return _childList; }
            set
            {
                _childList = value;
                NotifyOfPropertyChange(() => ChildList);
            }
        }

        public Child SelectedChild
        {
            get { return _selectedChild; }
            set
            {
                _selectedChild = value;
                NotifyOfPropertyChange(() => SelectedChild);
            }
        }
        
        public Dictionary<string, int> Types
        {
            get { return _types; }
        }

        public void CreateParents()
        {
            ParentList = new BindableCollection<Parent>();
            ParentList.Add(new Parent("Parent1", 1));
            ParentList.Add(new Parent("Parent2", 2));
            ParentList.Add(new Parent("Parent3", 3));
            SelectedParent = ParentList.FirstOrDefault();
        }

        public void CreateChildren(int parentId)
        {
            ChildList = new BindableCollection<Child>();
            ChildList.Add(new Child("Child1 of Parent " + parentId, 1, 1));
            ChildList.Add(new Child("Child2 of Parent " + parentId, 2, 2));
            ChildList.Add(new Child("Child3 of Parent " + parentId, 3, 3));
            ChildList.Add(new Child("Child4 of Parent " + parentId, 4, 1));
            SelectedChild = ChildList.FirstOrDefault();
        }

        public void CreateTypes()
        {
            _types = new Dictionary<string, int>();
            _types.Add("Type1", 1);
            _types.Add("Type2", 2);
            _types.Add("Type3", 3);
        }

        public ShellViewModel()
        {
            CreateTypes();
            CreateParents();
        }
    }

    public class Parent
    {
        private string _name;
        private int _id;

        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
            }
        }

        public int Id
        {
            get { return _id; }
            set
            {
                _id = value;
            }
        }

        public Parent(string name, int id)
        {
            _name = name;
            _id = id;
        }

        public override string ToString()
        {
            return Name;
        }
    }

    public class Child
    {
        private string _name;
        private int _id;
        private int _type;

        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
            }
        }

        public int Id
        {
            get { return _id; }
            set
            {
                _id = value;
            }
        }

        public int Type
        {
            get { return _type; }
            set
            {
                _type = value;
            }
        }

        public Child(string name, int id, int typeId)
        {
            _name = name;
            _id = id;
            _type = typeId;
        }

        public override string ToString()
        {
            return Name;
        }
    }
}
Sep 27, 2011 at 3:25 PM

I had the same problem. Maybe using SelectedItem instead of SelectedValue will solve the problem.

Take a look here:

http://caliburnmicro.codeplex.com/discussions/255161

Sep 28, 2011 at 11:49 AM

Thanks Tongo, that looks like it will get me where I need to be.