2
votes

I have this ListBox which is bound to an ObservableCollection. Each object in the list implements an interface called ISelectable

public interface ISelectable : INotifyPropertyChanged
{
    event EventHandler IsSelected;
    bool Selected { get; set; }
    string DisplayText { get; }
}

I want to keep track of which object is selected regardless of how it is selected. The user could click on the representation of the object in the ListBox but it could also be that an object is selected via code. If the user selects an object via the ListBox I cast the the selected item to an ISelectable and set the Selected property to true.

ISelectable selectable = (ISelectable)e.AddedItems[0];
selectable.Selected = true;

My problem is that when I select the object using code I can't get ListBox to change the selected item. I'm using a DataTemplate to show the selected object in a different color which means everything is displayed correctly. But the ListBox has the last object the user clicked as the SelectedItem which means that item can't be clicked without first selecting another object in the list.

Anyone got any idea on how to solve this? I pretty sure I can accomplish what I want by writing some custom code to handle the Mouse and Keyboard events but I rather not. I have tried adding a SelectedItem property to the collection and bind it to the ListBox's SelectItemProperty but no luck.

3

3 Answers

4
votes

You could also accomplish this by data binding ListBoxItem.IsSelected to your Selected property. The idea is to set the binding for each of the ListBoxItems as they are created. This can be done using a style that targets each of the ListBoxItems generated for the ListBox.

This way when an item in the ListBox is selected/unselected, the corresponding Selected property will be updated. Likewise setting the Selected property in code will be reflected in the ListBox

For this to work the Selected property must raise the PropertyChanged event.

<List.Resources>
    <Style TargetType="ListBoxItem">
        <Setter 
            Property="IsSelected" 
            Value="{Binding 
                        Path=DataContext.Selected, 
                        RelativeSource={RelativeSource Self}}" 
            />
    </Style>
</List.Resources>
1
votes

Have you looked at the list box's SelectedItemChanged and SelectedIndexChanged events?

These should be triggered whenever a selection is changed, no matter how it is selected.

0
votes

I think you should fire the propertyChanged event when the select has changed. Add this code to the object that implements ISelectable. You'll end up with something like:

private bool _Selected;
        public bool Selected
        {
            get
            {
                return _Selected;
            }
            set
            {
                if (PropertyChanged != null)                
                    PropertyChanged(this, new PropertyChangedEventArgs("Selected"));

                _Selected = value;
            }
        }

I've tried the folowing code:

public ObservableCollection<testClass> tests = new ObservableCollection<testClass>();

        public Window1()
        {
            InitializeComponent();
            tests.Add(new testClass("Row 1"));
            tests.Add(new testClass("Row 2"));
            tests.Add(new testClass("Row 3"));
            tests.Add(new testClass("Row 4"));
            tests.Add(new testClass("Row 5"));
            tests.Add(new testClass("Row 6"));
            TheList.ItemsSource = tests;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            tests[3].Selected = true;
            TheList.SelectedItem = tests[3];
        }

where testClass implements ISelectable.

The is a piece of xaml, nothing fancy:

<ListBox Grid.Row="0" x:Name="TheList"></ListBox>        
<Button Grid.Row="1" Click="Button_Click">Select 4th</Button>

I hope this helps.