当删除集合元素时,使用{RelativeSource PreviousData}出现问题

10

我正在使用以下(简化的)代码来在ItemsControl中除第一个项外的所有项中显示一个元素:

<TheElement Visibility="{Binding RelativeSource={RelativeSource PreviousData},
                                 Converter={StaticResource NullToVisibility}}/>

NullToVisibility 是一个简单的转换器,如果源为空则返回 Visibility.Hidden,否则返回 Visibility.Visible

现在,在绑定视图初始显示或向列表(一个ObservableCollection)添加元素时,它可以正常工作,但是在移除第一个元素时第二个元素没有被隐藏

有什么解决方法吗?


+1,很好地使用了RelativeSource PreviousData...将不得不使用它。 - iCollect.it Ltd
当我在ObservableCollection上使用Move时,我也遇到了这个问题。 - Lauraducky
3个回答

8

我之前的回答中有一些浪费的代码... 不如在这里使用:

关键是要刷新视图源,例如:

CollectionViewSource.GetDefaultView(this.Categories).Refresh();

enter image description here

以下是完整的示例源代码。 Remove First Item 会移除第一个元素并刷新视图:

RelativeSourceTest.xaml


(注:此内容为格式化后的文本,保留了HTML标签)
<UserControl x:Class="WpfApplication1.RelativeSourceTest"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:PersonTests="clr-namespace:WpfApplication1" mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <PersonTests:NullToVisibilityConvertor x:Key="NullToVisibility"/>
    </UserControl.Resources>
    <Grid>
        <StackPanel Background="White">
            <Button Content="Remove First Item" Click="Button_Click"/>
            <ListBox ItemsSource="{Binding Categories}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <CheckBox IsChecked="{Binding Checked, Mode=TwoWay}" >
                            <StackPanel>
                                <TextBlock Text="{Binding CategoryName}"/>
                                <TextBlock Text="Not The First" 
                                    Visibility="{Binding RelativeSource={RelativeSource PreviousData},
                                        Converter={StaticResource NullToVisibility}}"/>
                            </StackPanel>
                        </CheckBox>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </StackPanel>
    </Grid>
</UserControl>

RelativeSourceTest.xaml.cs

using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WpfApplication1
{
    public partial class RelativeSourceTest : UserControl
    {
        public ObservableCollection<Category> Categories { get; set; }

        public RelativeSourceTest()
        {
            InitializeComponent();
            this.Categories = new ObservableCollection<Category>()
                                {
                                    new Category() {CategoryName = "Category 1"},
                                    new Category() {CategoryName = "Category 2"},
                                    new Category() {CategoryName = "Category 3"},
                                    new Category() {CategoryName = "Category 4"}
                                };
            this.DataContext = this;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.Categories.RemoveAt(0);
            CollectionViewSource.GetDefaultView(this.Categories).Refresh();
        }
    }
}

Category.cs

using System.ComponentModel;

namespace WpfApplication1
{
    public class Category : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private bool _checked;
        public bool Checked
        {
            get { return _checked; }
            set
            {
                if (_checked != value)
                {
                    _checked = value;
                    SendPropertyChanged("Checked");
                }
            }
        }

        private string _categoryName;
        public string CategoryName
        {
            get { return _categoryName; }
            set
            {
                if (_categoryName != value)
                {
                    _categoryName = value;
                    SendPropertyChanged("CategoryName");
                }
            }
        }

        public virtual void SendPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

好吧,最终结果与重新创建集合(我试图避免这种情况)并没有太大的区别。如果没有更好的答案出现,我会接受这个答案。 - Diego Mijelshon

2

现在是2017年,但问题仍然存在。我所看到的MVVM方法:

public class PreviousDataRefreshBehavior : Behavior<ItemsControl> {
    protected override void OnAttached() {
        var col = (INotifyCollectionChanged)AssociatedObject.Items;
        col.CollectionChanged += OnCollectionChanged;
    }

    protected override void OnDetaching() {
        var col = (INotifyCollectionChanged)AssociatedObject.Items;
        col.CollectionChanged -= OnCollectionChanged;
    }

    private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
        if(e.Action != NotifyCollectionChangedAction.Remove) {
            return;
        }

        CollectionViewSource.GetDefaultView(AssociatedObject.ItemsSource).Refresh();
    }
}

和用法:

<ItemsControl>
  <int:Interaction.Behaviors>
    <behaviors:PreviousDataRefreshBehavior/>
  </int:Interaction.Behaviors>
</ItemsControl>

不需要在代码后面添加事件处理程序,就可以完美地工作。 - Lauraducky

0

在删除后,必须刷新基础的CollectionViewSource

CollectionViewSource.GetDefaultView(this.Items).Refresh();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接