【问题标题】:DataGrid generates empty rowsDataGrid 生成空行
【发布时间】:2014-12-30 21:53:53
【问题描述】:

我有两个线程 - 我们将它们命名为 Calc 线程和 UI 线程。在 Calc 线程中,我刷新了一个 ObservableCollection。我还有一个 ObservableCollection 的 CollectionCHanged 事件的处理程序。据我所知,处理程序在引发 CollectionChanged 事件的同一线程中执行 - 因此在我的情况下,这就是刷新 ObservableCollection 的同一线程。因此,要刷新 UI,我不能像在单线程应用程序中那样直接使用绑定 - UI 必须通过 Dispatcher 手动刷新。但是当我在 UI 中使用 DataGrid 时,我得到的是空行而不是任何数据,例如,当我使用 ListBox 时,会显示适当的数据:

左侧是数据网格大小写,右侧是列表框大小写

(列表框只是数据绑定和显示的示例;我不希望数据像在此列表框中那样显示,而是像在数据网格中那样显示(如果它按我预期的那样工作 - 不是以防万一在图片上)- 带有列标题的表格)

嗯,我准备了代码,你可以复制粘贴来重构问题:

C#

using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Threading;
using System.Windows;

namespace WpfApplication1
{
    public class MyClass
    {
        public int Integer { get; set; }
        public string Str { get; set; }
    }    

    public partial class MainWindow : Window
    {
        public ObservableCollection<MyClass> MyCollection { get; set; }

        public MainWindow()
        {
            InitializeComponent();    

            MyCollection = new ObservableCollection<MyClass>();
            MyCollection.CollectionChanged += MyCollection_CollectionChanged;

            Thread t = new Thread(new ThreadStart(() =>
                {
                    for (int i = 0; i < 10; i++)
                    {
                        MyCollection.Add(new MyClass() 
                        {
                            Integer = i, 
                            Str = "String" + i
                        });
                        Thread.Sleep(500);
                    }
                }));

            t.Start();
        }

        void MyCollection_CollectionChanged(
            object sender, 
            NotifyCollectionChangedEventArgs e)
        {
            Dispatcher.Invoke(
                () =>
                {
                    foreach (var item in e.NewItems)
                        dataGrid.Items.Add((MyClass)item);
                });
        }
    }
}

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>

    <!--<ListBox Name="dataGrid">
      <ListBox.ItemTemplate>
        <DataTemplate>
          <StackPanel>
            <TextBlock Text="{Binding Path=Integer}" />
            <TextBlock Text="{Binding Path=Str}" />
          </StackPanel>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>-->

    <DataGrid Name="dataGrid">
      <DataGrid.ItemTemplate>
        <DataTemplate>
          <StackPanel>
            <TextBlock Text="{Binding Path=Integer}" />
            <TextBlock Text="{Binding Path=Str}" />
          </StackPanel>
        </DataTemplate>
      </DataGrid.ItemTemplate>
    </DataGrid>

  </Grid>
</Window>

【问题讨论】:

  • DataGrid 是一个非常复杂的控件。它的ItemTemplate 不能正常工作(我们很少使用它)。我不认为我们可以改变 DataGrid 的设计布局,就像 grid 应该有一些列(至少 1 个)。在这种情况下,您没有为数据网格声明任何列,当将项目直接添加到 Items 属性时,AutoGenerateColumns 似乎不起作用。所以你看到的只是空行。尝试添加一些显式的DataGridTextColumn 并正确设置绑定,您会看到它有效。但是这里又看不到ItemTemplate的作用,在这里根本没用。
  • 如果你想在 ListBox 中呈现一些结果,你可以使用 1 DataGridTemplateColumn 然后编辑 CellTemplate

标签: c# wpf multithreading datagrid


【解决方案1】:

这是你想要的吗?

C#

namespace WpfApplication1
{
    public class MyClass
    {
        public int Integer { get; set; }
        public string Str { get; set; }
    }

    public partial class MainWindow : Window
    {
        public ObservableCollection<MyClass> MyCollection { get; set; }

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            MyCollection = new ObservableCollection<MyClass>();

            Thread t = new Thread(new ThreadStart(() =>
            {
                for (int i = 0; i < 10; i++)
                {
                   Dispatcher.Invoke(new Action(() =>
                   {
                    MyCollection.Add(new MyClass()
                    {
                        Integer = i,
                        Str = "String " + i
                    });
                   }));
                }
            }));

            t.Start();
        }
    }
}

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>
        <DataGrid Name="dataGrid" ItemsSource="{Binding MyCollection}">
            <DataGrid.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="{Binding Integer}" />
                        <TextBlock Text="{Binding Str}" />
                    </StackPanel>
                </DataTemplate>
            </DataGrid.ItemTemplate>
        </DataGrid>

    </Grid>
</Window>

其他方法是使用另一个List:

public partial class MainWindow : Window
    {
        private List<MyClass> _MyCollection;
        public ObservableCollection<MyClass> MyCollection { get; set; }

        private DispatcherTimer dispatcherTimer = new DispatcherTimer();

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            MyCollection = new ObservableCollection<MyClass>();
            _MyCollection = new List<MyClass>();
            dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 500);
            dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);

            Thread t = new Thread(new ThreadStart(() =>
            {
                for (int i = 0; i < 10; i++)
                {
                    _MyCollection.Add(new MyClass()
                    {
                        Integer = i,
                        Str = "String " + i
                    });
                    Thread.Sleep(500);
                }
            }));

            t.Start();
            dispatcherTimer.Start();
        }

        private void dispatcherTimer_Tick(object sender, EventArgs e)
        {
            if (_MyCollection.Count != MyCollection.Count)
            {
                MyCollection.Add(_MyCollection[_MyCollection.Count - 1]);
            }
        }
    }

与您一起进行第二次编辑示例:

namespace WpfApplication1
{
    public class MyClass
    {
        public int Integer { get; set; }
        public string Str { get; set; }
    }

    public partial class MainWindow : Window
    {
        private ObservableCollection<MyClass> _MyCollection;
        public ObservableCollection<MyClass> MyCollection { get; set; }

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            MyCollection = new ObservableCollection<MyClass>();
            _MyCollection = new ObservableCollection<MyClass>();
            _MyCollection.CollectionChanged += new NotifyCollectionChangedEventHandler(_MyCollection_CollectionChanged);

            Thread t = new Thread(new ThreadStart(() =>
            {
                for (int i = 0; i < 10; i++)
                {
                    _MyCollection.Add(new MyClass()
                    {
                        Integer = i,
                        Str = "String " + i
                    });
                    Thread.Sleep(500);
                }
            }));

            t.Start();
        }

        private void _MyCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            Dispatcher.Invoke(new Action(
                () =>
                {
                    foreach (var item in e.NewItems)
                        MyCollection.Add((MyClass)item);
                }));
        }
    }
}

【讨论】:

  • 是的,像这样。但是,如果示例变得更加复杂,并且我在线程“t”中有许多分层函数调用 - 我是否必须通过所有函数调用发送 Dispatcher 对象?
  • 我添加了另一种方法。在你的 GUI 中处理实现的方法还有很多,但我不知道你想在你的额外线程中做什么。您的额外线程“t”是否是关键线程并且不允许阻塞它?编辑:或者你制作第二个 ObservableCollection 并像你在你的例子中那样做。
  • 哦,我明白了。在最后一个示例中,我在后台线程中使用 _MyCollection 进行工作,并通过 Dispatcher 在 GUI 线程中刷新 MyCollection。而且 - 这很重要 - 我将 MyCollection 完全绑定到 GUI。我说的对吗?
  • 好吧,我尝试了我之前描述的方法 - 它有效!谢谢!最后一个例子更适合我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-26
  • 2015-07-20
  • 2016-09-08
  • 2013-08-28
  • 2013-11-29
  • 2022-01-22
  • 1970-01-01
相关资源
最近更新 更多