具有任意列和行的 Wpf DataGrid 数据绑定
Wpf DataGrid data binding with arbitrary colums and rows
对于我的一个项目,我正在尝试从文件(例如 *.csv)加载数据并将其显示在 DataGrid
中。文件的格式没有给出。例如,列数和行数因文件而异。因此,任务是将通用数据 table 加载到程序中并在 DataGrid
.
中查看它
我的第一次尝试是使用 DataTable
并以某种方式将其绑定到 DataGrid
。然而,经过一段时间的搜索,我了解到使用 DataTable
并不是最好的解决方案,应该改用数据模型。不幸的是,网络中的大多数示例都使用具有固定数据列的数据。此外,我无法使 DataGrid
和 DataTable
之间的绑定正常工作。能够对两端的数据进行更改将是非常好的:DataGrid
(如用户所愿)和底层数据结构(如代码所愿)。
拜托,有人可以解释一下,为什么不应该使用 DataTable
吗?应该使用什么来实现上述行为?
数据加载本身应该不是问题,但如何以灵活的方式存储、处理和绑定数据table是DataGrid
。
以下代码有效。但是,它似乎不响应对数据的任何更改,例如添加行或列,但 DataTable
包含正确的数据。
<DataGrid x:Name="myDataGrid" AutoGenerateColumns="True"
x:Name="myDataGrid1" AutoGenerateColumns="True" ItemsSource="{Binding myDataTable}" />
DataTable myDataTable { get; private set; }
...
myDataGrid.DataContext = this;
LoadData();
myDataGrid.ItemsSource = myDataTable.DefaultView;
非常感谢任何帮助!提前致谢!
尝试将 ItemsSource 设置为 BindingListCollectionView。到目前为止,我一直很幸运。我使用这些扩展方法:
public static BindingListCollectionView GetCollectionView(this DataTable table)
{
return new BindingListCollectionView(table.DefaultView);
}
public static DataTable GetDataTable(this BindingListCollectionView view)
{
var dataView = view.SourceCollection as DataView;
if (dataView != null)
{
return dataView.Table;
}
return null;
}
在您的代码中使用它,例如:
myDataGrid.ItemsSource = myDataTable.GetCollectionView();
和
var view = myDataGrid.ItemsSource as BindingListCollectionView;
var table = view.GetDataTable();
您应该能够像往常一样使用 DataTable 对象,并保留网格所做的所有更改。
经过进一步调查,我找到了适合我的解决方案。然而,感觉有点奇怪而且不是最好的解决方案,但我可以接受。再次感谢您的努力,对我帮助很大。
这个想法是,只要一列有 added/removed 由 GridView
的 DataContext
刷新 GridView
。我使用 DataTable.Columns.CollectionChanged
事件来实现此行为。
现在,几乎一切都很好。唯一剩下的问题是当用户通过 GridView
添加新行时(当单击最后一个空行并输入数据时)。然而,这只是一个小问题,因为绑定 DataTable
已正确更新,只有第二个 GridView
(在下面的示例中)未正确显示。
问题仍然存在,使用 DataTable
是否是最佳解决方案。如果DataContext
的重置导致大数据集的问题需要测试...
这里是关于这个问题和解决方案的完整示例:
XAML:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplicationTest.MainWindow"
Title="DataGrid and DataTable Test" Height="500" Width="800">
<DockPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
<Button Click="ButtonRefresh_Click">Refresh Grids</Button>
<Button Click="ButtonAddRow_Click">Add Row</Button>
<Button Click="ButtonAddCol_Click">Add Col</Button>
<Button Click="ButtonEdit_Click">Edit Randomly</Button>
</StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<DataGrid x:Name="myDataGrid0" Grid.Column="0" AutoGenerateColumns="True" ItemsSource="{Binding}" />
<DataGrid x:Name="myDataGrid1" Grid.Column="1" AutoGenerateColumns="True" ItemsSource="{Binding}" />
</Grid>
</DockPanel>
</Window>
隐藏代码:
using System;
using System.Data;
using System.Windows;
namespace WpfApplicationTest
{
public partial class MainWindow : Window
{
private DataTable myDataTable;
private Random myRandom = new Random();
public MainWindow()
{
InitializeComponent();
myDataTable = new DataTable();
// create some datatable
myDataTable.Columns.Add("First");
myDataTable.Columns.Add("Second");
myDataTable.Rows.Add(myRandom.Next(100).ToString(), myRandom.Next(100).ToString());
myDataTable.Rows.Add(myRandom.Next(100).ToString(), myRandom.Next(100).ToString());
myDataGrid0.DataContext = myDataTable;
myDataGrid1.DataContext = myDataTable;
myDataTable.Columns.CollectionChanged += Columns_CollectionChanged;
}
private void Columns_CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs e)
{
// refresh the grid views by reseting the data context after the columns have changed
myDataGrid0.DataContext = null;
myDataGrid0.DataContext = myDataTable;
myDataGrid1.DataContext = null;
myDataGrid1.DataContext = myDataTable;
}
private void ButtonRefresh_Click(object sender, RoutedEventArgs e)
{
// refresh the grid views by reseting the data context
myDataGrid0.DataContext = null;
myDataGrid0.DataContext = myDataTable;
myDataGrid1.DataContext = null;
myDataGrid1.DataContext = myDataTable;
}
private void ButtonAddRow_Click(object sender, RoutedEventArgs e)
{
// add rows with random content
DataRow row = myDataTable.NewRow();
for (int i = 0; i < row.ItemArray.Length; ++i)
row[i] = myRandom.Next(100);
myDataTable.Rows.Add(row);
}
int rowcount = 0;
private void ButtonAddCol_Click(object sender, RoutedEventArgs e)
{
// add columns with random content
myDataTable.Columns.Add("New" + rowcount.ToString());
foreach (DataRow row in myDataTable.Rows)
row["New" + rowcount.ToString()] = myRandom.Next(100);
++rowcount;
}
private void ButtonEdit_Click(object sender, RoutedEventArgs e)
{
// randomly change some data table values
myDataTable.Rows[myRandom.Next(myDataTable.Rows.Count)][myRandom.Next(myDataTable.Columns.Count)] = myRandom.Next(100);
}
}
}
对于我的一个项目,我正在尝试从文件(例如 *.csv)加载数据并将其显示在 DataGrid
中。文件的格式没有给出。例如,列数和行数因文件而异。因此,任务是将通用数据 table 加载到程序中并在 DataGrid
.
我的第一次尝试是使用 DataTable
并以某种方式将其绑定到 DataGrid
。然而,经过一段时间的搜索,我了解到使用 DataTable
并不是最好的解决方案,应该改用数据模型。不幸的是,网络中的大多数示例都使用具有固定数据列的数据。此外,我无法使 DataGrid
和 DataTable
之间的绑定正常工作。能够对两端的数据进行更改将是非常好的:DataGrid
(如用户所愿)和底层数据结构(如代码所愿)。
拜托,有人可以解释一下,为什么不应该使用 DataTable
吗?应该使用什么来实现上述行为?
数据加载本身应该不是问题,但如何以灵活的方式存储、处理和绑定数据table是DataGrid
。
以下代码有效。但是,它似乎不响应对数据的任何更改,例如添加行或列,但 DataTable
包含正确的数据。
<DataGrid x:Name="myDataGrid" AutoGenerateColumns="True"
x:Name="myDataGrid1" AutoGenerateColumns="True" ItemsSource="{Binding myDataTable}" />
DataTable myDataTable { get; private set; }
...
myDataGrid.DataContext = this;
LoadData();
myDataGrid.ItemsSource = myDataTable.DefaultView;
非常感谢任何帮助!提前致谢!
尝试将 ItemsSource 设置为 BindingListCollectionView。到目前为止,我一直很幸运。我使用这些扩展方法:
public static BindingListCollectionView GetCollectionView(this DataTable table)
{
return new BindingListCollectionView(table.DefaultView);
}
public static DataTable GetDataTable(this BindingListCollectionView view)
{
var dataView = view.SourceCollection as DataView;
if (dataView != null)
{
return dataView.Table;
}
return null;
}
在您的代码中使用它,例如:
myDataGrid.ItemsSource = myDataTable.GetCollectionView();
和
var view = myDataGrid.ItemsSource as BindingListCollectionView;
var table = view.GetDataTable();
您应该能够像往常一样使用 DataTable 对象,并保留网格所做的所有更改。
经过进一步调查,我找到了适合我的解决方案。然而,感觉有点奇怪而且不是最好的解决方案,但我可以接受。再次感谢您的努力,对我帮助很大。
这个想法是,只要一列有 added/removed 由 GridView
的 DataContext
刷新 GridView
。我使用 DataTable.Columns.CollectionChanged
事件来实现此行为。
现在,几乎一切都很好。唯一剩下的问题是当用户通过 GridView
添加新行时(当单击最后一个空行并输入数据时)。然而,这只是一个小问题,因为绑定 DataTable
已正确更新,只有第二个 GridView
(在下面的示例中)未正确显示。
问题仍然存在,使用 DataTable
是否是最佳解决方案。如果DataContext
的重置导致大数据集的问题需要测试...
这里是关于这个问题和解决方案的完整示例:
XAML:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplicationTest.MainWindow"
Title="DataGrid and DataTable Test" Height="500" Width="800">
<DockPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
<Button Click="ButtonRefresh_Click">Refresh Grids</Button>
<Button Click="ButtonAddRow_Click">Add Row</Button>
<Button Click="ButtonAddCol_Click">Add Col</Button>
<Button Click="ButtonEdit_Click">Edit Randomly</Button>
</StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<DataGrid x:Name="myDataGrid0" Grid.Column="0" AutoGenerateColumns="True" ItemsSource="{Binding}" />
<DataGrid x:Name="myDataGrid1" Grid.Column="1" AutoGenerateColumns="True" ItemsSource="{Binding}" />
</Grid>
</DockPanel>
</Window>
隐藏代码:
using System;
using System.Data;
using System.Windows;
namespace WpfApplicationTest
{
public partial class MainWindow : Window
{
private DataTable myDataTable;
private Random myRandom = new Random();
public MainWindow()
{
InitializeComponent();
myDataTable = new DataTable();
// create some datatable
myDataTable.Columns.Add("First");
myDataTable.Columns.Add("Second");
myDataTable.Rows.Add(myRandom.Next(100).ToString(), myRandom.Next(100).ToString());
myDataTable.Rows.Add(myRandom.Next(100).ToString(), myRandom.Next(100).ToString());
myDataGrid0.DataContext = myDataTable;
myDataGrid1.DataContext = myDataTable;
myDataTable.Columns.CollectionChanged += Columns_CollectionChanged;
}
private void Columns_CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs e)
{
// refresh the grid views by reseting the data context after the columns have changed
myDataGrid0.DataContext = null;
myDataGrid0.DataContext = myDataTable;
myDataGrid1.DataContext = null;
myDataGrid1.DataContext = myDataTable;
}
private void ButtonRefresh_Click(object sender, RoutedEventArgs e)
{
// refresh the grid views by reseting the data context
myDataGrid0.DataContext = null;
myDataGrid0.DataContext = myDataTable;
myDataGrid1.DataContext = null;
myDataGrid1.DataContext = myDataTable;
}
private void ButtonAddRow_Click(object sender, RoutedEventArgs e)
{
// add rows with random content
DataRow row = myDataTable.NewRow();
for (int i = 0; i < row.ItemArray.Length; ++i)
row[i] = myRandom.Next(100);
myDataTable.Rows.Add(row);
}
int rowcount = 0;
private void ButtonAddCol_Click(object sender, RoutedEventArgs e)
{
// add columns with random content
myDataTable.Columns.Add("New" + rowcount.ToString());
foreach (DataRow row in myDataTable.Rows)
row["New" + rowcount.ToString()] = myRandom.Next(100);
++rowcount;
}
private void ButtonEdit_Click(object sender, RoutedEventArgs e)
{
// randomly change some data table values
myDataTable.Rows[myRandom.Next(myDataTable.Rows.Count)][myRandom.Next(myDataTable.Columns.Count)] = myRandom.Next(100);
}
}
}