How to set selected items in MVVM using SelectionMode="Extended"? - c#

Goal
I currently have an app that select the items only by selecting the check box. Like so:
My goal is to multi select items (Which highlights the selected rows), right click and have a data context menu item to select, selected items.
Desired Output
As an example, this would be my desired output (Obviously not selecting the checkboxes manually).
Question
What would a good starting point to achieve this output? I couldn't find any examples.
Code
The Model is pretty simple.
public class OrdersModel : BaseVM
{
public int OrderId { get; set; } // Hidden
public string OrderNumber { get; set; }
public DateTime OrderDate { get; set; }
private bool _selectedRecord;
public bool SelectedRecord
{
get { return _selectedRecord; }
set
{
_selectedRecord = value;
OnPropertyChanged();
}
}
}
And this is my current XAML.
<DataGrid ItemsSource="{Binding Orders}"
AutoGenerateColumns="False"
SelectionMode="Extended"
CanUserAddRows="False">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SelectedRecord}" Value="True">
<Setter Property="Background" Value="LightGray" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<!--Columns Used-->
<DataGrid.Columns>
<!--Select All Column-->
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Content="Select All"
IsChecked="{Binding DataContext.SelectAll, RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"
Command="{Binding DataContext.ToggleAllOrdersCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=SelectedRecord, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Order Date"
Binding="{Binding OrderDate, StringFormat='{}{0:MM-dd-yyyy}'}" Width="*"
IsReadOnly="True"
FontSize="14" />
<DataGridTextColumn Header="Order Number"
Binding="{Binding OrderNumber}" Width="*"
IsReadOnly="True"
FontSize="14" />
</DataGrid.Columns>
<!--Menu Options-->
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Select Highlighted Rows" />
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>

This solution worked for me. I took the liberty of changing some of your property names to avoid confusion as to what is "selected" and what is "checked".
OrdersModel:
public class OrdersModel : BaseVM
{
public int OrderId { get; set; } // Hidden
public string OrderNumber { get; set; }
public DateTime OrderDate { get; set; }
private bool _selected;
private bool _checked;
public bool Selected
{
get { return _selected; }
set { _selected = value; OnPropertyChanged(); }
}
public bool Checked
{
get { return _checked; }
set { _checked = value; OnPropertyChanged(); }
}
}
MainWindow.xaml.cs
<DataGrid ItemsSource="{Binding Orders}"
AutoGenerateColumns="False"
SelectionMode="Extended"
CanUserAddRows="False"
Tag="{Binding ElementName=myWindow,Path=DataContext}"
>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="IsSelected" Value="{Binding Selected}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Selected}" Value="True">
<Setter Property="Background" Value="LightGray" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<!--Columns Used-->
<DataGrid.Columns>
<!--Select All Column-->
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Content="Select All"
IsChecked="{Binding DataContext.SelectAll, RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"
Command="{Binding DataContext.ToggleAllOrdersCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=Checked, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Order Date"
Binding="{Binding OrderDate, StringFormat='{}{0:MM-dd-yyyy}'}" Width="*"
IsReadOnly="True"
FontSize="14" />
<DataGridTextColumn Header="Order Number"
Binding="{Binding OrderNumber}" Width="*"
IsReadOnly="True"
FontSize="14" />
</DataGrid.Columns>
<!--Menu Options-->
<DataGrid.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Select Highlighted Rows" Command="{Binding CheckSelectedCommand}"/>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
MainWindow.xaml.cs:
public class MainWindowViewModel : BaseVM
{
private ObservableCollection<OrdersModel> _orders;
private RelayCommand _checkSelectedCommand;
public MainWindowViewModel()
{
Initialize();
}
public ObservableCollection<OrdersModel> Orders
{
get
{
if (_orders == null)
_orders = new ObservableCollection<OrdersModel>();
return _orders;
}
}
public RelayCommand CheckSelectedCommand
{
get
{
if (_checkSelectedCommand == null)
_checkSelectedCommand = new RelayCommand(o => DoCheckSelectedRows());
return _checkSelectedCommand;
}
}
public void Initialize()
{
var dateTime = DateTime.Now.AddDays(-5);
for (int i = 0; i < 10; i++)
{
this.Orders.Add(new OrdersModel() { OrderId = i, OrderDate = dateTime.AddHours(4), OrderNumber = $"CA{i + 1593:0000}" });
}
}
private void DoCheckSelectedRows()
{
var selectedOrders = this.Orders.Where(o => o.Selected);
foreach (var order in selectedOrders)
{
order.Checked = true;
}
}
}
Solution will look like this:

Related

new row in datagrid with custom data

I have two datagrids. The first one is an overview about a time tracking. The second one shows some details.
For example:
The first datagrid contains each day, where a employee works. The second datagrid contains the time stamping for a day.
However, when I add a new stamping row, the controls in the datagrid are empty. That is not really a problem but the datepicker shows the date 01/01/0001.
what i want is when i add a new row, the date field should be filled with the date from the first datagrid.
VIEW:
<DataGrid Grid.Column="0"
IsReadOnly="True"
AutoGenerateColumns="False"
Grid.Row="0"
x:Name="DgStundenView"
Margin="0 0 10 0"
SelectedItem="{Binding SelectedItem}"
ItemsSource="{Binding TimeOverviewList}">
<DataGrid.Columns>
<DataGridTextColumn Header="Wochentag" Binding="{Binding Wochentag}" />
<DataGridTextColumn Header="Datum" Binding="{Binding Tag}" />
<DataGridTextColumn Header="Arbeitszeit Soll" Binding="{Binding ArbeitszeitInStunden}" />
<DataGridTextColumn Header="Arbeitszeit gesamt" Binding="{Binding GesamtDauerInStunden}" />
<DataGridTextColumn Header="Pausenzeit" Binding="{Binding Pausenzeit}" />
<DataGridTextColumn Header="Pausendifferenz" Binding="{Binding PausenDifferenzInStunden}" />
<DataGridTextColumn Header="Arbeitszeit inkl. Pause" Binding="{Binding TatsaechlicheDauerInStunden}" />
<DataGridCheckBoxColumn Header="Status" Binding="{Binding StempelungVorhanden,Mode=OneWay}" />
<DataGridTextColumn Header="Info" Binding="{Binding Info}" />
</DataGrid.Columns>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Wochentag}" Value="Sa">
<Setter Property="Background" Value="LightGray" />
</DataTrigger>
<DataTrigger Binding="{Binding Wochentag}" Value="So">
<Setter Property="Background" Value="LightGray" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
<DataGrid Grid.Column="0"
Grid.Row="1"
x:Name="DgStempelungen"
Margin="0 10 10 0"
AutoGenerateColumns="False"
CanUserAddRows="True"
SelectedItem="{Binding SelectedValue}"
ItemsSource="{Binding TimeDetailList}">
<DataGrid.Resources>
<x:Array x:Key="Reasons" Type="system:String">
<system:String>NICHT ANWENDBAR</system:String>
<system:String>KRANK</system:String>
<system:String>GANZER TAG URLAUB</system:String>
</x:Array>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="ID" IsReadOnly="True" Binding="{Binding Id}"/>
<DataGridTemplateColumn Header="KOMMEN DATUM">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding ComeBooking}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="KOMMEN UHRZEIT">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<materialDesign:TimePicker Text="{Binding ComeBookingTime, StringFormat=t}"
Is24Hours="True">
</materialDesign:TimePicker>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="GEHEN DATUM">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding GoBooking}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="GEHEN UHRZEIT">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<materialDesign:TimePicker Text="{Binding GoBookingTime, StringFormat=t}"
Is24Hours="True">
</materialDesign:TimePicker>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridComboBoxColumn Header="GRUND"
ItemsSource="{StaticResource Reasons}"
TextBinding="{Binding Info}"/>
</DataGrid.Columns>
</DataGrid>
VIEW MODEL
[CanBeNull] private ClassTimeTrackingDayStamp _selectedItem;
[CanBeNull]
public ClassTimeTrackingDayStamp SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged();
if (_selectedItem != null)
TimeDetailList = new ObservableCollection<ClassTimeTrackingTimeStamp>(_selectedItem.Stempelungen);
}
}
private ObservableCollection<ClassTimeTrackingDayStamp> _timeOverviewList;
public ObservableCollection<ClassTimeTrackingDayStamp> TimeOverviewList
{
get { return _timeOverviewList; }
set
{
_timeOverviewList = value;
OnPropertyChanged();
}
}
[CanBeNull] private ObservableCollection<ClassTimeTrackingTimeStamp> _timeDetailList;
[CanBeNull]
public ObservableCollection<ClassTimeTrackingTimeStamp> TimeDetailList
{
get { return _timeDetailList; }
set
{
_timeDetailList = value;
OnPropertyChanged();
}
}
MODEL
public class ClassTimeTrackingTimeStamp : ViewModelBase
{
public DateTime ComeBooking { get; set; }
public DateTime GoBooking { get; set; }
public TimeSpan ComeBookingTime { get; set; }
public TimeSpan GoBookingTime { get; set; }
public int Id { get; set; }
public string Info { get; set; }
}
Update
EVENT OVER MVVM VIEW
<DataGrid Grid.Column="0"
Grid.Row="1"
x:Name="DgStempelungen"
Margin="0 10 10 0"
AutoGenerateColumns="False"
CanUserAddRows="True"
SelectedItem="{Binding SelectedValue}"
ItemsSource="{Binding TimeDetailList}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="AddingNewItem">
<i:InvokeCommandAction Command="{Binding Path=AddNewItemCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
...
VIEW MODEL
public ICommand AddNewItemCommand { get; set; }
#endregion Properties
#region Events
public event EventHandler AddNewItemEventHandler;
#endregion
#region Constructor
public WTimeClockOverviewVM()
{
AddNewItemCommand = new RelayCommand(o =>
{
this.AddNewItemEventHandler?.Invoke(this, EventArgs.Empty);
});
}
CODE BEHIND
public WTimeClockOverview()
{
InitializeComponent();
var vm = (WTimeClockOverviewVM)DataContext;
if(vm == null) return;
vm.AddNewItemEventHandler += (sender, args) =>
{
var selectedItemInFirstDg = vm.SelectedItem;
if (selectedItemInFirstDg != null)
{
var newItem = new ClassTimeTrackingTimeStamp();
newItem.ComeBooking = selectedItemInFirstDg.Tag;
}
};
}
Thank you for your help :)
Best regards
Levin
In second DataGrid, you can subscribe to AddingNewItem event
<DataGrid x:Name="DgStempelungen"
AddingNewItem="DgStempelungen_OnAddingNewItem"
...
In code behind
private void DgStempelungen_OnAddingNewItem(object sender, AddingNewItemEventArgs e)
{
// I'll assume that the Model's Name in first DataGrid collection is StudentItem!
var selectedItemInFirstDg = DgStundenView.SelectedItem as StudentItem;
// or you can get the selected item from the DataContext:
// (DgStundenView.DataContext as MyViewModel)?.SelectedItem;
if (selectedItemInFirstDg != null){
// create the new row with data from selectedItemInFirstDg, ex. date, etc...
e.NewItem = new ClassTimeTrackingDayStamp
{
ComeBooking = selectedItemInFirstDg.Date; // for example
};
}
}

Several combo boxes in one DataGrid

I'm trying to bind combobox with different values in each row to DataGrid. My problem is most likely that I do not know how to set ItemsSource and SelectedValuePath. How should it be done?
.cs:
private List<List<ComboItem>> ComboBoxItemsList = new List<List<ComboItem>>();
...
foreach (AppSettingsElement list in enablers[key])
{
string[] stringSeparators = new string[] { "\r\n" };
string[] lines = list.Comment.Split(stringSeparators, StringSplitOptions.None);
List<ComboItem0> comboItems = GetComboItemList(lines);
// clean comment
list.Comment = lines[1];
//add to comboList
appSettingsComboBoxList.Add(new AppSettingsElement {Comment=list.Comment, Name= list.Name, Value=list.Value, ComboItems = comboItems });
}
AppSettingsDropDowns.ItemsSource = appSettingsComboBoxList;
...
public class ComboItem0
{
public string ID { get; set; }
public string Text { get; set; }
}
public class AppSettingsElement : ComboItem0
{
public string Comment { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public string Type { get; set; }
public List<ComboItem0> ComboItems { get; set; }
}
.xaml:
<DataGrid x:Name="AppSettingsDropDowns" AutoGenerateColumns="False" Grid.ColumnSpan="6" Grid.Row="8" CanUserAddRows="False" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Comment}" Header="Comment" Width="4*" IsReadOnly="True" />
<DataGridTextColumn Binding="{Binding Name}" Header="Element Name" Width="*" IsReadOnly="True"/>
<DataGridComboBoxColumn ItemsSource="{Binding ComboItems}" SelectedValueBinding="{Binding Value}" SelectedValuePath="ID" DisplayMemberPath="Text" Header="Value" Width="*" />
</DataGrid.Columns>
</DataGrid>
EDIT: My code currently displays blank selection of combo boxes.
<DataGrid x:Name="AppSettingsDropDowns" AutoGenerateColumns="False" Grid.ColumnSpan="6" Grid.Row="8" CanUserAddRows="False" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Comment}" Header="Comment" Width="4*" IsReadOnly="True" />
<DataGridTextColumn Binding="{Binding Name}" Header="Element Name" Width="*" IsReadOnly="True"/>
<DataGridComboBoxColumn DisplayMemberPath="Text" SelectedValuePath="ID" Header="Value" Width="*" >
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding ComboItems}"/>
<Setter Property="SelectedValue" Value="{Binding Value}" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding ComboItems}"/>
<Setter Property="SelectedValue" Value="{Binding Value}" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>

How to obtain row index and assign it to observable collection property of datagrid

Basically, I have a DataGrid inside a DataGrid:
<DataGrid ScrollViewer.CanContentScroll="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
AutomationProperties.AutomationId="sCGridSelectedBasketItems"
HeadersVisibility="Row"
Height="{Binding ElementName=itemsgrid,Path=ActualHeight}"
Grid.Row="1"
RowDetailsVisibilityMode="Visible"
CanUserAddRows="false"
VerticalAlignment="Top"
AutoGenerateColumns="False"
SelectedItem="{Binding SelectedBasketItem}"
ItemsSource="{Binding SelectedOrderBasketItems}"
CanUserDeleteRows="True">
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid ScrollViewer.CanContentScroll="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
CanUserAddRows="false"
HeadersVisibility="Row"
AutoGenerateColumns="False"
ItemsSource="{Binding SelectedBasketItems}"
SelectedValue="{Binding SelectedBasketItemValue}"
SelectedIndex="{Binding SelectedOrderItemIndex}"
SelectedItem="{Binding SelectedSpecificItemInBasket, Mode=TwoWay}"
SelectionChanged="datagrid_SelectionChanged"
DataGrid.SelectionMode="Single"
Style="{StaticResource GridItemsControlStyle}"
ctrls:DragDrop.DropHandler="{Binding DropHandler}">
<DataGrid.Columns>
<DataGridTextColumn IsReadOnly="True"
Binding="{Binding Path=ItemName}"
Width="195">
<DataGridTextColumn.ElementStyle>
<Style>
<Setter Property="TextBlock.TextWrapping"
Value="Wrap" />
<!--<Setter Property="TextBlock.TextAlignment"
Value="Center" />-->
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Path=RulesCount}"
Background="{Binding Path=RulesCount ,Converter={StaticResource ItemColourConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="115">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Path=LinkedOrderCount}"
Background="{Binding Path=LinkedOrderCount ,Converter={StaticResource ItemColourConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="55">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Path=SDICount}">
<Label.Background>
<MultiBinding Converter="{StaticResource SdiItemColourConverter}">
<Binding ElementName="SDIMaxCnt"
Path="Value" />
<Binding Path="SDICount" />
</MultiBinding>
</Label.Background>
</Label>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
My ObservableCollection has the following fields where I have to bind SelectedItemIndex of each DataGrid row with SelectedOrderItemIndex(which is a property of ObservableCollection class MobileOrderBasketItems.cs):
public int SelectedOrderItemIndex
{
get
{
return selectedOrderItemIndex;
}
set
{
selectedOrderItemIndex = value;
}
}
My Observable collection:
public ObservableCollection<MobileOrderBasketItems> SelectedBasketItems
{
get
{
return _selectedBasketItems ?? (_selectedBasketItems = new ObservableCollection<MobileOrderBasketItems>());
}
set
{
_selectedBasketItems = value;
}
}
My ObservableCollection class definitions:
public class MobileOrderBasketItems
{
private int _sdiMaxCountVal;
private int selectedOrderItemIndex;
public string GrName { get; set; }
public string ItemName { get; set; }
public string RulesCount { get; set; }
public string LinkedOrderCount { get; set; }
public string SDICount { get; set; }
public int SelectedOrderItemIndex
{
get
{
return selectedOrderItemIndex;
}
set
{
selectedOrderItemIndex = value;
}
}
private ObservableCollection<MobileOrderBasketItems> _selectedBasketItems;
public ObservableCollection<MobileOrderBasketItems> SelectedBasketItems
{
get
{
return _selectedBasketItems ?? (_selectedBasketItems = new ObservableCollection<MobileOrderBasketItems>());
}
set
{
_selectedBasketItems = value;
}
}
}
Any help on how to achieve this is much appreciated.

IsEnabled button in datagrid mvvm wpf

How to enable/disable button inside datagrid using mvvm, where column status equals some value in c#?
Thanks.
P.S. I'm new in mvvm and wpf.
Edited:
My code only prints data in Datagrid. I want to disable edit button when column status equals to 1.
You can see my code below.
Model file
public class Reestr
{
private int id;
private string date;
private string market;
private int status;
public int Id
{
get { return id; }
set {
id = value;
}
}
public string Date
{
get { return date; }
set {
date = value;
}
}
public string Market
{
get { return market; }
set {
market = value;
}
}
public int Status
{
get { return status; }
set {
status = value;
}
}
}
ViewModel file
public ReestrViewModel()
{
reestr = GetListOfReestrs();
reestr.Add(new Reestr() { Date = "01.08.2017", Market = "Market1", Status = 0 });
reestr.Add(new Reestr() { Date = "02.08.2017", Market = "Market2", Status = 1 });
}
public ObservableCollection<Reestr> ReestrItems
{
get { return reestr; }
}
public ObservableCollection<Reestr> GetListOfReestrs()
{
return reestr;
}
Xaml file:
<DataGrid GridLinesVisibility="All"
AutoGenerateColumns="False"
CanUserAddRows="False"
x:Name="ReestrDataGrid"
ItemsSource="{Binding ReestrViewModel.ReestrItems}">
<DataGrid.Columns>
<DataGridTextColumn Header="ID"
Binding="{Binding Id}"
IsReadOnly="True"
Visibility="Hidden"
Width="*"/>
<DataGridTextColumn Header="Date"
Binding="{Binding Date}"
IsReadOnly="True"
Width="*"/>
<DataGridTextColumn Header="Market"
Binding="{Binding Market}"
IsReadOnly="True"
Width="*"/>
<DataGridTextColumn Header="Status"
Binding="{Binding Status}"
IsReadOnly="True"
Width="*"/>
<DataGridTemplateColumn Header="Operations" Width="200">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button x:Name="ViewButton"
Click="ViewButton_Click"></Button>
<Button x:Name="EditButton"></Button>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
You could use a Style with a DataTrigger:
<Button x:Name="EditButton">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value="1">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

WPF DataGrid different controls in the same column - incorrect binding

I am developing the application which performs checks on the list of items. Each item has the list of the checks that need to be performed on it. Each check can be one of 3 types: CheckBox, ComboBox, TextBox.
I would like to have Datagrid with 2 columns (one for item name, second for list of checks). Second column contains another DataGrid with 2 columns (one for check name, second for check control). The purpose is to have different types of controls in the same column bound with the Check models.
The problem is that binding with CheckValue doesn't work, however bindings with all the other properties work fine.
The last column contains CheckBoxes, TextBox and ComboBox, however they are not filled with any values.
Does anyone know what is wrong with below code?
Here are examples of model classes
public class Item
{
public string ItemName { get; set; }
public ObservableCollection<Check> Checks { get; set; }
public Item()
{
Checks = new ObservableCollection<Check>();
}
}
public enum CheckType
{
CheckBox,
ComboBox,
TextBox
}
public abstract class Check
{
public string CheckName { get; set; }
public CheckType CheckType { get; protected set; }
public abstract object CheckValue { get; set; }
}
public class CheckBox : Check
{
private bool checkValue;
public CheckBox()
{
CheckType = CheckType.CheckBox;
}
public override object CheckValue
{
get
{
return checkValue;
}
set
{
checkValue = (bool)value;
}
}
}
public class ComboBox : Check
{
private List<string> checkValue;
public ComboBox()
{
CheckType = CheckType.ComboBox;
}
public override object CheckValue
{
get
{
return checkValue;
}
set
{
checkValue = value as List<string>;
}
}
}
public class TextBox : Check
{
private string checkValue;
public TextBox()
{
CheckType = CheckType.TextBox;
}
public override object CheckValue
{
get
{
return checkValue;
}
set
{
checkValue = value as string;
}
}
}
public class MainViewModel
{
public ObservableCollection<Item> Items { get; set; }
public MainViewModel()
{
Items = new ObservableCollection<Item>();
Item item = new Item();
item.ItemName = "First item";
Check check1 = new CheckBox() { CheckName = "Check 1", CheckValue = true };
Check check2 = new CheckBox() { CheckName = "Check 2", CheckValue = false };
Check text1 = new TextBox() { CheckName = "Check 3", CheckValue = "Please enter check" };
Check combo1 = new ComboBox() { CheckName = "Check 4", CheckValue = new List<string> { "Value1", "Value2" } };
item.Checks.Add(check1);
item.Checks.Add(check2);
item.Checks.Add(text1);
item.Checks.Add(combo1);
Items.Add(item);
}
}
And finally here is XAML code of the main window.
<Window x:Class="ItemTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm ="clr-namespace:ItemTest"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<vm:MainViewModel x:Key="mainViewModel"/>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource mainViewModel}}">
<DataGrid ItemsSource="{Binding Path=Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Item" Binding="{Binding ItemName}" />
<DataGridTemplateColumn Header="Checks">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DataGrid ItemsSource="{Binding Checks}" AutoGenerateColumns="False" HeadersVisibility="None">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding CheckName}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding CheckType}" Value="CheckBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox IsChecked="{Binding CheckValue}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding CheckType}" Value="ComboBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ComboBox ItemsSource="{Binding CheckValue}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding CheckType}" Value="TextBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Text="{Binding CheckValue}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
Just set the ItemControl's Content property:
<ContentControl Content="{Binding}">
WPF will automatically set DataTemplate's DataContext to its parent ContentControl's Content. But in your XAML you don't set the Content property (you only specify ContentControl's Style, but forget to set its Content).
And don't forget to set UpdateSourceTrigger=PropertyChanged on your control bindings, otherwise you may see no updates in your viewmodel.
XAML example working, with binding for BindingList :
<DataGrid x:Name="dataGridParametros"
Grid.Row="1"
Margin="5"
AutoGenerateColumns="False"
HeadersVisibility="All"
ItemsSource="{Binding}"
RowHeaderWidth="20"
SelectionUnit="FullRow"
ScrollViewer.CanContentScroll="True"
CanUserAddRows="false"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
FontFamily="Arial"
CellEditEnding="dataGridParametros_CellEditEnding" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding IdParametro}" Header="Id" FontFamily="Arial" IsReadOnly="True" Visibility="Hidden"/>
<DataGridTextColumn Binding="{Binding Codigo}" Header="Código" FontFamily="Arial" IsReadOnly="True"/>
<DataGridTextColumn Width="200" Binding="{Binding Mnemonico}" Header="Mnemonico" FontFamily="Arial" IsReadOnly="True" />
<DataGridTextColumn Width="250*" Binding="{Binding Descricao}" Header="Descrição" FontFamily="Arial" IsReadOnly="True" />
<DataGridTemplateColumn Header="Valor" Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding TipoCampo}" Value="CheckBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox IsChecked="{Binding Valor , Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding TipoCampo}" Value="ComboBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ComboBox ItemsSource="{Binding Valor , Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding TipoCampo}" Value="TextBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Text="{Binding Valor , Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>

Categories