Обновление пользовательского интерфейса после обновления в привязанную к ListBox коллекцию - PullRequest
1 голос
/ 27 февраля 2012

Я пытаюсь визуально показать, что элемент (в моем случае BitmapImage) был добавлен в коллекцию в ListBox с использованием WPF / MVVM. Чтобы получить некоторое представление, я снимаю потоковое видео с помощью карты захвата и снимаю неподвижные изображения во время потокового видео. Эти изображения захватываются нажатием кнопки «Still Image» в пользовательском интерфейсе. После захвата изображения я хотел бы получить эскиз упомянутого изображения на отдельной панели в интерфейсе пользователя. Я понимаю, что нужно сделать, чтобы сделать это, но я не могу заставить его работать. Прямо сейчас моя модель выполняет весь поиск данных.

public ObservableCollection<BitmapImage> GetAssetThumbnails()
{
    var imageDir = ImgPath != null ? new DirectoryInfo(ImgPath) : null;
    if(imageDir != null)
    {
        try
        {
            foreach (FileInfo imageFile in imageDir.GetFiles("*.jpg"))
            {
                var uri = new Uri(imageFile.FullName);
                _assetThumbnails.Add(new BitmapImage(uri));
            }

        }
        catch (Exception)
        {
            return null;
        }
    }

    return _assetThumbnails;
}

My ViewModel создает новый экземпляр модели в своем конструкторе, а затем устанавливает открытое свойство AssetCollection, равное _assetModel.GetAssetThumbnails(). ViewModel имеет все стандартные события OnPropertyChanged и обработку событий.

private void OnPropertyChanged(string propertyName)
{
    if(PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

#region Public Properties
public ObservableCollection<BitmapImage> AssetCollection
{
    get { return _assetCollection; }
    set
    {
        if (_assetCollection != value)
        {
            _assetCollection = value;
            OnPropertyChanged("AssetCollection");    
        }
    }
}
#endregion

private void _assetCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    OnPropertyChanged("AssetCollection");
}
public event PropertyChangedEventHandler PropertyChanged;

Затем в моем файле XAML я связываю источник элемента ListBox с AssetCollection.

<ListBox x:Name="lstBoxAssets" ItemsSource="{Binding AssetCollection}" Grid.Row="1" Background="{DynamicResource GrayColor}" Margin="5,0,0,5" ></ListBox>   

Я читал, что каждый элемент в коллекции должен реализовывать интерфейс INotifyPropertyChanged. Я попытался создать класс-обертку для растрового изображения, которое это реализовало, но это тоже не сработало. Есть ли что-то, что я здесь скучаю? Изображения отображаются первоначально, но не обновляются после того, как изображение было снято и сохранено. У меня такое ощущение, что это происходит из-за того, что PropertyChanged не вызвали. В процессе отладки я заметил, что при проверке он всегда равен нулю, и поэтому метод OnPropetyChanged никогда не вызывается. Я не уверен, где я должен добавить этот обработчик событий, хотя. Я использую только файл code-behind для добавления контекста данных модели представления в представление, и это все. Вот где я обычно думаю добавить любую обработку событий. Может кто-нибудь увидеть что-то простое, что я здесь скучаю?

1 Ответ

2 голосов
/ 27 февраля 2012

Изменяете ли вы ObservableCollection при обновлении списка или меняете элементы, хранящиеся в коллекции?

Если вы изменяете элементы внутри коллекции, вам нужно найти способ сообщить WPF, что коллекция изменилась при изменении отдельных элементов, чтобы она могла перерисовать ее.

Обычно элементы в коллекции реализуют INotifyPropertyChanged, поэтому к элементам в списке легко прикрепить уведомление PropertyChange, которое сообщает WPF о возникновении события CollectionChanged при каждом изменении свойства.

// Wireup CollectionChanged in Constructor
public MyViewModel()
{
    ListOfSomeItems = new List<SomeItem>();
    AssetCollection.CollectionChanged += AssetCollection_CollectionChanged;
}

// In CollectionChanged event, wire up PropertyChanged event on items
void AssetCollection_CollectionChanged(object sender, CollectionChangedEventArgs e)
{
    if (e.NewItems != null)
    {
        foreach(Asset item in e.NewItems)
            item.PropertyChanged += Asset_PropertyChanged;
    }
    if (e.OldItems != null)
    {
        foreach(Asset item in e.OldItems)
            item.PropertyChanged -= Asset_PropertyChanged;
    }
}

// In PropertyChanged, raise CollectionChanged event
void Asset_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    OnPropertyChanged("AssetCollection");
}

Если элементы не реализуют INotifyPropertyChanged, вам придется вручную вызывать событие CollectionChanged каждый раз, когда элемент в коллекции изменяется.

AssetCollection[0].Thumbnail = new BitmapImage(uri);
OnPropertyChanged("AssetCollection");

Если выизменяя сам ObservableCollection, добавляя / удаляя элементы и не видя обновления пользовательского интерфейса, тогда это звучит так, как будто это может быть синтаксическая ошибка в том месте, где мы не видимся.

Самым распространенным, что я вижу, является внесение изменений в частную собственность, а не в публичную собственность.

// Will not raise the CollectionChanged notification
_assetCollection = _assetModel.GetAssetThumbnails();

// Will raise the CollectionChanged notification
AssetCollection = _assetModel.GetAssetThumbnails();

Хотя я также вижу много людей, которые используют List или обычайтакже вместо ObservableCollection, который не вызывает событие CollectionChanged.

...