10

考虑以下 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>);这个集合可以在后台重新加载(它是从网络服务下载的)。发生这种情况时,ComboBox 通过绑定正确地重新加载项目。但是,作为副作用,它还会重置所选项目!

我已经使用反射器来检查组合框源,显然这是预期的行为。

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

4

4 回答 4

11

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

IsSynchronizedWithCurrentItem="True"

于 2018-04-30T13:01:05.173 回答
4

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

您可以使用一个小的 hack-around 在单独的线程中更新您的 observable 集合

于 2010-02-09T12:20:41.790 回答
1

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

快速样机:

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>

视图/视图模型

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 框架。

于 2010-02-09T10:58:39.413 回答
0

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

于 2011-08-20T15:08:17.383 回答