DataGrid生成空行 [英] DataGrid generates empty rows

查看:96
本文介绍了DataGrid生成空行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



数据网格左边的情况,右边的列表框案例



(列表框只是例如数据绑定和显示;我不希望显示数据像在这个列表框中,但像数据网格(如果它按照我的预期工作 - 不如图片中的情况) - 表的列标题)





嗯,我准备好了代码,您可以复制并粘贴以重建问题:



C#

 使用System.Collections.ObjectModel; 
使用System.Collections.Specialized;
使用System.Threading;
使用System.Windows;

命名空间WpfApplication1
{
public class MyClass
{
public int Integer {get;组; }
public string Str {get;组; }
}

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

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(
对象发件人,
NotifyCollectionChangedEventArgs e)
{
Dispatcher.Invoke(
()= >
{
foreach(e.NewItems中的var item)
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 =MainWindowHeight =350Width =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>


解决方案

这是你想要的吗?



C#

 命名空间WpfApplication1 
{
public class MyClass
{
public int Integer {get;组; }
public string Str {get;组; }
}

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

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 =MainWindowHeight =350Width = 525>

< Grid>
< DataGrid Name =dataGridItemsSource ={Binding MyCollection}>
< DataGrid.ItemTemplate>
< DataTemplate>
< StackPanel>
< TextBlock Text ={Binding Integer}/>
< TextBlock Text ={Binding Str}/>
< / StackPanel>
< / DataTemplate>
< /DataGrid.ItemTemplate>
< / DataGrid>

< / Grid>
< / Window>

其他方法是使用另一个列表:

  public partial class MainWindow:Window 
{
private List< MyClass> _我的收藏;
public ObservableCollection< MyClass> MyCollection {get;组; }

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

第二次编辑与你的例子:

 命名空间WpfApplication1 
{
public class MyClass
{
public int Integer {get;组; }
public string Str {get;组; }
}

public partial class MainWindow:Window
{
private ObservableCollection< MyClass> _我的收藏;
public ObservableCollection< MyClass> MyCollection {get;组; }

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(e.NewItems中的var item)
MyCollection.Add((MyClass)项);
}));
}
}
}


I have two threads - let's name them Calc thread and UI thread. Inside the Calc thread I refreshes an ObservableCollection. I also have a handler for the CollectionCHanged event of the ObservableCollection. As I know, the handler executes within the same thread that raises the CollectionChanged event - so that is the same thread that refreshes the ObservableCollection in my case. So, to refresh UI I can't use bindings directly as in single-threaded application - UI must be refreshed manually through Dispatcher. But when I use DataGrid in the UI I get the empty rows instead of any data, and when I use ListBox, for example, the appropriate data is showed:

data grid case to the left, list box case to the right

(list box is just for example that the data binds and shows; I don't want the data to be showed like in this list box, but like in data grid (if it worked as I expect - not as in case on the picture) - table with column titles)

Well, I prepared the code, which you can copy and paste to reconstruct the problem:

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 (just comment/uncomment the list box case and the data grid case):

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

解决方案

Is this what you want?

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>

Other method is to use another 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]);
            }
        }
    }

Second edit with you example:

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

这篇关于DataGrid生成空行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆