When Selector.IsSynchronizedWithCurrentItem will be true?

|

This looks like an obvious question, you when set it to true either through XAML or code, then the Selector's (such as ListBox and ComboBox) selection operation will drive the setting of the current item within CollectionView which presents the data bound source collection, so when you want to synchronize current item with the selected item, you could write something like the following XAML snippet:

<
Pagexmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:sys="clr-namespace:System;assembly=mscorlib">
  <
Page.Resources>
    <
x:Arrayx:Key="data" Type="{x:Type sys:String}">
      <
sys:String>Paviel Dedved</sys:String>
      <
sys:String>Andriy Shevchenko</sys:String>
      <
sys:String>Paolo Maldini</sys:String>
      <
sys:String>Robert Baggio</sys:String>
      <
sys:String>Alessandro Del Piero</sys:String>
    </
x:Array>
  </
Page.Resources>
  <
StackPanel DataContext="{StaticResource data}">
    <
ListBox ItemsSource="{Binding}" Margin="5" IsSynchronizedWithCurrentItem="True"/>
    <
ComboBox ItemsSource="{Binding}" Margin="5" IsSynchronizedWithCurrentItem="True"/>
    <
TextBlock>CurrentItem: <TextBlock Text="{Binding /}"/>
    </
TextBlock>
  </
StackPanel>
</
Page>

From the above code, the ListBox and ComboBox is data bound to the same source collection, but for each ItemsSource binding, a unique CollectionView is generated, so that both ListBox and ComboBox can make independent selection without affecting each other, this is what most developers expect. So if you want both ListBox and ComboBox to drive the currency, you need to explicitly set the IsSynchronizedWithCurrentItem property to true, this is pretty obvious and straightforward, isn't it?

If you think this is an obvious thing to do, then I have another XAMLPad ready example which might confuse you:

<
Pagexmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:sys="clr-namespace:System;assembly=mscorlib">
  <
Page.Resources>
    <
x:Arrayx:Key="data" Type="{x:Type sys:String}">
      <
sys:String>Paviel Dedved</sys:String>
      <
sys:String>Andriy Shevchenko</sys:String>
      <
sys:String>Paolo Maldini</sys:String>
      <
sys:String>Robert Baggio</sys:String>
      <
sys:String>Alessandro Del Piero</sys:String>
    </
x:Array>
    <
CollectionViewSource x:Key="src" Source="{StaticResource data}"/>
  </
Page.Resources>
  <
StackPanel DataContext="{StaticResource src}">
    <
ListBox ItemsSource="{Binding}" Margin="5"/>
    <
ComboBox ItemsSource="{Binding}" Margin="5"/>
    <
TextBlock>CurrentItem: <TextBlock Text="{Binding /}"/>
    </
TextBlock>
  </
StackPanel>
</
Page>

If you run above XAML snippet in XAMLPad, you will find that both ListBox and ComBox can drive the currency as if the IsSynchronizedWithCurrentItem property is set to true. so what happens?

If you use reflector to example the source code of the Selector, if you will the SetSynchronizationWithCurrentItem() method implementation as follows:

private void SetSynchronizationWithCurrentItem()
{
    bool? isSynchronizedWithCurrentItem = IsSynchronizedWithCurrentItem;
    bool oldSync = IsSynchronizedWithCurrentItemPrivate;
    bool newSync;

    if (isSynchronizedWithCurrentItem.HasValue)
    {
        // if there's a value, use it
       
newSync = isSynchronizedWithCurrentItem.Value;
    }
    else
   
{
        // when the value is null, synchronize if selection mode is Single
        // and there's a non-default view.
       
SelectionMode mode = (SelectionMode)GetValue(ListBox.SelectionModeProperty);
        newSync = (mode == SelectionMode.Single) &&
        !CollectionViewSource.IsDefaultView(Items.CollectionView);
    }

    IsSynchronizedWithCurrentItemPrivate = newSync;

    if (!oldSync && newSync)
    {
        SetSelectedToCurrent();
    }
}

From the bolded line of code, it seems that this is an intentional behaviour, which means that if the SelectionMode of Selector is Single, and the CollectionView data bound to the Selector is not the implicit, default View generated by WPF's data binding engine, Selector will drive the currency. This might not make sense to you, but after the following explanation, I hope it finally makes sense to you.

There are two aspects for this issue, the first is multi selection, and the second is non-default view.

First, it's no brainer that when the Selector is in Multiple SelectionMode, WPF can not take on current synchronization, because there is no such concept of "multi-current items" from the perspective of each ICollectionView implementation(CollectionView for IEnumerable, ListCollectionView for IList and BindingListCollectionView for IBindingList etc), you might think that WPF can choose one item from the selected items as the current item, but it's hard to figure out which one is the best candidate for current item, because this decision usually varies tremendously in different scenarios, and it's nearly impossible for a general purpose framework like WPF to make a good decision for you.

Second, WPF drives currency if you are using the non-default explicit CollectionView, this usually is achieved by declaring the underlying data to be CollectionViewSource, because when you use CollectionViewSource which is a XAML friendly version of CollectionView, WPF assumes that you are mostly likely wanting to enable master/detail binding, and it's obvious that you need the currency synchronization service provided by Selector. So WPF thinks that it's a good default and correct behaviour to implicitly turn on currency synchronization for you.

Knowing that this is the heuristics WPF makes on how you are going to use the data, and it might cause some incorrect behaviour as reported in this WPF MSDN forum thread, so it might worth a blog entry to point this out:)

BTW, for any one who thinks that the {Binding /} syntax is alien to them, you could refer to this Ian Griffith's excellent blog post for explanation.

2 comments:

Roller shoes said...

good post,thank you for shareb

net performance said...

I just want you to know that I think you did a terrific job on this web sight.