Mvvmlight UpdateSourceTrigger not working - c#

xaml
<DataGrid ItemsSource="{Binding Products}" CanUserAddRows="False" AutoGenerateColumns="False" SelectedItem="{Binding SelectedProduct}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="CellEditEnding">
<cmd:EventToCommand Command="{Binding ProdcutCellEditCmd,UpdateSourceTrigger=PropertyChanged}" PassEventArgsToCommand="True"></cmd:EventToCommand>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding id}" Header="ID" IsReadOnly="True"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding ParentLCSKU}" Header="LCSKU(Parent)" IsReadOnly="True"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding ChildLCSKU}" Header="LCSKU(Child)" IsReadOnly="True"></DataGridTextColumn>
<DataGridComboBoxColumn ItemsSource="{Binding Source={StaticResource ProductColors}}" SelectedValueBinding="{Binding Color}" Header="颜色" IsReadOnly="True"></DataGridComboBoxColumn>
<DataGridComboBoxColumn ItemsSource="{Binding Source={StaticResource ProductSizes}}" SelectedValueBinding="{Binding Size}" Header="尺寸" IsReadOnly="True"></DataGridComboBoxColumn>
<DataGridComboBoxColumn ItemsSource="{Binding Source={StaticResource ProductCategories}}" SelectedValueBinding="{Binding Category}" Header="类别" IsReadOnly="True"></DataGridComboBoxColumn>
<DataGridTextColumn Binding="{Binding Cost}" Header="成本"></DataGridTextColumn>
<DataGridCheckBoxColumn Binding="{Binding IsOEM,UpdateSourceTrigger=PropertyChanged}" Header="OEM"></DataGridCheckBoxColumn>
</DataGrid.Columns>
viewmodel
public ProductVM()
{
ProdcutCellEditCmd = new RelayCommand<DataGridCellEditEndingEventArgs>(prodcutDataGridCellEditEnding);
}
public RelayCommand<DataGridCellEditEndingEventArgs> ProdcutCellEditCmd { get; set; }
private void prodcutDataGridCellEditEnding(DataGridCellEditEndingEventArgs e)
{
if (e.EditAction == DataGridEditAction.Commit)
{
var prodcut = e.Row.DataContext as BaseProduct;
SelectedProduct = prodcut;
productSave();
}
}
I want to trigger the CellEditEnding event after the checkbox be checked or unchecked, but this event only fires when the cell lost focus.
Whats wrong with this?
Sorry for my bad English.

It is normal behavior cause event name is CellEditEnding. It means that event is fired after finish of cell edit.
I suggest you to bind to Command property in CheckBox.
If you DataGrid placed in Window, you should write:
<CheckBox Header="OEM" IsChecked="{Binding IsOEM,UpdateSourceTrigger=PropertyChanged}}"
Command="{Binding DataContext.ProdcutCellEditCmd, RelativeSource=
{RelativeSource AncestorType=Window, Mode=FindAncestor}}"}" />
If you DataGrid placed in UserControl, you should write:
<CheckBox Header="OEM" IsChecked="{Binding IsOEM,UpdateSourceTrigger=PropertyChanged}}"
Command="{Binding DataContext.ProdcutCellEditCmd, RelativeSource=
{RelativeSource AncestorType=UserControl, Mode=FindAncestor}}"}" />

You can do one thing in that case you have to update code like
First give the name of your datagrid like
<DataGrid ItemsSource="{Binding Products}" Name="dg" CanUserAddRows="False" AutoGenerateColumns="False" SelectedItem="{Binding SelectedProduct}">
Now you have to bind the Checkbox checked event and pass the row of datagrid so you can access all values of that rows in ViewModel
<DataGridCheckBoxColumn Binding="{Binding IsOEM,UpdateSourceTrigger=PropertyChanged}" Header="OEM">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding CheckBoxChecked,Mode=TwoWay,RelativeSource={RelativeSource AncestorType=DataGrid}}" CommandParameter="{Binding ElementName=dg,Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGridCheckBoxColumn>
Your RelayCommand should be like
I assume that you have bind the List in your datagrid so based on it your relay command should be
private RelayCommand<BaseProduct> _CheckBoxChecked;
public RelayCommand<BaseProduct> CheckBoxChecked
{
get { return _CheckBoxChecked??(_CheckBoxChecked=new RelayCommand<BaseProduct>(CheckMethod)); }
set { _CheckBoxChecked = value; }
}
void CheckMethod(BaseProduct product)
{
// you can access product here
}

Related

Wpf multiple combobox binding to one property

I am having a data grid, one of its cells is a combo box like:
<DataGrid x:Name="Applications" RowStyle="{StaticResource CollapsedRow}" AutoGenerateColumns="false" CanUserAddRows="false" ItemsSource="{Binding Applications}">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content='˅' FontSize="9" Name="ExpanderButton" Click="OnGroupChange" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="181" Header="Name" Binding="{Binding Name, Mode=OneWay}" />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Cabins,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
SelectedValuePath="Id" IsSynchronizedWithCurrentItem="True"
SelectedValue="{Binding Path=DataContext.SelectedCabin,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
mah:TextBoxHelper.Watermark="{Binding Path=DataContext.CabinsWatermark, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
Height="2" Width="300" Margin="10 5 10 10" HorizontalAlignment="Left">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource GuidConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
And as you see in each row there ia a combo box in detail row (expanded row using button), each combo box is binded to one property:
private Guid? selectedCabin;
public override Guid? SelectedCabin
{
get => selectedCabin;
set
{
selectedCabin = value;
if (value.HasValue)
{
Console.WriteLine(value);
}
OnPropertyChanged();
}
Now problem is when i select item in combo box i am getting not single value but couple of them (I suppose there are all values from one combo box I made a selection on), to make sure i double checked with test code behind:
private void ComboBox_OnSelectCabinChanged(object sender, RoutedEventArgs e)
{
var combo = (ComboBox)sender;
if (combo != null && combo.IsDropDownOpen)
{
((ApplicationsViewModel)DataContext).SelectedCabin = (Guid?)sender;
combo.IsDropDownOpen = false;
}
}
And I am getting here and combo box item list and casting exception. What could be the root cause of this and is there a way to bind multiple combo box values to one property, so is i select one it will override another.
It seems like you are binding the SelectedValue of all row details ComboBoxes to the same source property. And you can't cast the sender argument to a Guid?. Try to cast the SelecteedValue property of the ComboBox:
SelectedCabin = (Guid?)combo.SelectedValue;
If you don't want to handle the SelectionChanged event in the view, you could use an interaction trigger that executes a command that sets the source property. Please refer to this blog post for more information about this.

Wpf MVVM Selecting/unSelecting Item in DataGrid

I'm working on an MVVM project and I have this Code in one of the the views:
<GroupBox Header="Defaut" BorderBrush="#FF4EA8DE" FontSize="16" Foreground="#FF436EFF" >
<DataGrid Background="Transparent" FontSize="14" CanUserAddRows="False" CanUserDeleteRows="False" IsReadOnly="True" AutoGenerateColumns="False" Style="{x:Null}"
ItemsSource="{Binding ErrorList}">
<DataGrid.Columns>
<DataGridTextColumn Width="0.5*" Header="{DynamicResource Numéro Cordon}" Binding="{Binding BeadName}"></DataGridTextColumn>
<DataGridTextColumn Width="0.5*" Header="{DynamicResource Indice Image}" Binding="{Binding IndiceImage}"></DataGridTextColumn>
<DataGridTextColumn Width="0.5*" Header="{DynamicResource Défaut}" Binding="{Binding DispDefault}"></DataGridTextColumn>
<DataGridTemplateColumn Header="{DynamicResource Criticité}" Width="0.5*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding IsError, Converter={StaticResource IsErrorToCriticityLevel}, Mode=OneWay}"></Label>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="CmdB:CommandBehavior.Event" Value="MouseDown" />
<Setter Property="CmdB:CommandBehavior.Command" Value="{Binding DataContext.RobotErrorSelectionChangedCommand,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type controls:MetroWindow}}}"/>
<Setter Property="CmdB:CommandBehavior.CommandParameter" Value="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="BorderBrush" Value="#FF6593CF" />
<Setter Property="Background" Value="#FF6593CF" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding RobotErrorSelectionChangedCommand}" CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
</GroupBox>
What I want to do is to be able to unselect the selected item in this list, however I can't find how to access it.
Here is the code in the ViewModel related to this list:
ObservableCollection<Erreur> _ErrorList;
public ObservableCollection<Erreur> ErrorList
{
get { return _ErrorList; }
set { _ErrorList = value; RaisePropertyChanged("ErrorList");}
}
private RelayCommand<Erreur> _RobotErrorSelectionChangedCommand;
public RelayCommand<Erreur> RobotErrorSelectionChangedCommand
{
get
{
return _RobotErrorSelectionChangedCommand
?? (_RobotErrorSelectionChangedCommand = new RelayCommand<Erreur>(
(Erreur err) =>
{
if (err != null)
{
viewservice.OpenDialog(new ErreurImageViewModel(err), ServiceLocator.Current.GetInstance<MainViewModel>());
}
}));
}
}
Thank you for any help or advice.
You can bind the SelectedItem property in the Datagrid to a property in the VM, and to clear the current selection you can just set the property to: null. That way you can deselect the SelectedItem through the code whenever you want.
You would bind it in your View like this:
<DataGrid ItemsSource="{Binding ErrorList}" SelectedItem="{Binding SelectedError}" ...>
Then in your ViewModel you would add:
private Erreur _selectedError = null;
public Erreur SelectedError
{
get => _selectedError;
set
{
if (_selectedError == value) return;
_selectedError = value;
RaisePropertyChanged(nameof(SelectedError));
}
}
Whenever you want to clear the selection you can just do:
SelectedError = null;
And if you want to select a specific instance from the code you can do:
SelectedError = myInstanceOfError;
Bind the SelectedError property to the SelectedItem Attribute in your XAML.
XAML:
<DataGrid Background="Transparent" FontSize="14" CanUserAddRows="False" CanUserDeleteRows="False" IsReadOnly="True" AutoGenerateColumns="False" Style="{x:Null}" ItemsSource="{Binding ErrorList}" SelectedItem="{Binding SelectedError}">
C# Property:
private Erreur _SelectedError;
public Erreur SelectedError
{
get { return _SelectedError; }
set {
if(_SelectedError != value)
{
_SelectedErrorList = value;
RaisePropertyChanged("SelectedError");
}
}
}
Thank you, both answers are correct with a little modification, I added the :
SelectedItem="{Binding SelectedError}" in the XAML code.
and I had to comment this part to disable the command from working :
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding RobotErrorSelectionChangedCommand}" CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
now the SelectedError get the Selected Item.Thanx

commandparameter in Datagrid & menucontext gives null

I stuck couple of hours in trying to get parameter from ContextMenu in Datagrid using MVVM.
The parameter comes from the CommandParameter is always null, out of set {Binding} but it's not what I want.
I new in WPF, so for me it is was not helpful to read answers from here and from others questions. It always remain null.
My code is below:
<DataGrid Grid.Row="2" Margin="25,0,0,4" SelectionMode="Single" AlternationCount="2" Name="dgAltPart" AutoGenerateColumns="False" ItemsSource="{Binding Path=AltPartResult}" HorizontalAlignment="Left" VerticalAlignment="Top"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<DataGrid.ContextMenu>
<ContextMenu >
<MenuItem Header="Delete" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.manufacturer}"
Command="{Binding DeleteManufacturerCommand}"/>
</ContextMenu>
</DataGrid.ContextMenu>
<DataGrid.Columns>
<DataGridTextColumn Header="Manufacturer" Width="175" Binding="{Binding manufacturer}"></DataGridTextColumn>
<DataGridTextColumn Header="Manufacturer Part Number" Width="200" Binding="{Binding manufacturer_pn}"></DataGridTextColumn>
<DataGridTextColumn Header="Price" Width="100" Binding="{Binding price}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
And My ViewModel:
private bool canExecute = true;
public ICommand DeleteManufacturerCommand
{
get
{
if (_deleteManufacturerCommand == null)
{
_deleteManufacturerCommand = new RelayCommand(DeleteManufacturer, param => this.canExecute);
}
return _deleteManufacturerCommand;
}
}
public void DeleteManufacturer(object obj)
{
}
In DeleteManufacturer() I always get the obj with null.
so far tried with all kinds of relative source.
What do I miss?
If you define set ContextMenu property at row level, you could bind directly to the DataContext of each row. Try this:
<DataGrid Grid.Row="2" Margin="25,0,0,4" SelectionMode="Single" AlternationCount="2"
Name="dgAltPart" AutoGenerateColumns="False"
ItemsSource="{Binding Path=AltPartResult}" HorizontalAlignment="Left" VerticalAlignment="Top"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<DataGrid.ItemContainerStyle>
<Style TargetType="DataGridRow">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Delete"
CommandParameter="{Binding}"
Command="{Binding PlacementTarget.Tag.DataContext.DeleteManufacturerCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ItemContainerStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Manufacturer" Width="175" Binding="{Binding manufacturer}"></DataGridTextColumn>
<DataGridTextColumn Header="Manufacturer Part Number" Width="200" Binding="{Binding manufacturer_pn}"></DataGridTextColumn>
<DataGridTextColumn Header="Price" Width="100" Binding="{Binding price}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<DataGrid Grid.Row="2" Margin="25,0,0,4" SelectionMode="Single" AlternationCount="2" Name="dgAltPart" AutoGenerateColumns="False" ItemsSource="{Binding Path=AltPartResult}" HorizontalAlignment="Left" VerticalAlignment="Top" SelectedItem="{Binding SelectedManufacturer}"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<DataGrid.ContextMenu>
<ContextMenu >
<MenuItem Header="Delete" CommandParameter="{Binding SelectedManufacturer}"
Command="{Binding DeleteManufacturerCommand}"/>
</ContextMenu>
</DataGrid.ContextMenu>
<DataGrid.Columns>
<DataGridTextColumn Header="Manufacturer" Width="175" Binding="{Binding manufacturer}"></DataGridTextColumn>
<DataGridTextColumn Header="Manufacturer Part Number" Width="200" Binding="{Binding manufacturer_pn}"></DataGridTextColumn>
<DataGridTextColumn Header="Price" Width="100" Binding="{Binding price}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
and your ViewModel should have a property like this:
private Manufacturer selectedManufacturer;
public Manufacturer SelectedManufacturer
{
get { return selectedManufacturer; }
set { selectedManufacturer = value; OnPropertyChanged(); }
}

DataGridRow.IsSelected is set and I dont know why

Hello this is my problem:
I got two styles defined in the resources for DataGridRow and DataGridCell:
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="DataGridRow.IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<Trigger Property="DataGridCell.IsSelected" Value="True">
<Setter Property="Background" Value="LightSteelBlue" />
</Trigger>
</Style.Triggers>
</Style>
I basically want a row to be highlighted blue when user selects it.
I got a MessageWrapper and a SignalWrapper class. Each MessageWrapper has a list of SignalWrappers (which can be empty sometimes). So the first DataGrid is to display all MessageWrappers:
<DataGrid x:Name="messageGrid"
Grid.Column="0"
Margin="3"
AutoGenerateColumns="False"
CanUserAddRows="False"
HorizontalScrollBarVisibility="Hidden"
IsReadOnly="True"
ItemsSource="{Binding SelectedBusWrapper.MessageWrappers}"
SelectedItem="{Binding SelectedBusWrapper.SelectedMessageWrapper}"
attachedProperties:SelectingItemAttachedProperty.SelectingItem="{Binding SelectedBusWrapper.MessageWrapperToScrollIntoView,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="Auto" Header="Filter">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox MaxWidth="15"
MaxHeight="15"
CommandParameter="{Binding}"
IsChecked="{Binding Filter}"
Style="{StaticResource CheckBoxStyle}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="9*"
Binding="{Binding Message.Name}"
Header="Name" />
<DataGridTextColumn Width="9*"
Binding="{Binding HexId}"
Header="Id" />
<DataGridTextColumn Width="11*"
Binding="{Binding Message.Sender}"
Header="Sender" />
</DataGrid.Columns>
</DataGrid>
Each Row of this DataGrid has a CheckBox column. If no CheckBox at all is checked then all SignalWrappers are displayed in second DataGrid. If one or more are checked, then only the SignalWrappers of the checkec MessageWrappers should be displayed. Here the DataGrid:
<DataGrid Grid.Column="1"
Margin="3"
AutoGenerateColumns="False"
CanUserAddRows="False"
HorizontalScrollBarVisibility="Hidden"
IsReadOnly="True"
ItemsSource="{Binding SelectedBusWrapper.SelectedSignalWrappers}"
SelectedItem="{Binding SelectedBusWrapper.SelectedSignalWrapper}"
SelectionMode="Extended">
<DataGrid.Resources>
<Style TargetType="DataGridRow">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</DataGrid.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding MouseDoubleClickCommand}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTemplateColumn Width="Auto" Header="Filter">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox MaxWidth="15"
MaxHeight="15"
CommandParameter="{Binding}"
IsChecked="{Binding Filter}"
Style="{StaticResource CheckBoxStyle}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="6*"
Binding="{Binding Signal.Name}"
Header="Name" />
<DataGridTemplateColumn Width="Auto" Header="Value">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox MinWidth="75"
IsEditable="True"
ItemsSource="{Binding Signal.Values.Values}"
SelectedItem="{Binding SelectedValue,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Now if the user selects one or more message(s) in the first DataGrid the signals of those messages should be highlighted in the second. If the user selects a signal in the second, the parent message should be highlighted in the first DataGrid.
Bound Property of MessageWrapper class:
public bool IsSelected
{
get
{
return isSelected;
}
set
{
if (isSelected == value) {
return;
}
isSelected = value;
foreach (var item in SignalWrappers) {
item.IsSelected = value;
}
RaisePropertyChanged("IsSelected");
}
}
Bound Property of SignalWrapper class:
public bool IsSelected
{
get
{
return isSelected;
}
set
{
if (isSelected == value) {
return;
}
isSelected = value;
ParentMessageWrapper.IsSelected = value;
RaisePropertyChanged("IsSelected");
}
}
Now I have an odd behavoir I cannot understand:
When I check the first CheckBox in the first DataGrid:
The message gets highlighted, the second DataGrid shows only the signals of that message and highlights them. This is correct so far.
If I now check a second message it highlights both rows and all signals of those rows. While debugging i noticed what happens is that the first MessageWrappers IsSelectedflag gets set to false, and all its signals flag gets set to false, which is correct. Afterwards the second MessageWrappers flag gets set to true and all its signals flag gets set to true, which is correct too.
And afterwards for a reason I cannot understand a signal of the first MessageWrapper IsSelected flag is set to true, this will set the MessageWrappers flag to true and this will set the other signals flag to true.
Note: If I check the third CheckBox everything is working as expected again.
Can anybody understand why this is? Thanks for your help in advance!
you have many loops when setting IsSelected in SingleWrapper because it set ParentMessageWrapper.IsSelected = value; which will return to set IsSelected in SingleWrapper and so on.
i suppose to make Method in SingleWrapper Like this:
public SetIsSelected(bool value)
{
if (isSelected == value) {
return;
}
isSelected = value;
RaisePropertyChanged("IsSelected");
}
and change item.IsSelected = value; in MessageWrapper to item.SetIsSelected(value)

DataGrid ComboBox binding with Entity Framework

I currently have a C# WPF .NET 4.5 application. I am using a DataGrid to show items from the project's database. I am able to populate everything in the DataGrid except for the DataGridComboBoxColumn. First, i would just like to get it to display data. In the future, I would like the combobox to display on the first line the current value in the database, but have the ComboBox contain set values, to limit what users can enter. SO it would be like list item1 = current value, list item2 = pounds, list item3 = ounces, etc. The Table has 3 columns I am working with, InventoryName, Qty, and Type. Type should be the ComboBox
Here is my Code
private AuroraDataEntities auroraDataEntities;
public ViewCup()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
LoadData();
}
private void LoadData()
{
auroraDataEntities = new AuroraDataEntities();
dgInv.DataContext = auroraDataEntities.Inventories;
}
void EditComplete(object sender, SelectedCellsChangedEventArgs e)
{
auroraDataEntities.SaveChanges();
}
private void Window_Unloaded(object sender, RoutedEventArgs e)
{
auroraDataEntities.SaveChanges();
}
}
And here is my XAML
<DataGrid x:Name="dgInv" HorizontalAlignment="Center" Height="134" Margin="-10,96,10,0" VerticalAlignment="Top" Width="Auto"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
AutoGenerateColumns="False"
GridLinesVisibility="None"
SelectedCellsChanged="EditComplete">
<DataGrid.Columns>
<DataGridTextColumn Width="200" Header="InventoryName" Binding="{Binding InventoryName}" />
<DataGridTextColumn Width="50" Header="Qty" Binding="{Binding Qty}" />
<DataGridComboBoxColumn x:Name="cbType"
Width="50"
Header="Type"
ItemsSource="{Binding Path=Type, Mode=TwoWay}"
DisplayMemberPath="Type"
SelectedValueBinding="{Binding Type}" >
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
In order to get the DataGridComboBoxColumn to work correctly, you have to use the ElementStyle and EditingElementStyle as follows:
<DataGridComboBoxColumn x:Name="cbType" Width="50" Header="Type"
DisplayMemberPath="Type" SelectedValueBinding="{Binding Type}" >
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Type}"/>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Type}"/>
</Style>
</DataGridComboBoxColumn.ElemEditingElementStyleentStyle>
</DataGridComboBoxColumn>
This will populate the ComboBox in the grid column with your ItemsSource. Depending on how you're using the ComboBox in the grid, you may not need to set the ElementStyle. See this answer for some information on why this is necessary.
Your DataContext should have a ViewModel which should expose two separate properties, Inventories and TypeCollection (an array or list containing ounces, pounds etc) and you should bind ItemsSource of DataGridComboBoxColumn to TypeCollection and SelectedValueBinding to Type.
public class ViewModel{
// implement INotifyPropertyChanged for following
public IEnumerable TypeCollection{ get; private set;}
public IEnumerable Inventories {get; private set;}
}
// set DataContext as shown below
this.DataContext = new ViewModel(.....);
// Set Binding as shown below
<DataGrid x:Name="dgInv" HorizontalAlignment="Center" Height="134" Margin="-10,96,10,0" VerticalAlignment="Top" Width="Auto"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Inventories}"
AutoGenerateColumns="False"
GridLinesVisibility="None"
SelectedCellsChanged="EditComplete">
<DataGrid.Columns>
<DataGridTextColumn Width="200" Header="InventoryName" Binding="{Binding InventoryName}" />
<DataGridTextColumn Width="50" Header="Qty" Binding="{Binding Qty}" />
<DataGridComboBoxColumn x:Name="cbType"
Width="50"
Header="Type"
ItemsSource="{Binding Path=DataContext.TypeCollection,
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}}"
DisplayMemberPath="Type"
SelectedValueBinding="{Binding Path=Type, Mode=TwoWay}" >
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
Note that you have to use RelativeSource to find DataContext of parent DataGrid.

Categories