I'm debugging a code made by a former employee at the company I am working as an intern.
Turns out a DataGrid is not selecting items. Everytime I try to access DataGrid.SelectedItem it returns me null, even though one row is selected.
As you can see in this print
Follows the XAML code:
<DataGrid x:Name="SchedulesDataGrid"
Grid.Row="1"
Margin="0 10 0 0"
AlternatingRowBackground="LightSteelBlue"
CanUserReorderColumns="False"
CanUserSortColumns="False"
CanUserResizeColumns="False"
CanUserAddRows="False"
CanUserResizeRows="False"
SelectionMode="Single"
SelectionUnit="CellOrRowHeader"
AutoGenerateColumns="False"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding AssemblySchedules, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"
Binding="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsReadOnly="False"
Width="200">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn Header="Type"
Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ScheduleType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedScheduleType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
x:Name="ScheduleTypeComboBox"
IsSynchronizedWithCurrentItem="True"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Category"
Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Categories, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedCategory, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
x:Name="ScheduleCategoryComboBox"
IsSynchronizedWithCurrentItem="True"
DisplayMemberPath="Name"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="View template"
Width="250">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ScheduleTemplates, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedScheduleTemplate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
x:Name="ScheduleTemplateComboBox"
IsSynchronizedWithCurrentItem="True"
DisplayMemberPath="Name"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Phase"
Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Phases, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedPhase, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
x:Name="SchedulePhase"
IsSynchronizedWithCurrentItem="True"
DisplayMemberPath="Name"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Am I missing something?
I read throughout the internet that a PropertyChanged binding is necessary but I believe it is already done the right way.
DataGrid print screen
Can it possibly be because I have to click over the Name in the row, therefore the SelectedItem is not "triggered"?
This is the way I initialized my Data Grid
<DataGrid MaxHeight="450"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
ItemsSource="{Binding Results}"
SelectedItem="{Binding SelectedResult}">
Then in the ViewModel I have this
private Data selectedResult;
public Data SelectedResult
{
get { return selectedResult; }
set
{
if (selectedResult != value)
{
selectedResult = value;
RaisePropertyChanged(nameof(SelectedResult));
}
}
}
Data Being the model for the DataGrid Columns
Okay, so basically I had to change some things.
Don't know why and how the selection part of my data grid stopped working. So I created another column to it and added a remove button to each row of the data grid.
To get the row I used inside the button On_Click method (sender as Button).DataContext and removing it from the data grid source list.
Related
Rows is ok, but I need to see the results over the combo, I've tried with selected index, it didn't work.
<DataGrid.Columns>
<DataGridTemplateColumn Header="Cheque/Transf." Width="105" CellStyle="{StaticResource cellStyle}" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<ComboBox
FontSize="10"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},
Path=DataContext.ChequeTransf}"
DisplayMemberPath="Descripcion"
SelectedValuePath="Descripcion"
SelectedValue="{Binding Path=cTipoTransaccion,Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
SelectedIndex="{Binding Path=cbxIdTransaccion,Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
></ComboBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
I have a DataGrid with 10-15 columns which can have around 100-200 rows. The datagrid is placed within a Tab control ( which is not in focus by default ).
I tried to virtualize the DataGrid but when I click the tab containing the DataGrid, the program freezes for like 4-5 seconds, and then the tab opens displaying the datagrid. After that the rows seem to scroll fast which is good, but the columns still behave slow like un-virtualized.
When I remove the code to virtualize (last 4 options in DataGrid tag), the grid displays immediately but scrolls very slow and lags.
The following is my datagrid code:
<DataGrid Name="xDataGridFieldConfig"
FrozenColumnCount ="1"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalAlignment="Stretch"
Style="{DynamicResource FieldConfigDataGridHeaderStyle}"
AutoGenerateColumns="False"
CanUserResizeColumns="False"
CanUserResizeRows="False"
CanUserReorderColumns="False"
SelectionMode="Single"
GridLinesVisibility="Horizontal"
HorizontalGridLinesBrush="#cbcaca"
HeadersVisibility="Column" ItemsSource="{Binding FieldConfigCollection}"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Visible"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingPanel.VirtualizationMode="Standard"
VirtualizingPanel.IsVirtualizing="True"
ScrollViewer.CanContentScroll="False">
<DataGrid.Columns>
<DataGridTextColumn Header="S No." Binding="{Binding Path=ID}" IsReadOnly="True" Width="80"/>
<!-- using template for custom checkbox -->
<DataGridTemplateColumn Header="EN" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<flatcheckbox:FlatCheckBox x:Name="xFlatCheckBoxFieldConfigEN" Margin="0" IsChecked="{Binding Path=Enabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<flatcheckbox:FlatCheckBox x:Name="xFlatCheckBoxFieldConfigEN" Margin="0" IsChecked="{Binding Path=Enabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Group" Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Name="xTextBlockFieldConfigGroup" Text="{Binding Path=Group, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Name="xTextBlockFieldConfigGroup" IsEnabled="{Binding Enabled}" Text="{Binding Path=Group, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Padding="0" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!-- 10 similar text-only editable rows -->
</DataGrid.Columns>
</DataGrid>
Is there anything wrong I'm doing ? How to make the datagrid to be displayed immediately as soon as I open the tab.
Environment for the following question:
C#, WPF, XAML
How can I achieve to setup a table/a DataGrid with an undefined number of rows having a Combobox in an arbitrary column (number of columns is undefined as well)? (The other row's cells will be filled with the properties of the underyling object/entity.) The ComboBoxes shall all have the same items but the selected item shall be assigned to the underlying object of the row (of course). Hence, I think the ComboBox of each row must have a unique identifier.
The selected item shall be stored in a property of the underlying object/entity.
By the way:
The ComboBox shall be filled with items of a collection (List) that is not part of the row's underlying object/entity.
What would be the best way using WPF / XAML?
UPDATE (2018-12-14):
<Window x:Class="ConfigTool.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ConfigTool"
xmlns:lb="clr-namespace:ConfigTool.DataBinding"
mc:Ignorable="d"
Title="xxx" Height="650" Width="1200" Closing="CloseWindow">
<Window.Resources>
<CollectionViewSource x:Key="TagsCollectionViewSource" CollectionViewType="ListCollectionView"/>
<CollectionViewSource x:Key="NotificationsCollectionViewSource" CollectionViewType="ListCollectionView"/>
<CollectionViewSource x:Key="TagNamesCollectionViewSource" CollectionViewType="ListCollectionView"/>
<lb:StringListConverter x:Key="StringListConverter" />
</Window.Resources>
<Grid Margin="10,10,10,10">
<TabControl>
<TabItem Header="Tags">
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<DataGrid x:Name="tagsGrid" DataContext="{StaticResource TagsCollectionViewSource}" ItemsSource="{Binding}"
AlternatingRowBackground="LightBlue" AutoGenerateColumns="False" CanUserAddRows="True" IsReadOnly="False"
SelectionMode="Single" BorderBrush="Magenta" BorderThickness="3">
<DataGrid.Columns>
<DataGridTextColumn x:Name="TagName" Header="Tag name" Binding="{Binding Mode=TwoWay, Path=TagName}"></DataGridTextColumn>
<DataGridTextColumn Header="Cycle" Binding="{Binding Mode=TwoWay, Path=Cycle}"></DataGridTextColumn>
<DataGridTextColumn Header="Source" Binding="{Binding Mode=TwoWay, Path=Source}"></DataGridTextColumn>
<DataGridTemplateColumn x:Name="editTagColumn" Header="Edit">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<WrapPanel>
<Button x:Name="btnTagDelete" Click="BtnTagDelete_Click" CommandParameter="{Binding}" Height="15" Width="15" Margin="2">
<Button.Content>
<Image Source="Resources/delete.png"></Image>
</Button.Content>
</Button>
</WrapPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
</TabItem>
<TabItem Header="Notifications">
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<DataGrid x:Name="notificationsGrid" DataContext="{StaticResource NotificationsCollectionViewSource}" ItemsSource="{Binding}"
AlternatingRowBackground="LightBlue" AutoGenerateColumns="False" CanUserAddRows="True" IsReadOnly="False"
SelectionMode="Single" BorderBrush="Magenta" BorderThickness="3">
<DataGrid.Columns>
<!--<DataGridTextColumn Header="Tag name" Binding="{Binding Mode=TwoWay, Path=TagName}"></DataGridTextColumn>-->
<DataGridTemplateColumn x:Name="tagNameColumn" Header="Tag name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<!--<ItemContainerTemplate>
<StackPanel>-->
<!--DataContext="{Binding Source={StaticResource TagNamesCollectionViewSource}}">-->
<!--<ComboBox Width="200" DataContext="{StaticResource TagNamesCollectionViewSource}" ItemsSource="{Binding TagNames, Converter={StaticResource StringListConverter}}">-->
<ComboBox Name="notificationTagName" Width="200" DataContext="{StaticResource TagNamesCollectionViewSource}" ItemsSource="{Binding Source={StaticResource TagNamesCollectionViewSource}, RelativeSource={RelativeSource AncestorType=local:MainWindow}}"
SelectionChanged="notificationTagName_SelectionChanged" />
<!--<ComboBox Width="200" DataContext="{StaticResource TagNamesCollectionViewSource}" ItemsSource="{Binding Converter={StaticResource StringListConverter}}" />-->
<!--<ComboBox Width="200" ItemsSource="{Binding ElementName=Window2, Path=DataContext.TagNames, Converter={StaticResource StringListConverter}}" />-->
<!--<ComboBox HorizontalAlignment="Left" Margin="256,260,0,0" VerticalAlignment="Top" Width="120" x:Name="DataList" ItemsSource="{Binding DetailParams, Converter={StaticResource StringListConverter}}"/>-->
</DataTemplate>
<!--</StackPanel>
</ItemContainerTemplate>-->
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridCheckBoxColumn Header="IsActive" Binding="{Binding Mode=TwoWay, Path=IsActive}"></DataGridCheckBoxColumn>
<DataGridTextColumn Header="Type" Binding="{Binding Mode=TwoWay, Path=Type}"></DataGridTextColumn>
<DataGridTextColumn Header="Limit" Binding="{Binding Mode=TwoWay, Path=Limit}"></DataGridTextColumn>
<DataGridTextColumn Header="DetailTemplate" Binding="{Binding Mode=TwoWay, Path=DetailTemplate}"></DataGridTextColumn>
<!--<DataGridTextColumn Header="DetailParams" Binding="{Binding Mode=TwoWay, Path=DetailParams, Converter={StaticResource StringListConverter}}"></DataGridTextColumn>-->
<DataGridTemplateColumn x:Name="detailsParamColumn" Header="Edit">
<DataGridTemplateColumn.CellTemplate>
<ItemContainerTemplate>
<StackPanel> <!--DataContext="{Binding Source={StaticResource TagNamesCollectionViewSource}}">-->
<!--<ComboBox Width="200" DataContext="{StaticResource TagNamesCollectionViewSource}" ItemsSource="{Binding TagNames, Converter={StaticResource StringListConverter}}">-->
<ComboBox Width="200" DataContext="{StaticResource TagNamesCollectionViewSource}" ItemsSource="{Binding}" />
<!--<ComboBox Width="200" DataContext="{StaticResource TagNamesCollectionViewSource}" ItemsSource="{Binding Converter={StaticResource StringListConverter}}" />-->
<!--<ComboBox Width="200" ItemsSource="{Binding ElementName=Window2, Path=DataContext.TagNames, Converter={StaticResource StringListConverter}}" />-->
<!--<ComboBox HorizontalAlignment="Left" Margin="256,260,0,0" VerticalAlignment="Top" Width="120" x:Name="DataList" ItemsSource="{Binding DetailParams, Converter={StaticResource StringListConverter}}"/>-->
</StackPanel>
</ItemContainerTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="CauseProbability" Binding="{Binding Mode=TwoWay, Path=CauseProbability, Converter={StaticResource StringListConverter}}"></DataGridTextColumn>
<DataGridTemplateColumn x:Name="editNotificationsColumn" Header="Edit">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<WrapPanel>
<Button x:Name="btnNotificationDelete" Click="BtnNotificationDelete_Click" Height="15" Width="15" Margin="2">
<Image Source="Resources/delete.png"></Image>
</Button>
</WrapPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
</TabItem>
</TabControl>
</Grid>
</Window>
It is happening because you are using the same collection for the Combo-Box in each row.
The ItemsSource for the Combo-Box should be a collection and it should be a member/property of class that set as DataContext for the DataGrid.
Or
Bind the SelectedItem property of Combo-Box to a member of your class.
See https://www.c-sharpcorner.com/uploadfile/dpatra/combobox-in-datagrid-in-wpf/
I have two tables in a dataset:
Schedules
-------------
id INT
schedule_date DATETIME
service_id INT FOREIGN KEY REFERENCES services(id)
Services
-------------
id INT
name VARCHAR
I can easily bind a DataGridComboBoxColumn and get the result I want in the following way:
<DataGridComboBoxColumn Header="Service"
ItemsSource="{Binding Source={StaticResource schedulesDataSet}, Path=services}"
DisplayMemberPath="name"
SelectedValueBinding="{Binding Path=service_id}"
SelectedValuePath="id" />
But how do I do it with a DataGridTemplateColumn? I want to do this:
<DataGridTemplateColumn Header="Service" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=services.name}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
But as I am using a Dataset and not Entity Framework, I guess that's why it doesn't work.
What's the right way to do it?
EDIT:
Complete DataGrid code:
<Grid DataContext="{Binding Source={StaticResource schedulesViewSource}}">
<DataGrid Margin="0,10,0,0" Grid.Row="1" ItemsSource="{Binding}" AutoGenerateColumns="False" CanUserResizeRows="False" Name="schedulesDataGrid">
<DataGrid.Columns>
<DataGridTextColumn Header="Date" Binding="{Binding Path=schedule_date,StringFormat=dd-MMM-yy,ConverterCulture=da-DK}" Width="*" />
<DataGridTemplateColumn Header="Service" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=services.name}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridComboBoxColumn Header="Status" Width="*" x:Name="scheduleStatusColumn" ItemsSource="{StaticResource ScheduleStatuses}" />
<DataGridTextColumn Header="Source" Binding="{Binding Path=done_by}" Width="*" />
</DataGrid.Columns>
</DataGrid>
</Grid>
Set the ItemsSource for your DataGrid
<DataGrid Margin="0,10,0,0" Grid.Row="1" ItemsSource="{Binding schedulesViewSource, Mode=OneWay}" AutoGenerateColumns="False" CanUserResizeRows="False" Name="schedulesDataGrid">
I want to bind SelectedItem of Combobox with a value converter to "DataGridTextColumn" after it. I am using MVVM pattern.
<Datagrid>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Left">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="Leftcombo" ItemsSource="{Binding Path=DataContext.Column, RelativeSource={RelativeSource AncestorType=Window}}"
SelectedItem="{Binding SelectedColumn, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Right" Binding="{Binding SelectedColumn, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
</DataGrid>
Please note: SelectedColumn is a property in viewmodel bound to Selected item of combobox.
Binding works the way it is in xaml above but, I want to use a value converter on the "DataGridTextColumn Header="Right"" Column.
I think you need to try Mode = TwoWay.
<Datagrid>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Left">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="Leftcombo" ItemsSource="{Binding Path=DataContext.Column, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=Window}}"
SelectedItem="{Binding SelectedColumn, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Right" Binding="{Binding SelectedColumn, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
</DataGrid>