Use DataTrigger in DataGridTemplateColumn.CellStyle - wpf

I have a datagrid as follows,
<DataGrid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" ItemsSource="{Binding SelectedSet.Rows}" IsSynchronizedWithCurrentItem="True" AutoGenerateColumns="False" CanUserAddRows="False" Style="{StaticResource DataGridStyle2}" HeadersVisibility="Column" SelectedItem="{Binding SelectedItem}" EnableRowVirtualization="False">
<DataGrid.Columns>
<DataGridTemplateColumn Width="30*" Header="{StaticResource RangeColumnHeader}" HeaderStyle="{StaticResource HeaderStyle2}" SortMemberPath="StartValue">
<DataGridTemplateColumn.CellStyle>
<DataTrigger Binding="{Binding SelectedSet.IsDefault}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding SelectedSet.IsDefault}" Value="False">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DatGrid>
I Want use " SelectedSet's " IsDefault property to set cell's IsEnabled property.
I tried above code, which is not working.
Classes are defined as follows,
public class UCSetModel : ViewModelBase
{
private Set _SelectedSet;
public Set SelectedSet
{
get
{
return _SelectedSet;
}
set
{
_SelectedSet = value;
RaisePropertyChanged("SelectedSet");
}
}
}
public class Set
{
private ObservableCollection<Markers> _rows;
public ObservableCollection<Markers> Rows
{
get
{
return _rows;
}
set
{
_rows = value;
RaisePropertyChanged("Rows");
}
}
private bool _isDefault;
public bool IsDefault
{
get
{
return _isDefault;
}
set
{
_isDefault = value;
RaisePropertyChanged("IsDefault");
}
}
}
I want to bind to a property of the same 'SelectedSet' that the rows come from.

The markup you have posted won't even compile. This does:
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<DataTrigger Binding="{Binding IsDefault}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
IsDefault is supposed to be a property of a Row object, i.e. you should remove "Set." from the binding path assuming that a row doesn't have a Set property.
If you want to bind to a property of the same SelectedSet that the rows come from, the binding should be defined like this:
<DataTrigger Binding="{Binding DataContext.SelectedSet.IsDefault, RelativeSource={RelativeSource AncestorType=DataGrid}}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>

This worked for me,
<DataTrigger Binding="{Binding Path=DataContext.SelectedSet.IsDefault,ElementName=SetWindow}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>

Related

Binding value with conditional

I'm binding a value to my DataGrid in WPF, and I would like to know how can I do something like this.
I have a int field (Category) into my model, and I would like to show a description string in my DataGrid.
Example:
The 'Category' field have this values 1,2,3,4,5 and each value has a description, how can I show the description and not the code in datagrid binding?
I've tried to create a method in my ViewModel and this method will return the string description.
//ViewModel
public string GetDescription(int pCategory)
{
if(pCategory == 1)
return "01. Description";
else if(pCategory == 2)
return "02. Description";
else if(pCategory == 3)
return "03. Description";
else
return "None";
}
//XAML DataGrid binding code
<DataGridTextColumn IsReadOnly="True" Header="Category" Binding="{Binding GetDescription(Category)}"/>
In the view use a DataGridTemplateColumn and add data triggers, something like this
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Text" Value="None"/>
<Style.Triggers>
<DataTrigger Binding="{Binding pCategory}" Value="1">
<Setter Property="Text" Value="01. Description"/>
</DataTrigger>
<DataTrigger Binding="{Binding pCategory}" Value="2">
<Setter Property="Text" Value="02. Description"/>
</DataTrigger>
<DataTrigger Binding="{Binding pCategory}" Value="3">
<Setter Property="Text" Value="03. Description"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

WPF Datagrid clipboard get cell value

I need to handle the Ctrl+C function in my datagrid, especially I need to get the cell of a selected value. Here's the XAML code of the datagrid. Please note that I use DataGridTextColumn
<DataGrid x:Name="GridFormule" Grid.Row="0" BorderBrush="#abadb3" CanUserSortColumns="true" Sorting="GridFormule_Sorting" MaxColumnWidth="Infinity" Style="{DynamicResource DataGridStyle}" ColumnHeaderStyle="{DynamicResource DataGridColumnHeaderStyle}" ItemsSource="{Binding ElementName=ObjectsTree, Path=SelectedItem.Infos}" CellEditEnding="GridFormule_CellEditEnding" Margin="8,5,2,0" ContextMenu="{StaticResource cntextListe}">
<!-- OVERRIDE COPY CONTROL -->
<DataGrid.InputBindings>
<KeyBinding Key="C" Modifiers="Control" Command="Copy" />
</DataGrid.InputBindings>
<DataGrid.CommandBindings>
<CommandBinding Command="Copy" Executed="Comandi_Executed" />
</DataGrid.CommandBindings>
<!-- /OVERRIDE COPY CONTROL -->
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Field.Formula.Formula, Mode=TwoWay}" Header="#_57_Formule" Foreground="Black" Width="*" ClipboardContentBinding="{Binding Field.Formula.Formula, Mode=TwoWay}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Padding" Value="4" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="11.55" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsDirty}" Value="True">
<Setter Property="TextBlock.Background" Value="{StaticResource IsDirtyColor}" />
<Setter Property="Padding" Value="4" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="Background" Value="White"></Setter>
<Setter Property="Padding" Value="2,4,2,3"></Setter>
<Setter Property="BorderThickness" Value="0"></Setter>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
The code behind to handle the copy:
private void Comandi_Executed(object sender, ExecutedRoutedEventArgs e)
{
RoutedUICommand c = (RoutedUICommand)e.Command;
switch (c.Name)
{
// Copia
case "Copy":
DataGrid sel = (sender as DataGrid);
if (sel == null) return;
if (sel.CurrentItem != null) return;
var cc = sel.CurrentColumn;
Binding binding = (Binding)cc;
// Here I get the property Name!
string BoundPropName = binding.Path.Path;
try
{
//Clipboard.SetDataObject(text);
}
catch (Exception ex)
{
Global.LOG.Log(ex.Message);
}
break;
}
}
Basically my BoundPropName returns "Field.Formula.Formula" instead of the value visible in the cell of datagrid. How can I get the cell value?

WPF - DataGrid column binding to attached property not working on context menu

I have ToggleButtons and a DataGrid, each row in DataGridColumn has a ColGroup AttachedProperty set to the name of the column group name.
Attached property:
public class DataGridColumnsGroupProperty {
public static readonly DependencyProperty ColGroupProperty =
DependencyProperty.RegisterAttached("ColGroup", typeof(object), typeof(DataGridColumnsGroupProperty), new FrameworkPropertyMetadata(null));
public static void SetColGroup(DependencyObject element, string value) {
element.SetValue(ColGroupProperty, value);
}
public static string GetColGroup(DependencyObject element) {
return (string)element.GetValue(ColGroupProperty);
}
}
The ToggleButtons has two jobs, on Check/UnCheck show/collapse all columns with the same group name.
and it has a ContextMenu which shows only the DataGridColumns with the same group name.
I've managed to bind all DataGridColumns to the ToggleButton, but couldn't find a way to Collapse the DataGridColumns with different group names.
How to fill context menu with only the columns with the givin group name inside the Style Trigger?
And how to hid all columns that has the group name when un-check toggle button?
XAML:
<ToggleButton.ContextMenu>
<ContextMenu x:Name="ContextMenu" ItemsSource="{Binding Columns, ElementName=ElementDataGrid, Converter={StaticResource TestConverter}}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="HeaderTemplate" Value="{Binding HeaderTemplate}"/>
<Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="StaysOpenOnClick" Value="True" />
<Setter Property="AutomationProperties.Name" Value="{Binding Header}"/>
<Setter Property="IsCheckable" Value="True" />
<Setter Property="IsChecked" Value="{Binding Visibility, Mode=TwoWay, Converter={StaticResource VisibilityToBooleanConverter}}" />
<Style.Triggers>
<Trigger Property="attachedProperties:DataGridColumnsGroupProperty.ColGroup" Value="FirstGroup">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</ToggleButton.ContextMenu>
DataGridColumns:
<DataGridTextColumn x:Name="StoryCol" attachedProperties:DataGridColumnsGroupProperty.ColGroup="FirstGroup" Header="{x:Static p:Resources.Story}" IsReadOnly="True" Binding="{Binding Story}" Visibility="Visible" />
<DataGridTextColumn x:Name="CadIdCol" attachedProperties:DataGridColumnsGroupProperty.ColGroup="SecondGroup" Header="{x:Static p:Resources.CadId}" IsReadOnly="False" Binding="{Binding CadId}" Visibility="Visible" />
Using a DataTrigger should work as far as the binding to the attached property is concerned:
<DataTrigger Binding="{Binding Path=(attachedProperties:DataGridColumnsGroupProperty.ColGroup)}" Value="FirstGroup">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>

Unable to disable rows in WPF DataGrid using Style trigger

I want to disable rows based on a property "IsEditable".
This is my RowStyle
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsEditable}" Value="False">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
I have implemented INotifyPropertyChanged.
I'm able to disable the entire grid using the same property but I'm unable to disable rows.
"IsEditable" property is defined in my ViewModel. My datacontext is also the ViewModel.
This is my grid code
<DataGrid RowHeaderWidth="0" MouseDoubleClick="listViewItem_MouseDoubleClick"
AutoGenerateColumns="False" IsReadOnly="True" SelectionMode="Extended" SelectionUnit="FullRow"
BorderBrush="#FF898C95" ItemsSource="{Binding EqpGrpList}" CanUserSortColumns="False" HorizontalGridLinesBrush="#FFDEDBDB"
VerticalGridLinesBrush="#FFDEDBDB" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Margin="0,-5,0,0" Background="White" CellStyle="{StaticResource DataGridCellStyle}">
<DataGrid.Columns...>
</DataGrid>
I have included the row style in UserControl.Resources.
Bind your property into DataGrid ItemCollection Class.
xaml....
<Style x:Key="DataGridCellStyle" TargetType="DataGridRow">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsEnable}" Value="False">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
<DataGrid RowHeaderWidth="0"
AutoGenerateColumns="False" IsReadOnly="True" SelectionMode="Extended" SelectionUnit="FullRow"
BorderBrush="#FF898C95" ItemsSource="{Binding GridData}" CanUserSortColumns="False" HorizontalGridLinesBrush="#FFDEDBDB"
VerticalGridLinesBrush="#FFDEDBDB" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Margin="0,-5,0,0" Background="White" RowStyle="{StaticResource DataGridCellStyle}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="City" Binding="{Binding City}"/>
</DataGrid.Columns>
</DataGrid>
Code...
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
for(int i=0;i<10;i++)
{
GridData.Add(new UserData { Name = "NAME " + i, City = "CITY " + i });
}
}
private ObservableCollection<UserData> _gridData=new ObservableCollection<UserData>();
public ObservableCollection<UserData> GridData
{
get { return _gridData; }
set { _gridData = value; }
}
class....
public class UserData
{
public string Name { get; set; }
public string City { get; set; }
private bool _isEnable = false;
public bool IsEnable
{
get { return _isEnable; }
set { _isEnable = value; }
}
}

How to check if a row has odd number?

I'm trying to set different color for odd rows using XAML.
The datagrid in question has 3 different types of data, which I want to color differently, and simply changing AlternatingRowBackground won't do.
I'm planning on using something like
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Type}" Value="0"/>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
<Condition Binding="{Binding IsOddRow, RelativeSource={RelativeSource Self}}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="#FFDFE6ED"/>
</MultiDataTrigger>
There doesn't seem to be such a property as IsOddRow. What property should I check instead?
You can set AlternationCount on the DataGrid and then bind to the ancestor DataGridRows attached property ItemsControl.AlternationIndex. If the value is "1" you have an odd row number.
<DataGrid ItemsSource="{Binding ...}"
AlternationCount="2">
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Type}" Value="0"/>
<Condition Binding="{Binding RelativeSource={RelativeSource Self},
Path=IsSelected}"
Value="False"/>
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}},
Path=(ItemsControl.AlternationIndex)}"
Value="1"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="#FFDFE6ED"/>
</MultiDataTrigger>
<!-- ... -->
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
<!-- ... -->
</DataGrid>
Note that when binding to an attached property, you must put parentheses around the attached property. Path=(ItemsControl.AlternationIndex) will work but Path=ItemsControl.AlternationIndex won't.
Update
You could also create the property IsOddRow through an attached behavior. In the behavior you subscribe to LoadingRow. In the event handler you get the index for the loaded row and check if it is odd or not. The result is then stored in an attached property called IsOddRow which you can bind to.
To start the behavior add behaviors:DataGridBehavior.ObserveOddRow="True" to the DataGrid
<DataGrid ItemsSource="{Binding ...}"
behaviors:DataGridBehavior.ObserveOddRow="True">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Type}" Value="0"/>
<Condition Binding="{Binding Path=IsSelected,
RelativeSource={RelativeSource Self}}" Value="False"/>
<Condition Binding="{Binding Path=(behaviors:DataGridBehavior.IsOddRow),
RelativeSource={RelativeSource Self}}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="#FFDFE6ED"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
DataGridBehavior
public class DataGridBehavior
{
#region ObserveOddRow
public static readonly DependencyProperty ObserveOddRowProperty =
DependencyProperty.RegisterAttached("ObserveOddRow",
typeof(bool),
typeof(DataGridBehavior),
new UIPropertyMetadata(false, OnObserveOddRowChanged));
[AttachedPropertyBrowsableForType(typeof(DataGrid))]
public static bool GetObserveOddRow(DataGrid dataGrid)
{
return (bool)dataGrid.GetValue(ObserveOddRowProperty);
}
public static void SetObserveOddRow(DataGrid dataGrid, bool value)
{
dataGrid.SetValue(ObserveOddRowProperty, value);
}
private static void OnObserveOddRowChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
DataGrid dataGrid = target as DataGrid;
dataGrid.LoadingRow += (object sender, DataGridRowEventArgs e2) =>
{
DataGridRow dataGridRow = e2.Row;
bool isOddRow = dataGridRow.GetIndex() % 2 != 0;
SetIsOddRow(dataGridRow, isOddRow);
};
}
#endregion // ObserveOddRow
#region IsOddRow
public static DependencyProperty IsOddRowProperty =
DependencyProperty.RegisterAttached("IsOddRow",
typeof(bool),
typeof(DataGridBehavior),
new PropertyMetadata(false));
[AttachedPropertyBrowsableForType(typeof(DataGridRow))]
public static bool GetIsOddRow(DataGridRow dataGridCell)
{
return (bool)dataGridCell.GetValue(IsOddRowProperty);
}
public static void SetIsOddRow(DataGridRow dataGridCell, bool value)
{
dataGridCell.SetValue(IsOddRowProperty, value);
}
#endregion // IsOddRow
}
I am not sure which grid/row type you are using, so I can't give you the exact property names, however, bind to the row's index (row number) and use a value converter (that returns true) to check if the row is odd or even.
Almost every answer use AlternationCount="2" but I find it a little bit too limiting. On my side I use something like AlternationCount="{ Binding MainData.ProjColl.Count}" in order to number my rows until the end ( take care it starts at 0 ! ).
In this case I need a value Converter as mentioned by #Danny Varod.
I use the converter to alternate the color of the rows ( nearly answering the question )
public class IsEvenConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
bool res = false;
int? val = value as int?;
if (null != val)
res = (0 == (val % 2));
return res;
}
...
}
And the calling XAML
<UserControl ...
<UserControl.Resources>
...
<vm_nmspc:IsEvenConverter x:Key="IsEven"/>
<Style TargetType="DataGridRow">
<Setter Property="Width" Value="Auto"/>
<Setter Property="Background" Value="LightGray"/>
<!--Converter will be used below-->
<Style.Triggers>
...
<Setter Property="Background" Value="LightGray"/
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
Path=(ItemsControl.AlternationIndex),
Converter={StaticResource ResourceKey=IsEven}}" Value="true">
<Setter Property="Background" Value="Lavender"/>
</DataTrigger>
<Trigger Property="IsMouseOver" Value="True" >
<Setter Property="Background" Value="LightGreen"/>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<DataGrid ItemsSource="{Binding MainData.ProjColl}" AutoGenerateColumns="False"
AlternationCount="{ Binding MainData.ProjColl.Count}" >
...
<DataGridTextColumn Header="Project Name" ....
</DataGrid>
</Grid>
</UserControl>
and some discrete screenshots
Another part of the same code:
Simple way to display row numbers on WPF DataGrid

Resources