当项目源更改时,WPF ComboBox重置所选项目

时间:2010-02-09 10:48:07

标签: wpf combobox selecteditem

考虑以下XAML:

<ComboBox Name="CompanyComboBox" 
    HorizontalAlignment="Stretch"
    ItemsSource="{Binding Path=GlobalData.Companies}" 
    SelectedValuePath="Id"
    SelectedValue="{Binding Customer.CompanyId, ValidatesOnDataErrors=True}"
    DisplayMemberPath="Name" />

GlobalData.Companies是公司的集合(IEnumerable<Company>);此集合可以在后台重新加载(从Web服务下载)。发生这种情况时,ComboBox会通过绑定正确地重新加载项目。但是作为副作用,它还会重置所选项目!

我使用Reflector来检查组合框源,显然这是预期的行为。

有什么“好”的方式来解决这个问题吗?我想要实现的是,如果用户选择“公司A”并在之后重新加载公司列表,则“公司A”保持选中状态(假设它在新列表中)。

4 个答案:

答案 0 :(得分:4)

也许您可以使用ObservableCollection<Company>而不是IEnumerable<Company>?然后,在后台更改时,您只会添加/删除新列表中新/不存在的项目,所选项目应保留,除非更改已删除它。

你可以update your observable collection in a separate thread with a small hack-around

答案 1 :(得分:3)

请尝试使用以下代码。 将以下属性启用到组合框

IsSynchronizedWithCurrentItem="True"

答案 2 :(得分:0)

嗯,我不知道它是否是一种“好”的方式,但是如果你可以在重新加载之前访问所选项目,你可以保存它(或它的键或其他东西),然后再以编程方式再次选择它。重新加载完成。

快速模型:

var selectedItem = myCombo.SelectedItem;
DoReload();
myCombo.SelectedItem = selectedItem;

但我认为你的意思不同于本手册的另一种方法? 希望无论如何都有帮助...

<强>更新
好的,我从背景线程看到 您是否也使用ICollectionView来绑定组合框?如果是这样,您可以使用CurrentItem属性来保留引用。我做了一个快速的模型,这正在我的设置。假设您有对UI的引用:

XAML

<Grid VerticalAlignment="Top">  
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <ComboBox ItemsSource="{Binding Items}" IsSynchronizedWithCurrentItem="True" Grid.Column="0" Grid.Row="0" DisplayMemberPath="Name"/>
    <Button Command="{Binding UpdateCommand}" Grid.Column="1" Grid.Row="0">Update</Button>
</Grid>

查看/ ViewModel

public partial class Window1 : Window {
   public Window1() {
        InitializeComponent();
        this.DataContext = new ViewModel(this);
   }
}

public class ViewModel
{
    private readonly Window1 window;
    private ObservableCollection<Item> items;
    private ICollectionView view;

    public ViewModel(Window1 window) {
        this.window = window;
        items = new ObservableCollection<Item>
            {
                new Item("qwerty"),
                new Item("hello"),
                new Item("world"),
            };

        view = CollectionViewSource.GetDefaultView(items);
    }

    public ObservableCollection<Item> Items { get { return items; } }

    public ICommand UpdateCommand {
        get { return new RelayCommand(DoUpdate); }
    }

    public Item SelectedItem { get; set; }

    private void DoUpdate(object obj) {
        var act = new Func<List<Item>>(DoUpdateAsync);
        act.BeginInvoke(CallBack, act);
    }

    private List<Item> DoUpdateAsync() {
        return new List<Item> {
                new Item("hello"),
                new Item("world"),
                new Item("qwerty"),
            };
    }

    private void CallBack(IAsyncResult result) {
        try {
            var act = (Func<List<Item>>)result.AsyncState;
            var list = act.EndInvoke(result);

            window.Dispatcher.Invoke(new Action<List<Item>>(delegate(List<Item> lst) {
                                                                    var current = lst.Single(i => i.Name == ((Item)view.CurrentItem).Name);
                                                                    Items.Clear();
                                                                    lst.ForEach(Items.Add);
                                                                    view.MoveCurrentTo(current);
                                                                }), list);

        } catch(Exception exc){ Debug.WriteLine(exc); }
    }
}

public class Item {
    public Item(string name) {
        Name = name; 
    }
    public string Name { get; set; }
}

如果所选项目不在列表中,您将需要进行一些处理 IsSynchronizedWithCurrentItem 属性在这里很重要,否则它将无效!
此外,对主窗口的引用的方式应该是DI框架。

答案 3 :(得分:0)

正如Yacoder所指出,这与对象平等有关。只要绑定SelectedValue而不是SelectedItem,就可以将ItemsSource定义为匿名类型集合。然后不会发生此问题(如果您需要从数据库中读取值,它也会更快)。