【问题标题】:DataGrid update not working when changed source更改源时DataGrid更新不起作用
【发布时间】:2022-01-20 02:36:16
【问题描述】:

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

绑定

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

我的班级

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

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

【问题讨论】:

  • while (i &lt; 30)中添加Thread.Sleep(100);会减慢UI中的值更新速度
  • @cg-zhou 不工作。我也试过Thread.sleep()Task.Delay,但这并没有帮助。
  • 我尝试按照你的描述构建一个项目,由于缺少部分代码,这似乎很难;如果你提供可运行的 xx.xaml.cs 和 xx.cs,那将有很大帮助。

标签: c# wpf mvvm


【解决方案1】:

尝试在你的模型中实现 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));
        }
    }
}

【讨论】:

    【解决方案2】:

    [解决方案]

    在WPF中,你可以从Application.Current.Dispatcher得到dispatcher

    How to pass the UI Dispatcher to the ViewModel

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

    ObservableCollection 的文档说:

    表示一个动态数据集合,在添加、删除项目或刷新整个列表时提供通知。

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

    【讨论】:

    • 感谢您的回答。我会试试这个。
    • 我发现这个答案最大的问题。很抱歉这个答案,文本块很好用 dispatcherDataGrid 在属性不断变化时不能很好地工作。
    • 我更新了答案,ObservableCollection 只接受add/remove/refresh 事件。
    • 我找到了这个article
    猜你喜欢
    • 2012-06-22
    • 2016-05-22
    • 1970-01-01
    • 1970-01-01
    • 2011-10-26
    • 1970-01-01
    • 2014-08-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多