我希望删除用户选择的行。
据我所知,DataGrid.ItemsSource
不能直接使用DataGrid.Items.Remove()
方法删除,所以我必须将DataGrid.ItemsSource
转换为DataTable
并使用DataGrid.ItemsSource = DataTable.DefaultView
属性。为了将DataGrid.ItemsSource
转换为DataTable
,我在各种情况下尝试了以下解决方案,每种方案都有优缺点(我花了几个月的时间进行研究和测试):
第一个解决方案(我自己做的):
XAML:
<DataGrid x:Name="BookDataGrid" EnableRowVirtualization="True" VirtualizingPanel.ScrollUnit="Pixel" CanUserAddRows="False" BeginningEdit="BookDataGrid_BeginningEdit" RowEditEnding="BookDataGrid_RowEditEnding" HeadersVisibility="Column" AutoGenerateColumns="False" ItemsSource="{Binding}" HorizontalAlignment="Left" VerticalAlignment="Top" Height="386" Width="486" Margin="0">
<DataGrid.Columns>
<DataGridTextColumn x:Name="BookName" Binding="{Binding BookName}" Width="SizeToHeader">
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="AcceptsReturn" Value="True"/>
<Setter Property="ContextMenu" Value="{StaticResource CustomContextMenu}"/>
<Setter Property="TextWrapping" Value="WrapWithOverflow"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn x:Name="BookImage" Width="SizeToHeader">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image x:Name="BookImg" Source="{Binding BookImage}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
C#:
public byte[] ImageSourceToBytes(BitmapEncoder BitEncoder, ImageSource ImgSource)
{
byte[] Bytes = null;
switch ((ImgSource as BitmapSource) != null)
{
case true:
BitEncoder.Frames.Add(BitmapFrame.Create((ImgSource as BitmapSource)));
using (var Stream = new System.IO.MemoryStream())
{
BitEncoder.Save(Stream);
Bytes = Stream.ToArray();
}
break;
}
return Bytes;
}
public DataTable DataGridToDataTable(DataGrid DG, DataTable DT, byte NumberOfColumns, byte VisualColumnIndex, string ControlName)
{
for (int i = 0; i < DG.Items.Count; i++)
{
DT.Rows.Add(DG.Items[i]);
}
for (int i = 0; i < DG.Items.Count; i++)
{
for (byte j = 0; j < NumberOfColumns; j++)
{
switch (j == VisualColumnIndex)
{
case true:
FrameworkElement FE = DG.Columns[j].GetCellContent((DataGridRow)DG.ItemContainerGenerator.ContainerFromIndex(i));
Image Img = new Image() { Source = ((((DataGridTemplateColumn)DG.Columns[j]).CellTemplate.FindName(ControlName, FE) as Image).Source) };
DT.Rows[i][j] = ImageSourceToBytes(new PngBitmapEncoder(), Img.Source);
break;
default:
DG.ScrollIntoView((DataRowView)DG.Items[i]);
DT.Rows[i][j] = ((DG.Columns[j].GetCellContent(((DataGridRow)DG.ItemContainerGenerator.ContainerFromIndex(i)))) as TextBlock).Text;
break;
}
}
}
return DT;
}
优点:即使其中一个数据行是Image
类型,此方法也不会发生错误。
缺点:当EnableRowVirtualization
属性设置为True
时,必须使用方法DG.ScrollIntoView((DataRowView)DG.Items[i])
;对于大量的行,这种方法非常慢(例如,如果我们有20,000行,可能需要一个小时或更长时间)。
第二个解决方案(我自己做的):
XAML:
<DataGrid x:Name="BookDataGrid" EnableRowVirtualization="False" CanUserAddRows="False" BeginningEdit="BookDataGrid_BeginningEdit" RowEditEnding="BookDataGrid_RowEditEnding" HeadersVisibility="Column" AutoGenerateColumns="False" ItemsSource="{Binding}" HorizontalAlignment="Left" VerticalAlignment="Top" Height="386" Width="486" Margin="0">
<DataGrid.Columns>
<DataGridTextColumn x:Name="BookName" Binding="{Binding BookName}" Width="SizeToHeader">
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="AcceptsReturn" Value="True"/>
<Setter Property="ContextMenu" Value="{StaticResource CustomContextMenu}"/>
<Setter Property="TextWrapping" Value="WrapWithOverflow"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn x:Name="BookImage" Width="SizeToHeader">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image x:Name="BookImg" Source="{Binding BookImage}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
x1米15英寸
public DataTable DataGridToDataTable(DataGrid DG, DataTable DT, byte NumberOfColumns, byte VisualColumnIndex, string ControlName)
{
for (int i = 0; i < DG.Items.Count; i++)
{
DT.Rows.Add(DG.Items[i]);
}
for (int i = 0; i < DG.Items.Count; i++)
{
for (byte j = 0; j < NumberOfColumns; j++)
{
switch (j == VisualColumnIndex)
{
case true:
FrameworkElement FE = DG.Columns[j].GetCellContent((DataGridRow)DG.ItemContainerGenerator.ContainerFromIndex(i));
Image Img = new Image() { Source = ((((DataGridTemplateColumn)DG.Columns[j]).CellTemplate.FindName(ControlName, FE) as Image).Source) };
DT.Rows[i][j] = ImageSourceToBytes(new PngBitmapEncoder(), Img.Source);
break;
default:
DT.Rows[i][j] = ((DG.Columns[j].GetCellContent(((DataGridRow)DG.ItemContainerGenerator.ContainerFromIndex(i)))) as TextBlock).Text;
break;
}
}
}
return DT;
}
优点:此解决方案将DataGrid.ItemsSource
转换为DataTable
的速度比第一个解决方案快,因为在此情况下EnableRowVirtualization
等于False
。
缺点:此解决方案消耗大量内存;例如,如果我们有100,000行,数据库表的大小为2GB,它将占用2GB的RAM
,并且可能发生RAM
空间错误。x1c 0d1x
第三种解决方案:
XAML:
<DataGrid x:Name="BookDataGrid" EnableRowVirtualization="True" VirtualizingPanel.ScrollUnit="Pixel" CanUserAddRows="False" BeginningEdit="BookDataGrid_BeginningEdit" RowEditEnding="BookDataGrid_RowEditEnding" HeadersVisibility="Column" AutoGenerateColumns="False" ItemsSource="{Binding}" HorizontalAlignment="Left" VerticalAlignment="Top" Height="386" Width="486" Margin="0">
<DataGrid.Columns>
<DataGridTextColumn x:Name="BookName" Binding="{Binding BookName}" Width="SizeToHeader">
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="AcceptsReturn" Value="True"/>
<Setter Property="ContextMenu" Value="{StaticResource CustomContextMenu}"/>
<Setter Property="TextWrapping" Value="WrapWithOverflow"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn x:Name="BookImage" Width="SizeToHeader">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image x:Name="BookImg" Source="{Binding BookImage}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
C#:
uint[] BookCodeSelectedItems = null; //I need this for further calculations
private void DataGridDeleteMenu_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
switch (BookDataGrid.SelectedItems.Count > 0)
{
case true:
List<object> DefaultRow = new List<object>();
DataTable BDT = ((DataView)BookDataGrid.ItemsSource).ToTable(); //The first time the event is executed, no error occurs, but the second time the error occurs on this line
for (int i = 0; i < BookDataGrid.Items.Count; i++)
{
DefaultRow.Add(BookDataGrid.Items[i]);
}
BookCodeSelectedItems = new uint[BookDataGrid.SelectedItems.Count];
for (int i = 0; i < BookDataGrid.SelectedItems.Count; i++)
{
BookCodeSelectedItems[i] = uint.Parse(BDT.Rows[i][3].ToString());
DefaultRow.Remove(BookDataGrid.SelectedItems[i]);
}
BookDataGrid.ItemsSource = DefaultRow;
break;
}
}
优点:第一次触发DataGridDeleteMenu PreviewMouseLeftButtonDown
事件时,DataGrid.ItemsSource
会在此方法中快速变更为DataTable
。
缺点:但是,重新运行事件时,会发生System.InvalidCastException: 'Unable to cast object of type 'System.Collections.Generic.List 1[System.Object]' to type 'System.Data.DataView'.'
错误。
我测试了以下代码(我放置了一个断点):
var dataType = BookDataGrid.ItemsSource.GetType().BaseType;
结果:
第一次
值={名称=“按值组件封送处理”全名=“系统.组件模型.按值组件封送处理”}
**类型=**系统.类型{系统.运行时类型}
第二次
值={名称=“对象”全名=“系统.对象”}
**类型=**系统.类型{系统.运行时类型}
似乎只有 * 值 * 发生了变化。
"但为什么"
"解决方法是什么"
我使用以下工具:
x1米28英寸1x
<Window.Resources>
<local:DatabaseDataSet x:Key="Database_DataSet"/>
<CollectionViewSource x:Key="BookTableViewSource" Source="{Binding BookTable, Source={StaticResource Database_DataSet}}"/>
<CollectionViewSource x:Key="MemberTableViewSource" Source="{Binding MemberTable, Source={StaticResource Database_DataSet}}"/>
</Window.Resources>
<Grid DataContext="{StaticResource BookTableViewSource}" Width="486" Height="386">
<DataGrid x:Name="BookDataGrid" HeadersVisibility="Column" EnableRowVirtualization="True" VirtualizingPanel.ScrollUnit="Pixel" CanUserAddRows="False" AutoGenerateColumns="False" ItemsSource="{Binding}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="486" Height="386" Margin="0">
<DataGrid.Columns>
<DataGridTextColumn x:Name="BookName" Binding="{Binding BookName}" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="Publisher" Binding="{Binding Publisher}" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="Category" Binding="{Binding Category}" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="BookCode" Binding="{Binding BookCode}" IsReadOnly="True" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="Inventory" Binding="{Binding Inventory}" IsReadOnly="True" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="ReleaseDate" Binding="{Binding ReleaseDate}" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="DateTaken" Binding="{Binding DateTaken}" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="ReturnDate" Binding="{Binding ReturnDate}" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="RecipientName" Binding="{Binding RecipientName}" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="Language" Binding="{Binding BookLanguage}" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="Length" Binding="{Binding Length}" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="Form" Binding="{Binding Form}" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="Translator" Binding="{Binding Translator}" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="Narrator" Binding="{Binding Narrator}" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="ISBN" Binding="{Binding ISBN}" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="Location" Binding="{Binding Location}" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="Price" Binding="{Binding Price}" Width="SizeToHeader"/>
<DataGridTemplateColumn x:Name="BookImage" Width="SizeToHeader">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image x:Name="BookImg" Source="{Binding BookImage}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
C#:
public void BookDatagridRefresh()
{
DatabaseDataSet Database_DataSet = ((DatabaseDataSet)TryFindResource("Database_DataSet"));
DatabaseDataSetTableAdapters.BookTableTableAdapter BookTable_TableAdapter = new DatabaseDataSetTableAdapters.BookTableTableAdapter();
BookTable_TableAdapter.Fill(Database_DataSet.BookTable);
BookDataGrid.ItemsSource = Database_DataSet.Tables["BookTable"].DefaultView;
}
x一个月30个月1个月x一个月31个月1个月x一个月32个月1个月
谢谢大家的关注。
1条答案
按热度按时间wydwbb8l1#
最后,我找到了一个不存在上述问题的解决方案。
我犯了一个策略性错误,当我们第一次将
DataGrid.ItemsSource
连接到DataView
或CollectionViewSource.Source
时,DataView
和CollectionViewSource.Source
的值会随着每次更改(如插入、编辑和删除)而自动更改,因此连接DataGrid.ItemsSource
到DataView
或CollectionViewSource.Source
的代码不需要重写。请仔细查看下图,以更好地了解此功能的工作原理。当第一个窗口中的
DataGrid.ItemsSource
发生变化时,第二个窗口中的DataGrid.ItemsSource
也会发生变化。x1c 0d1x我在不同的情况下测试了以下代码,几乎使用了
MS Access
数据库的全部容量。条件如下:
记录数= 252,500
数据库大小= 2,094,128 KB(没有任何行的数据库大小= 604 KB)
每行的大小=(
Database
size -没有任何行的数据库大小)/记录数=〉大约8.291184 KB(当然,如果我没有弄错的话)测试中使用的硬件和软件=
Acer Aspire 5750G Laptop (Core i5 2nd Gen/4 GB RAM/Win7-x64)
、Visual Studio 2017
、.NET Framework 4.5.2
、WPF
XAML:
个C#:
个输出:
我也尝试了下面的代码,它非常慢。它花了超过10分钟,我停止了应用程序。但
List<>
花了大约16秒。我希望这是将
DataGrid.ItemsSource
转换为DataTable
、DataView
或其他类型的数据源的最全面的解决方案。谢谢大家的关注。