-1

I have created a custom UserControl. It is basically a combobox that allow a multiple selection (each combobox item is a checkbox). Everything works fine except for the Selected items property

public static readonly DependencyProperty SelectedItemsProperty =
     DependencyProperty.Register("SelectedItems", typeof(ObservableCollection<string>), typeof(MultiSelectionComboBox), new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(MultiSelectionComboBox.OnSelectedItemsChanged)));


public ObservableCollection<string> SelectedItems
{
    get { return (ObservableCollection<string>)GetValue(SelectedItemsProperty); }
    set
        {
            SetValue(SelectedItemsProperty, value);
        }
}


private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    MultiSelectionComboBox control = (MultiSelectionComboBox)d;
    control.SetText();
}

On this side the things seems to work, meaning that the the SelectedItems change and the callback is triggered when i select a new item. The problem rise when i use this custom usercontrol.

This is how i have defined it:

<views:MultiSelectionComboBox SelectedItems="{Binding Path=IpAddressSelection, UpdateSourceTrigger=PropertyChanged}" Background="White" BorderThickness="1" ItemsSource="{Binding Path=Address}" BorderBrush="LightGray" Grid.Row="0" Grid.Column="1" Width="200" Margin="70 10 0 0" DefaultText="Indirizzo IP..." />

This is the binding of the SelectedItems property:

public ObservableCollection<string> IpAddressSelection
{
    get { return ipAddressSelection; }
    set
    {
        SetField(ref ipAddressSelection, value, "IpAddressSelection");
    }
}

private ObservableCollection<string> ipAddressSelection = new ObservableCollection<string>();

SetField is a function that implement the INotifyPropertyChanged interface. My problem is that when i select an item, IpAddressSelection does not see any change (i.e. i can't get inside the "set" of IpAddressSelection). Do you know what i'm doing wrong here?

Daniele Sartori
  • 1,674
  • 22
  • 38
  • 1
    Why would the setter of the IpAddressSelection property be called when you select an item? It is only called when you set or bind the IpAddressSelection property to an ObservableCollection... – mm8 Nov 03 '17 at 14:40
  • Just reading your comment i found my mistake. I needed to set Mode=TwoWay in the binding. I need to IpAddressSelection to store the SelectedItems – Daniele Sartori Nov 03 '17 at 14:43
  • So it works as you would expect now? – mm8 Nov 03 '17 at 14:44
  • yes. I'll leave an answer, instead of deleting the question. Maybe it could be useful to someone – Daniele Sartori Nov 03 '17 at 14:45
  • Is there any reason why the property type is `ObservableCollection`? You don't seem to register any CollectionChanged even handler anywhere. You're also unable to use any other item type than `string`. – Clemens Nov 03 '17 at 14:58
  • @Clemens I can surely change ObservableCollection to IEnumerable to use other type, but at the moment i was more focused to have this control to work in my case. Plus, but correct me if i am wrong, i don't need to implement a collection changed handler if i use an observable collection – Daniele Sartori Nov 03 '17 at 15:17
  • The other way round: you don't need to force the property type to be ObservableCollection (or any other collection that implements INotifyCollectionChanged) if you don't want to get notified about collection changes. Doing so only reduces flexibility without reason. – Clemens Nov 03 '17 at 15:24
  • @Clemens Well i hardly see any context where i don't want a SelectedItems property to not notify its change. Guess however that i can change string to object. However i'll try to make this control more general purpose in the future, now that i solved my problem – Daniele Sartori Nov 03 '17 at 15:31
  • Take a look at the ItemsSource property of an ItemsControl. While you typically bind it to an observable collection, the property itself is just declared as IEnumerable (for greater flexibility). You should do the same for your SelectedItems property. – Clemens Nov 03 '17 at 16:03

2 Answers2

2

Just like e.g. the Selector.SelectedItem property, your SelectedItems property should bind two-way by default.

Set FrameworkPropertyMetadataOptions.BindsTwoWayByDefault during registration:

public static readonly DependencyProperty SelectedItemsProperty =
    DependencyProperty.Register(
        nameof(SelectedItems),
        typeof(IEnumerable),
        typeof(MultiSelectionComboBox),
        new FrameworkPropertyMetadata(
            null,
            FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
            OnSelectedItemsChanged));

public IEnumerable SelectedItems
{
    get { return (IEnumerable)GetValue(SelectedItemsProperty); }
    set { SetValue(SelectedItemsProperty, value); }
}

In case you need to react on changes of the SelectedItems collection, you could check if it implements the INotifyCollectionChanged interface, and attach/detach a handler method. See e.g. this answer.

Clemens
  • 123,504
  • 12
  • 155
  • 268
0

Found my error. I needed to set TwoWay mode inside the binding

<views:MultiSelectionComboBox SelectedItems="{Binding Path=IpAddressSelection, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Background="White" BorderThickness="1" ItemsSource="{Binding Path=Address}" BorderBrush="LightGray" Grid.Row="0" Grid.Column="1" Width="200" Margin="70 10 0 0" DefaultText="Indirizzo IP..." />
Daniele Sartori
  • 1,674
  • 22
  • 38
  • A SelectedItems property should certainly bind two-way by default, as e.g. ListBox.SelectedItem. Set [`FrameworkPropertyMetadataOptions.BindsTwoWayDyDefault`](https://msdn.microsoft.com/en-us/library/system.windows.frameworkpropertymetadataoptions(v=vs.110).aspx) during registration. – Clemens Nov 03 '17 at 14:52
  • @Clemens well that's something i didn't know. If you post an answer where you write how the property should be defined i will mark your as accepted – Daniele Sartori Nov 03 '17 at 14:54
  • Please also note that `UpdateSourceTrigger=PropertyChanged` is the default. You don't need to set it explicitly. – Clemens Nov 03 '17 at 16:13