【问题标题】:Issues with {RelativeSource PreviousData} when removing collection elements删除集合元素时出现 {RelativeSource PreviousData} 的问题
【发布时间】:2011-10-15 10:34:12
【问题描述】:

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

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

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

现在,当最初绑定视图或将元素添加到列表(ObservableCollection)时,这可以正常工作,但删除第一个元素时元素不会在第二个元素上不可见

关于如何解决这个问题的任何想法?

【问题讨论】:

  • +1 很好地用于 RelativeSource PreviousData... 必须使用那个。
  • 我在 ObservableCollection 上使用 Move 时也遇到了这个问题。

标签: wpf mvvm binding


【解决方案1】:

之前的答案有一些浪费的代码......不妨在这里使用它:

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

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

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

RelativeSourceTest.xaml

<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));
            }
        }
    }
}

【讨论】:

  • 嗯,最终结果与重新创建集合(我试图避免)没有太大区别。如果没有更好的结果,我会接受答案。
【解决方案2】:

现在是 17 年,但问题就在这里。 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>

【讨论】:

  • 工作精美,无需使用事件处理程序污染后面的代码。
【解决方案3】:

删除后必须刷新底层CollectionViewSource

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

【讨论】:

    猜你喜欢
    • 2011-10-30
    • 1970-01-01
    • 2015-03-22
    • 1970-01-01
    • 2020-12-18
    • 1970-01-01
    • 1970-01-01
    • 2011-02-06
    • 1970-01-01
    相关资源
    最近更新 更多