Sorry, my English is bad.
Help me please, I need set selected Item for table in RowDetailsTemplate.
Xaml code:
<Grid>
<DataGrid
Name="GvHeader"
AutoGenerateColumns="False"
HorizontalScrollBarVisibility="Auto"
CanUserDeleteRows="False"
CanUserAddRows="False"
CanUserResizeColumns="True"
CanUserReorderColumns="False"
CanUserResizeRows="False"
AllowDrop="False"
RowDetailsVisibilityMode="Collapsed"
IsReadOnly="True" ClipboardCopyMode="ExcludeHeader" SelectionMode="Single" SelectionChanged="GvHeader_SelectionChanged">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<EventSetter Event="MouseDoubleClick" Handler="HeaderRowDoubleClick"/>
<EventSetter Event="Unselected" Handler="HeaderRowUnselected"></EventSetter>
</Style>
</DataGrid.RowStyle>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid x:Name="GvLines"
AutoGenerateColumns="False"
HorizontalScrollBarVisibility="Auto"
CanUserDeleteRows="False"
CanUserAddRows="False"
CanUserResizeColumns="True"
CanUserReorderColumns="False"
CanUserResizeRows="False"
AllowDrop="False"
IsReadOnly="True" ClipboardCopyMode="ExcludeHeader" SelectionMode="Single" ItemsSource="{Binding Path=Lines}"
SelectionChanged="GvLines_SelectionChanged" MouseDoubleClick="GvLines_OnMouseDoubleClick">
<DataGrid.Columns>
<DataGridTemplateColumn x:Name="ShowLineStatus"
CanUserSort="False"
IsReadOnly="True"
Width="22">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Width="20" Name="pop" Height="20" ToolTip="Восстановить" Source="/Management;component/Images/key_enter.png">
</Image>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding CancelSatatus}" Value="False">
<Setter TargetName="pop" Property="Source" Value="/Management;component/Images/cancel.png"/>
<Setter TargetName="pop" Property="ToolTip" Value="Отменить"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn
x:Name="ColumnNumber"
Header="oooo1"
MinWidth="50" Width="Auto"
Binding="{Binding Path=line1,Mode=OneWay}"/>
<DataGridTextColumn
Header="ooo2"
MinWidth="20" Width="Auto"
CellSt yle="{StaticResource CellStyleCenter}"
Binding="{Binding Path=line2,Mode=OneWay}"/>
<DataGridTextColumn
Header="00003"
MinWidth="50" Width="Auto"
Binding="{Binding Path=line3,Mode=OneWay}"/>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
<DataGrid.Columns>
<DataGridTextColumn
x:Name="Number"
Header="Номер"
MinWidth="50" Width="Auto"
Binding="{Binding Path=head1,Mode=OneWay}"/>
<DataGridTextColumn
Header="Sss"
MinWidth="20" Width="Auto"
CellSt yle="{StaticResource CellStyleCenter}"
Binding="{Binding Path=head2,Mode=OneWay}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
Code C# for test:
var line1 = new List<ILine>
{
new Line("head11","h1line11", "h1line12", "h1line13", "h1line14"),
new Line("head11","h1line21", "h1line22", "h1line23", "h1line24"),
new Line("head11","h1line31", "h1line32", "h1line33", "h1line34"),
new Line("head11","h1line41", "h1line42", "h1line43", "h1line44")
};
var line2 = new List<ILine>
{
new Line("head21","h2line11", "h2line12", "h2line13", "h2line14"),
new Line("head21","h2line21", "h2line22", "h2line23", "h2line24"),
new Line("head21","h2line31", "h2line32", "h2line33", "h2line34")
};
var header = new List<Header>
{
new Header("head11", "head12", "head13", "head14"),
new Header("head21", "head22", "head23", "head24")
};
header[0].SetLines(line1);
header[1].SetLines(line2);
Global_H_items.AddRange(header);
GvHeader.ItemsSource = null;
GvHeader.ItemsSource = Global_H_items;
I need get access for table "GvLines"
like this (this my method for lock/unlock interface and after unblock I need set select Header item and Line item).
public void IsBlock(bool value, IHeader sel_header, ILine sel_line)
{
if (!this.Dispatcher.CheckAccess())
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action<Boolean, IHeader, ILine>(IsBlock), value, sel_header);
return;
}
GridMain.IsEnabled = value;
if (sel_header != null)
{
GvHeader.ScrollIntoView(sel_header);
GvHeader.SelectedItem = sel_header;
}
if (sel_line != null)
{
var row = (DataGridRow)GvHeader.ItemContainerGenerator.ContainerFromIndex(GvHeader.SelectedIndex);
row.DetailsVisibility = Visibility.Visible;
//Here I need take dgLines table and set selected item. I don't know how make this.
if (dgLines != null)
{
dgLines.ScrollIntoView(sel_line);
dgLines.SelectedItem = sel_line;
}
}
}
Related
I want the rows that the user selects to be deleted.
As far as I know, the DataGrid.ItemsSource cannot be removed directly using the DataGrid.Items.Remove() method, so I must convert the DataGrid.ItemsSource to a DataTable and use the DataGrid.ItemsSource = DataTable.DefaultView property.
To convert DataGrid.ItemsSource to DataTable, I tried the following solutions in various situations, and each has advantages and disadvantages (I spent several months researching and testing):
First solution (I made this by myself):
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;
}
Advantage: Even if one of the columns is of the Image type, this approach operates without an error.
Disadvantage: The method DG.ScrollIntoView((DataRowView)DG.Items[i]) must be used when the EnableRowVirtualization attribute is set to True; otherwise, a null error will occur. For large numbers of rows, this approach is incredibly sluggish (e.g., if we have 20,000 rows, it could take an hour or more).
Second solution (I made this by myself):
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>
C#:
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;
}
Advantage: This solution converts DataGrid.ItemsSource to DataTable faster than the first because EnableRowVirtualization is equal to False in this case.
Disadvantage: This solution consumes a lot of memory; for example, if we have 100,000 rows and the database table is 2GB in size, it will consume 2GB of RAM and a RAM space error may occur.
Third solution:
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;
}
}
Advantage: The DataGrid.ItemsSource is rapidly changed to DataTable in this approach when the DataGridDeleteMenu PreviewMouseLeftButtonDown event is fired for the first time.
Disadvantage: However, when the event is re-run, a System.InvalidCastException: 'Unable to cast object of type 'System.Collections.Generic.List 1[System.Object]' to type 'System.Data.DataView'.' error occurs.
I tested the following code (I put a breakpoint):
var dataType = BookDataGrid.ItemsSource.GetType().BaseType;
Result:
For the first time:
Value= {Name = "MarshalByValueComponent" FullName = "System.ComponentModel.MarshalByValueComponent"}
Type= System.Type {System.RuntimeType}
For the second time:
Value= {Name = "Object" FullName = "System.Object"}
Type= System.Type {System.RuntimeType}
It seems that only the value has changed.
But why?
And what is the solution?
I use the following tools:
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 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;
}
Visual Studio 2017 .NET Framework 4.5.2 WPF
Thank you for your attention.
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.Source 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.
im trying to select multiple rows/items from a datagrid
<DataGrid IsReadOnly="True" SelectionMode="Extended" SelectionUnit="FullRow" AutoGenerateColumns="False" ItemsSource="{Binding Source={StaticResource ClientiVS}}" SelectedItem="{Binding Path=ClienteSelezionato}" RowHeaderWidth="0" EnableColumnVirtualization="False" CanUserReorderColumns="False" EnableRowVirtualization="False" CanUserAddRows="false" ColumnWidth="*" FontFamily="Agency FB" FontSize="22" Name="DataGrid1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" SelectionChanged="DataGrid1_SelectionChanged" Grid.Row="1" Margin="30,10,30,30">
XAML DATAGRID CODE
<DataGrid.Columns>
<DataGridTextColumn Width="100" FontFamily="Agency FB" FontSize="20" Header="Nome" Binding="{Binding Nome}" ></DataGridTextColumn>
<DataGridTextColumn Width="100" FontFamily="Agency FB" FontSize="20" Header="Cognome" Binding="{Binding Cognome}" ></DataGridTextColumn>
<DataGridTextColumn Width="150" FontFamily="Agency FB" FontSize="20" Header="Indirizzo" Binding="{Binding Indirizzo}"></DataGridTextColumn>
<DataGridTextColumn Width="150" FontFamily="Agency FB" FontSize="20" Header="Telefono" Binding="{Binding NumeroTelefono}"></DataGridTextColumn>
<DataGridTextColumn Width="1*" FontFamily="Agency FB" FontSize="20" Header="Email" Binding="{Binding Email}"></DataGridTextColumn>
<DataGridCheckBoxColumn Header="Selezionato" Binding="{Binding ClienteSelezionato}"/>
</DataGrid.Columns>
</DataGrid>
DATAGRID SELECTIONCHANGED EVENT CODE:
public Cliente ClienteSelezionato { get; set; }
private ObservableCollection<Cliente> _ListaClientiSelezionati;
public ObservableCollection<Cliente> ListaClientiSelezionati { get { return _ListaClientiSelezionati; } set { _ListaClientiSelezionati = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ListaClientiSelezionati")); } }
private void DataGrid1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ClienteSelezionato != null )
{
ListaClientiSelezionati.Add(ClienteSelezionato);
}
}
The problem is that: if i select two times the same Row from the DataGrid("ClienteSelezionato") it will obviously insert into the list the same item twice. How can i fix it ?
I just want to implement the control that tells me if an item is already contained in the list ("ListaClientiSelezionati")
Thanks!
I am currently developing my first WPF application. I am using .NET Framework 4.8.
In my WPF application I use a DataGrid element. I define my DataGrid element like this:
<DataGrid IsReadOnly="True" HeadersVisibility="Column" AutoGenerateColumns="False" Grid.Row="1" Grid.Column="0" x:Name="MyDataGrid" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="Turquoise" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="BorderThickness" Value="0,0,1,2" />
<Setter Property="BorderBrush" Value="Black" />
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="id" Binding="{Binding id}" Visibility="Hidden" Width="*"/>
<DataGridTextColumn Header="fileId" Binding="{Binding fileId}" Visibility="Hidden" Width="*"/>
<DataGridTextColumn Header="val1" Binding="{Binding val1}" Width="*"/>
<DataGridTextColumn Header="val2" Binding="{Binding val2, StringFormat=\{0:dd.MM.yy\}}" Width="*"/>
<DataGridTextColumn Header="val3" Binding="{Binding val3, StringFormat=\{0:dd.MM.yy\}}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
Furthermore, I have included two Datepickers and a TextBox. I have defined these as follows:
<TextBox Grid.Row="0" IsReadOnly="False" Grid.Column="1" Name="FilterVal1" TextChanged="FilterDataGrid"/>
<DatePicker Grid.Row="1" Grid.Column="1" Name="FilterVal2" SelectedDateChanged="FilterDataGrid"/>
<DatePicker Grid.Row="2" Grid.Column="1" Name="FilterVal3" SelectedDateChanged="FilterDataGrid"/>
In my FilterDataGrid method I now filter the rows of the DataGrid as follows:
private void FilterDataGridKampagnen(object sender, object e)
{
using (var ctx = new myEntities())
{
var query = from k in ctx.MyTbl select k;
var lstMyTbl = query.ToList();
if(this.FilterVal1.Text.Trim().Length > 0)
{
lstMyTbl = lstMyTbl.Where(x => x.val1.ToLower().Contains(this.FilterVal1.Text.ToLower())).ToList();
}
if (FilterVal2.SelectedDate != null)
{
lstMyTbl = lstMyTbl.Where(x => x.val2 >= FilterVal2.SelectedDate).ToList();
}
if (FilterVal3.SelectedDate != null)
{
lstMyTbl = lstMyTbl.Where(x => x.val3 <= FilterVal3.SelectedDate).ToList();
}
this.MyDataGrid.ItemsSource = lstMyTbl;
}
}
I would now like to know if this is the correct (recommended) way to implement filters for a DataGrid element? Especially on the reference that there should be an AND relation between the filters and in case of an empty filter it should not be considered at all.
Thanks to mm8. The probably "recommended" solution here would be the implementation in the MVVM design pattern. I have modified my code according to the MVVM design pattern. My DataGrid element looks like this (Important here is the binding of the ItemSource to ListData. ListData is a list in the ViewModel according to the MVVM design pattern.):
<DataGrid ItemsSource="{Binding ListData}" IsReadOnly="True" HeadersVisibility="Column" AutoGenerateColumns="False" Grid.Row="1" Grid.Column="0" x:Name="DataGridKampagnen" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="Turquoise" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="BorderThickness" Value="0,0,1,2" />
<Setter Property="BorderBrush" Value="Black" />
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="id" Binding="{Binding id}" Visibility="Hidden" Width="*"/>
<DataGridTextColumn Header="fileId" Binding="{Binding fileId}" Visibility="Hidden" Width="*"/>
<DataGridTextColumn Header="val1" Binding="{Binding val1}" Width="*"/>
<DataGridTextColumn Header="val2" Binding="{Binding val2, StringFormat=\{0:dd.MM.yy\}}" Width="*"/>
<DataGridTextColumn Header="val3" Binding="{Binding val3, StringFormat=\{0:dd.MM.yy\}}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
My two datepickers and my textbox look like this:
<TextBox Grid.Row="0" IsReadOnly="False" Grid.Column="1" Name="FilterVal1" Style="{StaticResource FilterInput}" Text="{Binding Val1, UpdateSourceTrigger=PropertyChanged}"/>
<DatePicker Grid.Row="1" Grid.Column="1" Name="FilterVal2" Style="{StaticResource FilterInput}" SelectedDate="{Binding Val2}"/>
<DatePicker Grid.Row="2" Grid.Column="1" Name="FilterVal3" Style="{StaticResource FilterInput}" SelectedDate="{Binding Val3}"/>
My method which provides the data for binding to ListData looks like this:
private List<MyTbl> GetMyTblLst()
{
using (var ctx = new myEntities())
{
var query = from k in ctx.MyTbl select k;
var lstMyTbl = query.ToList();
if(Val1.Trim().Length > 0)
{
lstMyTbl = lstMyTbl.Where(x => x.val1.ToLower().Contains(Val1.ToLower())).ToList();
}
if (Val2 != null)
{
lstMyTbl = lstMyTbl.Where(x => x.val2 >= Val2).ToList();
}
if (Val3 != null)
{
lstMyTbl = lstMyTbl.Where(x => x.val3 <= Val3).ToList();
}
return lstMyTbl;
}
}
The binding to ListData is made like this:
public List<MyTbl> ListData => GetMyTblLst();
This approach works and is a very clean and reusable way to solve the problem.
I am trying to make a page in an app which will use a datagrid to allow users to view and edit data from a SQL Server database. I would like changes to be saved back to the database immediately after they are made by the user.
I have been trying the CellEditEnding event with SqlCommandBuilder to update the database as edits are made; but after every change it shows 0 updates and nothing is saved.
I can see that the changes in the datagrid are reflected in the datatable, and there is an UpdateCommand generated, but no change is made to the database.
Is there something here that I have missed?
Alternatively is there a way to manually create an update command based on the data in the edited row?
I am new to C# and WPF so any help is appreciated.
EDIT
I have tried using a manually generated UpdateCommand and it still does nothing, so I believe the problem occurs around adapter.Update(dt);. For some reason the data is not saving to the database from the datatable.
Code:
public partial class pgMain : Page
{
private SqlDataAdapter adapter;
private DataTable dt;
public pgMain()
{
InitializeComponent();
}
private void Page_Initialized(object sender, EventArgs e)
{
try
{
string connectionString = ConfigurationManager.ConnectionStrings["connPlanDB"].ConnectionString;
SqlConnection cnn = new SqlConnection(connectionString);
cnn.Open();
adapter = new SqlDataAdapter("SELECT * FROM [AdvProdApp].[dbo].[Orders] WHERE Position > 0 AND Hidden = 0 ORDER BY Linje, Position", cnn);
dt = new DataTable();
adapter.Fill(dt);
ICollectionView indata = CollectionViewSource.GetDefaultView(dt.DefaultView);
indata.GroupDescriptions.Add(new PropertyGroupDescription("Linje"));
Tbl_Plan.DataContext = dt;
cnn.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error: Cannot connect to database");
}
}
private void Tbl_Plan_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
try
{
string connectionString = ConfigurationManager.ConnectionStrings["connPlanDB"].ConnectionString;
SqlConnection cnn = new SqlConnection(connectionString);
cnn.Open();
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
adapter.UpdateCommand = builder.GetUpdateCommand();
int u = adapter.Update(dt);
dt.AcceptChanges();
MessageBox.Show(u.ToString());
cnn.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error: Cannot connect to database");
}
}
}
XAML
<DataGrid x:Name="Tbl_Plan" Margin="100,30,50,100" AlternatingRowBackground="Beige" AlternationCount="2" RenderTransformOrigin="0.5,0.5"
CanUserSortColumns="False" VerticalAlignment="Top" Height="900" VerticalScrollBarVisibility="Visible" CanUserDeleteRows="False"
AutoGenerateColumns="False" ItemsSource="{Binding}" CellEditEnding="Tbl_Plan_CellEditEnding">
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<StackPanel>
<StackPanel Orientation="Horizontal" Background="Black">
<TextBlock Text=" Linje " FontWeight="Bold" FontSize="16" Foreground="White" />
<TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="16" Foreground="White" />
</StackPanel>
<ItemsPresenter />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Plandate" Binding="{Binding Plandate}" IsReadOnly="True" />
<DataGridTextColumn Header="Nr" Binding="{Binding Nr}" IsReadOnly="True" />
<DataGridTextColumn Header="Ordernr" Binding="{Binding Ordernr}" IsReadOnly="True" />
<DataGridTextColumn Header="Ursprungsnr" Binding="{Binding Ursprungsnr}" IsReadOnly="True" />
<DataGridTextColumn Header="Description" Binding="{Binding Description}" IsReadOnly="True" />
<DataGridTextColumn Header="Kundnr" Binding="{Binding Kundnr}" IsReadOnly="True" />
<DataGridTextColumn Header="Kundnamn" Binding="{Binding Kundnamn}" IsReadOnly="True" />
<DataGridTextColumn Header="Duedate" Binding="{Binding Duedate}" IsReadOnly="True" />
<DataGridTextColumn Header="Antal" Binding="{Binding Antal}" IsReadOnly="True" />
<DataGridTextColumn Header="Optisk_Ref" Binding="{Binding Optisk_Ref}" IsReadOnly="True" />
<DataGridTextColumn Header="Gen Info" Binding="{Binding sign_Geninfo, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False" />
<DataGridTextColumn Header="Optisk Start" Binding="{Binding sign_Optiskstart, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False" />
<DataGridTextColumn Header="Optisk Slut" Binding="{Binding sign_Optiskslut, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False" />
<DataGridTextColumn Header="Ingen Optisk Ref" Binding="{Binding sign_Ingenoptisk, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False" />
<DataGridTextColumn Header="Efterblandad" Binding="{Binding Efterblandad, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False" />
<DataGridTextColumn Header="Ändringar" Binding="{Binding Ändringar, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False" />
<DataGridTextColumn Header="Kgh" Binding="{Binding Kgh, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False" />
<DataGridTextColumn Header="Notes" Binding="{Binding Notes, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False" />
</DataGrid.Columns>
</DataGrid>
i am changing the value of a column cell, but whenever i hit enter, it display the old value. what can i do to make it give me the new value? Here's my code
private void DataGrid1_CellEditEnding_1(object sender, DataGridCellEditEndingEventArgs e)
{
// Now you have the cell you are dealing with, so you can do what ever you want from here
var currentRowIndex = DataGrid1.Items.IndexOf(DataGrid1.CurrentItem);
int i = DataGrid1.SelectedIndex;
DATA[currentRowIndex].TotalPrice1 = DATA[currentRowIndex].Quantity * DATA[currentRowIndex].ItemPrice;
MessageBox.Show(DATA[currentRowIndex].TotalPrice1.ToString());
DataGrid1.ItemsSource = null;
DataGrid1.ItemsSource = DATA;
}
and heres my xaml
<DataGrid RowEditEnding="DataGrid1_RowEditEnding_1" CellEditEnding="DataGrid1_CellEditEnding_1" TargetUpdated="DataGrid1_TargetUpdated" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="True" EnableColumnVirtualization="False" EnableRowVirtualization="False" IsSynchronizedWithCurrentItem="True" SelectedItem="{Binding SelectedItem}" Grid.Row="3" Grid.ColumnSpan="2" Name="DataGrid1" ItemsSource="{Binding DATA,NotifyOnSourceUpdated=True, Mode=TwoWay, IsAsync=True,UpdateSourceTrigger=PropertyChanged}" Margin="10,10,10,10" PreviewKeyDown="DataGrid1_PreviewKeyDown" SourceUpdated="DataGrid1_SourceUpdated" TextInput="DataGrid1_TextInput" >
<DataGrid.Columns>
<DataGridTextColumn Header="Item Name" IsReadOnly="True" Binding="{Binding Path=ItemName}" Width="*"></DataGridTextColumn>
<DataGridTextColumn Header="Item Price" IsReadOnly="True" Binding="{Binding Path=ItemPrice}" Width="*"></DataGridTextColumn>
<DataGridTextColumn x:Name="QuantityColumn" Header="Quantity" IsReadOnly="False" Binding="{Binding Path=Quantity, Mode=TwoWay}" Width="*"></DataGridTextColumn>
<DataGridTextColumn Header="Total Price" IsReadOnly="True" Binding="{Binding Path=TotalPrice1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="*"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>