Как обновить ComboBox ItemsSource без мерцания

Я борюсь с обновлением ComboBox, которое раньше работало. Изначально у меня ItemsSource был привязан к свойству ObservableCollection<char>, доступному только для чтения, в ViewModel. Когда пользователь инициирует изменения (что делается с помощью движений мыши, в некоторых случаях десятки раз в секунду), get перестраивает коллекцию из модели и возвращает ее.

Когда я переключился на свой собственный объект в ObservableCollection, ComboBox начал мигать во время обновлений. Я не уверен, что не так. Вот код, который работает, начиная с XAML:

<ComboBox ItemsSource='{Binding FromBins}' SelectedValue='{Binding SelectedFromBin, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}' />

ViewModel:

public ObservableCollection<char> FromBins
    {
        get
        {
            ObservableCollection<char> tempBins = new ObservableCollection<char>();
            foreach (var item in Map.BinCounts)
            {
                tempBins.Add(item.Key);
            }
            return tempBins;
        }
    }

Я просто вызываю изменение свойства при каждом движении мыши, и интерфейс работает так, как ожидалось (есть другая логика, чтобы убедиться, что SelectedItem действителен).

Чтобы сделать интерфейс более полезным, я решил добавить больше информации к ComboBox, используя свой собственный класс:

public class BinItem : IEquatable<BinItem>
{
    public char Bin { get; set; }
    public SolidColorBrush BinColor { get; set; }
    public string BinColorToolTip { get {...} }

    public BinItem( char bin )
    {
        Bin = bin;
        BinColor = new SolidColorBrush(BinColors.GetBinColor(bin));
    }

    public bool Equals(BinItem other)
    {
        return other.Bin == Bin ? true : false;
    }
}

Если я заменяю char на BinItem в рабочем коде ViewModel, я начинаю мерцать при перемещении мыши. Вот обновленный XAML:

<ComboBox ItemsSource='{Binding FromBins}' SelectedValue='{Binding SelectedFromBin, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}'>
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation = "Horizontal" ToolTip='{Binding BinColorToolTip}'>
                <Rectangle Fill='{Binding BinColor}' Width='10' Height='10' HorizontalAlignment='Center' VerticalAlignment='Center' Margin='0,0,4,0' Stroke='#FF747474' />
                    <TextBlock Text = "{Binding Bin}" Width='16' />
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

Я пробовал множество вещей, в том числе, но не ограничиваясь: -Используется List вместо ObservableCollection, но даже если Get срабатывает каждый раз и возвращает правильный набор элементов, интерфейс не всегда обновляется (хотя мерцание исчезает). -Оставить все возможные бункеры в источнике элементов и добавить свойство Visibility в класс BinItem, к которому я привязан (не удалось его обновить).

Я подозреваю, что делаю что-то в корне неправильно, но никакие поиски ТАК или иным образом пока не помогли. Любая помощь приветствуется.

В качестве примечания, использование ObservableCollection имеет смысл только тогда, когда вы добавляете или удаляете элементы в / из одного и того же объекта коллекции (который хранится как ItemsSource). Когда вы всегда заменяете экземпляр коллекции, он вам не нужен.

Clemens 21.05.2018 21:13

Вероятно, он мерцает от постоянного обновления списка при каждом вызове списка. Когда вы заполняете этот список, насколько он меняется? Вы просто добавляете список или он каждый раз кардинально меняется? Потому что может быть полезно просто предварительно заполнить ObservableCollection

Chris 21.05.2018 21:20

Кеш меняется, обновляется каждые x секунд.

user1228 21.05.2018 22:09

Клеменс, есть идеи, почему пользовательский интерфейс иногда не обновлялся, когда я вместо этого использовал List<T>? Крис, коллекция будет немного колебаться во время редактирования, но не более одного добавления и одного вычитания за обновление, поэтому я каждый раз перестраивал ее. Я подозреваю, что метод грубой силы из моего ответа - не лучший способ, но я предполагаю, что он работает, потому что ни в коем случае коллекция никогда не бывает пустой или нулевой. Уилл, спасибо за предложение, я бы попробовал это дальше, если бы ручные добавления / вычитания не работали.

M. Swindler 22.05.2018 14:41
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
272
1

Ответы 1

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

Вместо того, чтобы заменять коллекцию при каждом обновлении, я прохожу логику выяснения того, что изменилось (при каждом обновлении может быть добавление И удаление одновременно). Код ниже:

private ObservableCollection<BinItem> _FromBins = new ObservableCollection<BinItem>();

public ObservableCollection<BinItem> FromBins
{
    get
    {
        if (_FromBins.Count > 0)
        {
            List<char> BinsToRemove = new List<char>();
            foreach (var item in _FromBins)
            {
                if (!Map.BinCounts.ContainsKey(item.Bin))
                {
                    BinsToRemove.Add(item.Bin);
                }
            } 
            foreach (var item in BinsToRemove)
            {
                _FromBins.Remove(new BinItem(item));
            }
        }
        foreach (var item in Map.BinCounts)
        {
            if (!_FromBins.Contains(new BinItem(item.Key)) && item.Value > 0)                   {
                _FromBins.Add(new BinItem(item.Key));
            }
        }
        return _FromBins;
    }
}

Надеюсь, это поможет и кому-то другому.

Другие вопросы по теме