您的位置:首页 > 产品设计 > UI/UE

WPF standard ComboBox Items Source Change Issue

2015-07-16 13:47 531 查看
Today I encountered an issue with the WPF standard CombBox where if the bound ItemsSource (collection) of a ComboBox has changed the binding on the SelectedItem of the ComboBox will fail to update to the already properly set view model property.

For example if the ItemsSource is bound to the following property of the view model which is common and presumably the best practice:

public ObservableCollection<string> Items { get; private set; }


And the SelectedItem is bound to

public string SelectedItem
{
get { return SelectedModel.SelectedItem; }
set
{
SelectedModel.SelectedItem = value;
OnPropertyChanged();
}
}


And the SelectedModel can be changed this way:

public Model SelectedModel
{
get { return _selectedModel; }
set
{
if (_selectedModel != value)
{
_selectedModel = value;

Items.Clear();
foreach (var item in _selectedModel.Items)
{
Items.Add(item);
}

OnPropertyChanged("SelectedItem");
}
}
}


where each SelectedItem on SelectedModel has already been properly set up.

One might suppose it would work. However it doesn't.

The reason is kind of explained in this article,

http://stackoverflow.com/questions/5242275/combobox-itemssource-changed-selecteditem-is-ruined

As I've done a few experiments, I have feeling that the (initial) binding update is lost due to the fact that the framework modifies the SelectedItem property and holds certain assumptions as to the modification during the collection (Items) change and neglects property changed notification afterwards if things went against its assumption (no matter when it happens, and only not when the property actually changes from its value as of the end of the collection process).

Given the above speculation, I continued playing with the code and eventually come up with a workable solution like below (complete source code).

View model:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using ComboBoxItemsSourceChangeTest.Annotations;

namespace ComboBoxItemsSourceChangeTest
{
/// <summary>
///  The view model of the main UI
/// </summary>
/// <remarks>
///  http://stackoverflow.com/questions/5242275/combobox-itemssource-changed-selecteditem-is-ruined /// </remarks>
public class MainViewModel : INotifyPropertyChanged
{
private Model _selectedModel;

private bool _isComboTransitioning;

public MainViewModel()
{
Items = new ObservableCollection<string>();
}

public Model SelectedModel
{
get { return _selectedModel; }
set
{
if (_selectedModel != value)
{
_selectedModel = value;

_isComboTransitioning = true;
Items.Clear();
foreach (var item in _selectedModel.Items)
{
Items.Add(item);
}
_isComboTransitioning = false;

OnPropertyChanged("SelectedItem");
}
}
}

public ObservableCollection<string> Items { get; private set; }

public string SelectedItem
{
get
{
return _isComboTransitioning ? null: SelectedModel.SelectedItem;
}
set
{
if (SelectedModel.SelectedItem != value && !_isComboTransitioning)
{
SelectedModel.SelectedItem = value;
OnPropertyChanged();
}
}
}

public event PropertyChangedEventHandler PropertyChanged;

[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}

public void RaisePropertyChanged(string propertyName)
{
OnPropertyChanged(propertyName);
}
}
}


Model:

namespace ComboBoxItemsSourceChangeTest
{
public class Model
{
public string[] Items { get; set; }

public string SelectedItem { get; set; }
}
}


View:

<Window x:Class="ComboBoxItemsSourceChangeTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<ComboBox Margin="10" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}"
Name="ComboBox1">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Margin="10" Name="Button1" Click="Button1_Click">Data Source 1</Button>
<Button Margin="10" Name="Button2" Click="Button2_OnClick">Data Source 2</Button>
<Button Margin="10" Name="Button3" Click="Button3_OnClick">Data Source 3</Button>
<Button Margin="10" Name="ButtonRandomSelect" Click="ButtonRandomSelect_OnClick">Random Select</Button>
</StackPanel>
</Grid>
</Window>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: