WPF ComboBox сбрасывает выбранный элемент при изменении источника элемента - PullRequest
6 голосов
/ 09 февраля 2010

Рассмотрим следующий 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 корректно перезагружает элементы через привязку. Однако, как побочный эффект, он также сбрасывает выбранный элемент!

Я использовал Reflector для проверки источников комбинированного списка, и, очевидно, это предполагаемое поведение.

Есть ли "хороший" способ, как обойти это? Чего я хочу добиться, так это того, что если пользователь выбирает «Компания А» и впоследствии перезагружает список компаний, то «Компания А» остается выбранной (при условии, что она находится в новом списке).

Ответы [ 4 ]

4 голосов
/ 09 февраля 2010

Может быть, вы можете использовать ObservableCollection<Company> вместо вашего IEnumerable<Company>? Затем при смене фона вы можете только добавлять / удалять элементы, которые являются новыми / отсутствуют в новом списке, выбранный элемент должен оставаться, если он не был удален изменением.

Вы можете обновить свою наблюдаемую коллекцию в отдельном потоке с небольшим дополнением .

3 голосов
/ 30 апреля 2018

Пожалуйста, попробуйте следующий код. Включите следующее свойство в поле со списком

IsSynchronizedWithCurrentItem="True"

0 голосов
/ 20 августа 2011

Как указал Якодер, это связано с равенством объектов. Пока вы связываете SelectedValue вместо SelectedItem, вы можете определять ItemsSource как коллекцию анонимных типов. Тогда эта проблема не возникнет (и это также быстрее, если вам нужно прочитать значения из базы данных).

0 голосов
/ 09 февраля 2010

хм, я не знаю, является ли это "хорошим" способом, но если вы можете получить доступ к выбранному элементу до перезагрузки, вы можете сохранить его (или его ключ или что-то в этом роде) и снова выбрать его программно после перезагрузка завершена.

быстрый макет:

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

Но я предполагаю, что вы имеете в виду другой способ, чем это ручное обходное решение?
Надеюсь, это поможет в любом случае ...

UPDATE
Хорошо, я вижу, из фоновой темы.
Используете ли вы ICollectionView, чтобы связать свой комбобокс тоже? Если это так, вы можете использовать свойство CurrentItem для сохранения ссылки. Я сделал быстрый макет, и это работает на моей установке. это предполагает, что у вас есть ссылка на ваш пользовательский интерфейс:

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>

View / 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-инфраструктуры.

...