【问题标题】:WPF ObservableCollection Insert at index and NotifyOnTargetUpdatedWPF ObservableCollection 在索引和 NotifyOnTargetUpdated 处插入
【发布时间】:2019-09-24 04:23:39
【问题描述】:

什么

DataGrid 绑定到 ViewModel 中的 ObservableCollection。

每 2 秒,交替修改/添加一个项目。

修改/添加的项目数据网格行突出显示 1 秒。

问题

添加的项目必须插入到给定的索引处,不一定在 ObservableCollection 的末尾。这是通过使用Insert(int index, ListItemVM item) 来实现的。

错误

该项目被添加到索引 1 的第二个位置。 UI 会相应更新并突出显示新项目但也错误地突出显示最后一个项目。我真的很想摆脱不需要的行为,但我不知道如何。

代码可运行:

XAML

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <DataGrid ItemsSource="{Binding MyList}" AutoGenerateColumns="False" CanUserAddRows="False" GridLinesVisibility="None" HeadersVisibility="None" BorderThickness="0" ScrollViewer.VerticalScrollBarVisibility="Visible">
        <DataGrid.RowStyle>
            <Style TargetType="DataGridRow">
                <Setter Property="Background" Value="Green" />
                <Style.Triggers>
                    <EventTrigger RoutedEvent="Binding.TargetUpdated">
                        <BeginStoryboard>
                            <Storyboard>
                                <ColorAnimation Duration="00:00:01" Storyboard.TargetProperty="Background.Color" From="Blue" To="Transparent" />
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </Style.Triggers>
            </Style>
        </DataGrid.RowStyle>
        <DataGrid.Columns>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Label>
                            <TextBlock Text="{Binding Id}" />
                        </Label>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Label>
                            <TextBlock x:Name="txtTheData" Text="{Binding TheData, NotifyOnTargetUpdated=True}" Background="Transparent" />
                        </Label>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</Window>

代码隐藏

using System.Windows;
using WpfApp1.ViewModels;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            DataContext = new MainWindowVM();

            InitializeComponent();
        }
    }
}

视图模型

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows.Threading;

namespace WpfApp1.ViewModels
{
    public class MainWindowVM : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        Random random = new Random();

        public ObservableCollection<ListItemVM> MyList { get; set; } = new ObservableCollection<ListItemVM>
            {
                new ListItemVM(1, "Data1"),
                new ListItemVM(2, "Data2"),
                new ListItemVM(3, "Data3"),
            };

        bool mustAdd;
        public MainWindowVM()
        {
            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(2000);
            timer.Tick += (object sender, EventArgs e) =>
            {
                mustAdd = !mustAdd;
                if (mustAdd == false)
                {
                    // Emulate Update existing
                    var nextData = new string(Enumerable.Repeat("ABCDEFG", 10).Select(s => s[random.Next(s.Length)]).ToArray());
                    MyList[1].SetNewData(nextData);
                }
                else
                {
                    // Emulate Add new
                    var newItem = new ListItemVM(99, "old");
                    MyList.Insert(1, newItem);
                    // Notify the UI of the change
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyList)));
                    newItem.SetNewData("NEW");
                }
            };
            timer.Start();
        }
    }
}

项目视图模型

using System.ComponentModel;

namespace WpfApp1.ViewModels
{
    public class ListItemVM : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public int Id { get; set; }
        public string TheData { get; set; }

        public ListItemVM(int id, string theData)
        {
            Id = id;
            TheData = theData;
        }

        internal void SetNewData(string nextData)
        {
            // Change data
            TheData = nextData;
            // Notify the UI of the change
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TheData)));
        }
    }
}

【问题讨论】:

  • 只禁用项目容器回收(虚拟化)。
  • 你并不总是插入你也在更新现有的项目。 c# 中的集合使用零基索引。因此第一个是索引 0,第二个是 1。一个 observablecollection 实现了 inotifycollectionchanged。如果您将集合中的一个项目设置为另一个对象甚至它本身,它会引发整个项目的更改。如果插入。它为该新项目提出。您可以在视图中处理该事件并使用它来启动动画。
  • @andy 更新工作正常,在示例中是为了完成。该错误是关于添加项目
  • @ibiza:那你为什么要处理Binding.TargetUpdated,而不是像我建议的here 那样使用附加行为?最后一行也将引发此事件。

标签: c# .net wpf xaml eventtrigger


【解决方案1】:

不如在 Binding.TargetUpdated 上启动高亮动画,而是在 ViewModel 上设置一个属性,比如

private bool _shouldHighlight = false;
public bool ShouldHighlight
{
    get => _shouldHighlight;
    set
    {
        _shouldHighlight = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ShouldHighlight)));
    }
}

将项目添加到集合时,首先为所有元素设置ShouldHighlight = false,然后添加ShouldHighlight = true的元素。

在视图中,而不是

                <EventTrigger RoutedEvent="Binding.TargetUpdated">
                    <BeginStoryboard>
                        ...
                </EventTrigger>
            </Style.Triggers>

您可以使用绑定设置为 ShouldHighlight 和 Value = True 的 DataTrigger。或者订阅DataContext的PropertyChanged事件(在xaml.cs Loaded事件中),当PropertyChanged的args显示ShouldHighlight变为true时启动高亮动画:

Loaded += delegate
{
    if (DataContext is ListItemVM viewModel)
    {
        viewModel.PropertyChanged += delegate (object o, PropertyChangedArgs args)
        {
             if (args.PropertyName == nameof(viewModel.ShouldHighlight) && viewModel.ShouldHighlight)
                 // Starts the animation //
        }
    }
}

我只是猜测,如果它有效,请告诉我:)

【讨论】:

    猜你喜欢
    • 2011-03-04
    • 1970-01-01
    • 2011-09-19
    • 1970-01-01
    • 1970-01-01
    • 2016-06-19
    • 2018-09-22
    • 2022-01-05
    • 1970-01-01
    相关资源
    最近更新 更多