Finally, I found a solution that does not have the above problems.
I made a strategic mistake. When we connect DataGrid.ItemsSource
to DataView
or CollectionViewSource.Source
for the first time, the values of DataView
and CollectionViewSource.Source
change automatically with each change (like insert, edit, and delete), so the code that connects DataGrid.ItemsSource
to DataView
or CollectionViewSource
does not need to be rewritten.
Look carefully at the image below to get a better understanding of how this feature works. When DataGrid.ItemsSource
changes in the first window, DataGrid.ItemsSource
in the second window also changes.
I tested the following codes in different situations, using nearly the entire capacity of the MS Access
database.
The conditions are as follows:
Number of records = 252,500
Database size = 2,094,128 KB (database size without any rows = 604 KB)
Size of each row = (Database size - database size without any rows) / Number of records => about 8.291184 KB (Of course, if I haven't made a mistake)
Hardware and software used in the test = Acer Aspire 5750G Laptop (Core i5 2nd Gen/4 GB RAM/Win7-x64)
, Visual Studio 2017
, .NET Framework 4.5.2
, WPF
XAML:
<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 DataView BDV = new DataView();
object[] BookCodeSelectedItems = null;
object[] ISBN_Value = null;
public MainWindow()
{
InitializeComponent();
BDV = BookDataGrid.ItemsSource as DataView; //In "XAML" or "C#," binding is only necessary once
}
private void DataGridDeleteMenu_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
switch (BookDataGrid.SelectedItems.Count > 0)
{
case false:
MessageWindow MW = new MessageWindow();
MW.YesButton.Visibility = Visibility.Hidden;
MW.NoButton.Visibility = Visibility.Hidden;
MW.MessageLabel.Margin = new Thickness(0, 110, 0, 0);
MW.MessageLabel.HorizontalAlignment = HorizontalAlignment.Center;
MW.Image.Source = GetImageFromBytes(System.IO.File.ReadAllBytes(System.Windows.Forms.Application.StartupPath + @"\Images\Warning.bin"));
MW.MessageTextBlock.Text = "No rows selected";
MW.OKButton.Content = "OK";
MW.ShowDialog();
break;
default:
var stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
List<object> Row = new List<object>();
//Additionally, I tested the "AddRange" function of the "List<>" and it appeared to be one second slower. I mean "Row.AddRange(BookDataGrid.Items.Cast<object>().ToList());"
for (int i = 0; i < BookDataGrid.Items.Count; i++)
{
Row.Add(BookDataGrid.Items[i]);
}
BookCodeSelectedItems = new object[BookDataGrid.SelectedItems.Count]; //I need this for further calculations
ISBN_Value = new string[BookDataGrid.SelectedItems.Count]; //I need this for further calculations
int j = 0;
foreach (DataRowView DRV in BookDataGrid.SelectedItems)
{
BookCodeSelectedItems[j] = BDV.Table.Rows[BDV.Table.Rows.IndexOf(DRV.Row)][3];
ISBN_Value[j] = BDV.Table.Rows[BDV.Table.Rows.IndexOf(DRV.Row)][14];
}
for (int i = 0; i < BookDataGrid.SelectedItems.Count; i++)
{
Row.Remove(BookDataGrid.SelectedItems[i]);
}
BookDataGrid.ItemsSource = Row;
stopwatch.Stop();
MessageBox.Show("Total seconds: " + stopwatch.Elapsed.TotalSeconds.ToString());
break;
}
}
Output:
I also tried the following code, it was very slow. It took more than 10 minutes and I stopped the app. But List<> took about 16 seconds.
var stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
for (int i = BookDataGrid.SelectedItems.Count - 1; i >= 0; i--)
{
BDV.Table.Rows.Remove(((DataRowView)BookDataGrid.SelectedItems[i]).Row);
}
BookDataGrid.ItemsSource = BDV.Table.DefaultView;
stopwatch.Stop();
MessageBox.Show("Total seconds: " + stopwatch.Elapsed.TotalSeconds.ToString());
I hope this is the most comprehensive solution for converting DataGrid.ItemsSource
to DataTable
, DataView
or another type of data source.
Thank you for your attention.