更改源时 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
。
期待结果
单元格值连续显示0
到30
。
编辑:
这是我的全部代码:
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.
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));
}
}
}
这是我的 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
。
期待结果
单元格值连续显示0
到30
。
编辑:
这是我的全部代码:
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.
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));
}
}
}