Реализация ListView с помощью CollectionViewSource - не обновляется? - PullRequest
1 голос
/ 20 декабря 2011

Я работаю над настройкой ListView, у которого для свойства Source установлено значение ivar моего класса, называемого Cat.

Каждый Cat имеет ObservableCollection из Trait объектов:

private ObservableCollection<Trait> _traits = new ObservableCollection<Trait>();

public ObservableCollection<Trait> Traits
{
get
    {
        return _traits;
    }
}

public void AddTrait(Trait t)
{
    _traits.Add(t);
    // Is this redundant? Is one better than the other?
    this.OnPropertyChanged("_traits");
    this.OnPropertyChanged("Traits");
}

public IEnumerator<Object> GetEnumerator()
{
    return _traits.GetEnumerator();
}

А затем я присваиваю Source свойство этой коллекции Traits:

this.CollectionViewSource.Source = CurrentCat.Traits;

Это работает правильно, и объекты Traitправильно отображаются в моем ListView.

Проблема заключается в том, что изменения в этой базовой коллекции _traits не приводят к правильному обновлению пользовательского интерфейса.Например, это:

void AddTraitButton_Click(object sender, RoutedEventArgs e)
{
    if (this.CurrentCat != null)
    {
        this.CurrentCat.AddTrait(new Trait());
    }
}

Похоже, что немедленно в пользовательском интерфейсе не оказывает никакого влияния, но если я сбросил свойство Source следующим образом:

var oldSource = this.CollectionViewSource.Source;
this.CollectionViewSource.Source = null;
this.CollectionViewSource.Source = oldSource;

Затем ListView обновляется правильно.Но я уверен, что должно быть что-то, чего мне не хватает, поскольку я хотел бы, чтобы пользовательский интерфейс обновлялся при добавлении / удалении элемента.

Редактировать: CollectionViewSource применяется к ListView в моем файле XAML:

<CollectionViewSource x:Name="CollectionViewSource" x:Key="CollectionViewSource" />

...

<ListView x:Name="ItemListView" ItemsSource="{Binding Source={StaticResource CollectionViewSource}}" ...

Ответы [ 4 ]

0 голосов
/ 28 октября 2013

Вы можете по-прежнему работать исключительно с ObservableCollection. Хотя есть одна проблема - он не будет отображать данные в IsInDesignMode. Возможно, в будущем это улучшится.

public class MainViewModel : ViewModelBase
{
...
    private ObservableCollection<PartViewModel> _parts;
    public ObservableCollection<PartViewModel> Parts
    {
        get
        {
            if (_parts == null)
            {
                _parts = new ObservableCollection<PartViewModel>();
                _parts.CollectionChanged += _parts_CollectionChanged;
            }
            return _parts;
        }
    }

    object m_ReorderItem;
    int m_ReorderIndexFrom;
    void _parts_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Remove:
                m_ReorderItem = e.OldItems[0];
                m_ReorderIndexFrom = e.OldStartingIndex;
                break;
            case NotifyCollectionChangedAction.Add:
                if (m_ReorderItem == null)
                    return;
                var _ReorderIndexTo = e.NewStartingIndex;
                m_ReorderItem = null;
                break;
        }
    }

    private PartViewModel _selectedItem;
    public PartViewModel SelectedItem
    {
        get
        {
            return _selectedItem;
        }
        set
        {
            if (_selectedItem != value)
            {
                _selectedItem = value;
                RaisePropertyChanged("SelectedItem");
            }
        }
    }
   ...

    #region ViewModelBase

    public override void Cleanup()
    {
        if (_parts != null)
        {
            _parts.CollectionChanged -= _parts_CollectionChanged;
        }
        base.Cleanup();
    }

    #endregion

  }

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <Grid.Resources>
        <CollectionViewSource x:Name="PartsCollection" Source="{Binding Parts}"/>
    </Grid.Resources>

    <ListView Margin="20" CanReorderItems="True" CanDragItems="True" AllowDrop="True" 
              ItemsSource="{Binding Source={StaticResource PartsCollection}}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" SelectionMode="Single">
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
            </Style>
        </ListView.ItemContainerStyle>
        <ListView.ItemTemplate>
        ...
        </ListView.ItemTemplate>
    </ListView>
</Grid>
0 голосов
/ 21 декабря 2011

Вместо того, чтобы напрямую привязывать к CollectionViewSource и заменять его Source для принудительного обновления, я полагаю, что вы хотите привязать к View свойству CVS ...

<ListView x:Name="ItemListView" 
          ItemsSource="{Binding Source={StaticResource CollectionViewSource}, Path=View}" ...

... и вызовите CollectionViewSource.Refresh() после обновления исходной коллекции.

void AddTraitButton_Click(object sender, RoutedEventArgs e)
{
    if (this.CurrentCat != null)
    {
        this.CurrentCat.AddTrait(new Trait());
        this.CollectionViewSource.Refresh();
    }
}

Кроме того, пара замечаний, поскольку вы, кажется, относительно новичок в соглашениях .NET / WPF:

  • Закрытые члены классов .NET обычно называют «полями», а не «иварами» (фон Objective-C?:))
  • Префикс членов класса с ключевым словом this обычно избыточен, если в области видимости нет другого идентификатора с таким же именем
  • Стоит изучить MVVM и связанные с ним шаблоны, если вы будете делать что-то нетривиальное в WPF; они помогают вам сделать ваши представления (объекты XAML) максимально легкими и легко изменяемыми.

    В вашем случае, например, я предполагаю, что код, который вы показали, взят из кода позади любого Window или UserControl, содержащего ваш ListView. Следование шаблону MVVM потребовало бы создания отдельного класса ViewModel, который содержал бы коллекцию Traits и представлял ее через CollectionViewSource (используя свойство View, как я уже упоминал). Тогда ваш UserControl будет иметь экземпляр ViewModel, назначенный как DataContext, и ListView может быть привязан к открытому CollectionView.

0 голосов
/ 10 февраля 2012

Ознакомьтесь с демонстрацией, которую я выложил относительно подключения привязки ListView и работы для всех операций CRUD.

http://www.flaskofespresso.com/2012/01/windows-8-metro-app-listview-binding-and-editing/

0 голосов
/ 20 декабря 2011

Я не могу найти его сейчас, но мне кажется, что я помню некоторые проблемы с привязкой к CollectionViewSource. Вы пытались связать непосредственно с CurrentCat.Traits и установить this.DataContext = this в коде (я предполагаю, что вы не используете MVVM здесь)?

<ListView x:Name="ItemListView" ItemsSource="{Binding CurrentCat.Traits}" />
...