更改源时 DataGrid 更新不起作用

DataGrid update not working when changed source

这是我的 DataGrid 测试代码。我使用 ObservableCollection 在 DataGrid 源上设置了一些数据并绑定了它。并且我修改了 ObservableCollection 成员 属性 所在的值。在这种情况下,我的 UI 必须显示该值已更改。但是,我的 DataGrid 仅在我选择单元格时进行交互。

绑定

public ObservableCollection<MyClass> griddata { get; set; } = new ObservableCollection<MyClass>();

我的Class

public class MyClass
{
    public int num { get; set; }
    public int idxnumber { get; set; }
}

XAML

<Grid>
    <DataGrid ItemsSource="{Binding griddata, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
    </DataGrid>
</Grid>

函数

//... griddata.add(someOfData);
public void ViewModel()
{
    int i = 0;
    while(i < 30)
    {
        testing(0);
        i++;
    }
}

private void testing(int idxnum)
{
    var test = griddata.Where(z => z.idxnumber == idxnum).FirstOrDefalut();
    test.num += 1;
}

结果
单元格值显示 0,我选择的值立即更改为 30

期待结果
单元格值连续显示030

编辑:
这是我的全部代码:

XAML

<Window x:Class="TestSol3.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:TestSol3"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    
    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    <Grid>
        <DataGrid ItemsSource="{Binding griddata}">
            
        </DataGrid>
    </Grid>
</Window>

Model.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestSol3
{
    public class Model
    {
        public int num { get; set; }
        public int idxnumber { get; set; }
    }
}

查看Model.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestSol3
{
    public class ViewModel
    {
        public ObservableCollection<Model> griddata { get; set; } = new ObservableCollection<Model>();

        public ViewModel()
        {
            griddata.Add(new Model() { num = 0, idxnumber = 0 });
            griddata.Add(new Model() { num = 1, idxnumber = 1 });

            Load(0);
        }

        private void Load(int idxnum)
        {
            int i = 0;
            while(i < 30)
            {
                i++;
                griddata[idxnum].num++;
                //Thread.Sleep(200);
            }
        }
    }
}

[解决方法]

在 WPF 中,您可以从 Application.Current.Dispatcher

获取 dispatcher

How to pass the UI Dispatcher to the ViewModel

并且我们需要更改 ObservableCollection 中的项目以通知值更改事件。

ObservableCollection的文档说:

Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.

https://docs.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1?view=net-6.0

private void Load(int idxnum)
{
    Task.Run(() =>
    {
        int i = 0;
        while (i < 30)
        {
            i++;
            var model = griddata[idxnum];
            model.num++;

            var dispatcher = Application.Current.Dispatcher;
            dispatcher.Invoke(() =>
            {
                griddata.RemoveAt(idxnum);
                griddata.Insert(idxnum, model);
            });

            Thread.Sleep(500);
        }
    });
}

[简单演示]

我写了一个演示项目来展示如何连续更新 UI 值,如下所示。

它使用Task启动另一个线程来更新UI值,希望对您有所帮助。

MainWindow.xaml

<Window x:Class="TestWpfApp.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"
    mc:Ignorable="d" Title="MainWindow" Height="450" Width="800">        

    <StackPanel>
        <TextBlock Name="MyTextBlock" FontSize="20"></TextBlock>
        <Button Click="Button_Click">Start</Button>
    </StackPanel>
</Window>

MainWindow.xaml.cs

using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace TestWpfApp
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private Task? CurrentTask { get; set; }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // only one task is supported simultaneously
            if (this.CurrentTask != null
                && !this.CurrentTask.IsCompleted)
            {
                return;
            }

            // start a task to calculate value continuously
            this.CurrentTask = Task.Run(() =>
            {
                int i = 0;
                while (i < 30)
                {
                    ++i;
                    Thread.Sleep(500);

                    // update value in the UI thread
                    this.Dispatcher.Invoke(() =>
                    {
                        this.MyTextBlock.Text = i.ToString();
                    });
                }
            });
        }
    }
}

尝试在您的模型中实施 INotifyPropertyChanged

namespace TestSol3
{
    public class ViewModel : INotifyPropertyChanged
    {
        public ObservableCollection<Model> _griddata = new ObservableCollection<Model>();
        public ObservableCollection<Model> griddata 
        { 
            get => _griddata;
            set
            {
                _griddata= value;
                OnChangedProperty("griddata");
            }
        }

        public void OnChangedProperty(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    }
}