none
DataGrid和Datatable数据绑定后,Datatable在另一个线程中被修改后DataGrid数据不能及时更新的问题 RRS feed

  • 问题

  • DataGrid通过ItemsSource绑定Datatable,在主线程中修改DataTable的话DataGrid貌似可以自动更新(与DataTable相适应)

    但问题是我的DataTable会被另一个线程修改,被修改后DataGrid无任何反映,但是只要用鼠标拉动窗口改变一下WPF窗体的大小,DataGrid就能更新了。

    网上说实现INotifyPropertyChanged接口的方法,在我这里估计不行。
    因为我的是Datatable类型的变量,不是自定义的类。 修改的是变量的数据,而不是自定义类的属性,没法在属性的set中发出数据更新事件的通知

    实在不知道怎么解决,请前辈们帮帮忙吧。

    我在想,另一个线程更新完Datatable变量后,有没有办法告诉DataGrid主动刷新一下?

     


    2011年8月20日 11:55

答案

  • 我在想,另一个线程更新完Datatable变量后,有没有办法告诉DataGrid主动刷新一下?

    答案是可以的,如果你的DataGrid名字为 dg 的话,下面代码就可以让你在多线程下通知i并要求DataGrid更新:

    dg.Dispatcher.Invoke(new Action(()=> dg.Items.Refresh()));
    

     

    我的例子:

    XAML:

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Button Content="Add Data in the other Thread" x:Name="Add" Click="Add_Click" Margin="10"/>
            <DataGrid x:Name="dg" Grid.Row="1" ItemsSource="{Binding Table}" AutoGenerateColumns="False" CanUserAddRows="False">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="2*"/>
                    <DataGridTextColumn Header="Age" Binding="{Binding Age}" Width="*"/>
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
    </Window>
    

    C#:

        public partial class MainWindow : Window
        {
            public DataTable Table { getset; }
            public MainWindow()
            {
                InitializeComponent();
                LoadData();
                this.DataContext = this;
            }
     
            private void LoadData()
            {
                Table = new DataTable();
                Table.Columns.Add(new DataColumn("Name"));
                Table.Columns.Add(new DataColumn("Age"));
                DataRow row;
                for (int i = 0; i < 10; i++)
                {
                    row = Table.NewRow();
                    row["Name"] = "Name" + i.ToString();
                    row["Age"] = 20 + i % 4;
                    Table.Rows.Add(row);
                }
            }
     
            private void Add_Click(object sender, RoutedEventArgs e)
            {
                Thread thread = new Thread(() =>
                {
                    if (Table != null)
    
                    {
                        DataRow row = Table.NewRow();
                        row["Name"] = "New Name";
                        row["Age"] = 30;
                        Table.Rows.Add(row);
                    }
                    dg.Dispatcher.Invoke(new Action(()=> dg.Items.Refresh()));
                });
                thread.Start();
            }
        }

     

    Sincerely, 


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2011年8月22日 17:41
    版主

全部回复

  • 假设你有一个Model 定义了 DataTable属性... 

    public DataTable Source{get;set;}

    那么其他线程修改变量值的时候你能不能显示的在该Model中raise一个PropertyChanged事件, 属性名就是"Source"?

    另外绑定到未实现INotifyPropertyChanged 或 非DependencyProperty对象的属性有可能会引起内存泄露...


    just another day.
    2011年8月20日 16:59
  • 假设你有一个Model 定义了 DataTable属性... 

    public DataTable Source{get;set;}

    那么其他线程修改变量值的时候你能不能显示的在该Model中raise一个PropertyChanged事件, 属性名就是"Source"?

    另外绑定到未实现INotifyPropertyChanged 或 非DependencyProperty对象的属性有可能会引起内存泄露...


    just another day.

    其它线程改变值时,我怎么显示一个PropertyChanged事件呢?

    其它线程改变值的时候,我是直接改的DataTable变量里面的内容。  这时,我怎么raise事件呢? INotifyPropertyChanged 只像只能用于单个属性的变量的变动。  我是改了一个表格,不可能把每个单元格都当做一个属性,每改一个单元格都raise一个事件吧。 我是个菜鸟,能再讲详细点吗? ^_^

    2011年8月22日 7:37
  • DataTable没有实现 INotifyPropertyChanged   DataTable某项改变后肯定不能通知到UI ..

    所以我觉得可以在Model定义一个方法.... 该方法中Raise PropertyChanged事件...  每次改变DataTable的时候去调用这个方法....

    这是我的想法..供你参考下..或者根据你的实际情况有其他更好解决方法..


    just another day.
    2011年8月22日 12:41
  • 我在想,另一个线程更新完Datatable变量后,有没有办法告诉DataGrid主动刷新一下?

    答案是可以的,如果你的DataGrid名字为 dg 的话,下面代码就可以让你在多线程下通知i并要求DataGrid更新:

    dg.Dispatcher.Invoke(new Action(()=> dg.Items.Refresh()));
    

     

    我的例子:

    XAML:

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Button Content="Add Data in the other Thread" x:Name="Add" Click="Add_Click" Margin="10"/>
            <DataGrid x:Name="dg" Grid.Row="1" ItemsSource="{Binding Table}" AutoGenerateColumns="False" CanUserAddRows="False">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="2*"/>
                    <DataGridTextColumn Header="Age" Binding="{Binding Age}" Width="*"/>
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
    </Window>
    

    C#:

        public partial class MainWindow : Window
        {
            public DataTable Table { getset; }
            public MainWindow()
            {
                InitializeComponent();
                LoadData();
                this.DataContext = this;
            }
     
            private void LoadData()
            {
                Table = new DataTable();
                Table.Columns.Add(new DataColumn("Name"));
                Table.Columns.Add(new DataColumn("Age"));
                DataRow row;
                for (int i = 0; i < 10; i++)
                {
                    row = Table.NewRow();
                    row["Name"] = "Name" + i.ToString();
                    row["Age"] = 20 + i % 4;
                    Table.Rows.Add(row);
                }
            }
     
            private void Add_Click(object sender, RoutedEventArgs e)
            {
                Thread thread = new Thread(() =>
                {
                    if (Table != null)
    
                    {
                        DataRow row = Table.NewRow();
                        row["Name"] = "New Name";
                        row["Age"] = 30;
                        Table.Rows.Add(row);
                    }
                    dg.Dispatcher.Invoke(new Action(()=> dg.Items.Refresh()));
                });
                thread.Start();
            }
        }

     

    Sincerely, 


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2011年8月22日 17:41
    版主
  • to Bob Bao:

    太棒了,哈哈!

    试了下终于可以了。

    只是不知道怎么回事,执行到Invoke就死在这句上,改成BeginInvoke就可以了

    dg.Dispatcher.Invoke(new Action(()=> dg.Items.Refresh()));
    改成
    dg.Dispatcher.BeginInvoke(new Action(()=> dg.Items.Refresh()));

    我调试的时候用Invoke是可以的,但实现用到我那个线程的时候只能用,就用异步的BeginInvoke好了,不知道怎么回事。

    解决了,多谢,哈哈

     


    2011年8月23日 8:03
  • to KA_KA07

    我的类里面有很多个DataTable,每个DataTable对应不同的DataGrid。
    这样,还是不知道怎么做,呵呵

    谢谢你的回答,先用Bob Bao说的方法了 ^_^

    2011年8月23日 8:09