Setting DataGrid.GroupStyle in style - wpf

I want to set DataGrid.GroupStyle in my style, so it will be mutual for all datagrids, and not in my view file, where I need to set it to each datagrid separatedly.
This is what I have now:
<Style x:Key="DGView" TargetType="DataGrid">
<Setter Property="Height" Value="Auto" />
<Setter Property="Margin" Value="0,3,0,0" />
<Setter Property="RowHeaderWidth" Value="0" />
<Setter Property="SelectionMode" Value="Single" />
<Setter Property="SelectionUnit" Value="FullRow" />
<Setter Property="CanUserAddRows" Value="False" />
<Setter Property="CanUserDeleteRows" Value="False" />
<Setter Property="AutoGenerateColumns" Value="False" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="DataGrid.CellStyle" Value="{StaticResource DGCell}" />
</Style>
<DataGrid Style="{StaticResource DGSecondaryView}" >
<DataGrid.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GIView}" >
<GroupStyle.Panel>
<ItemsPanelTemplate>
<DataGridRowsPresenter/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="BookID" Binding="{Binding BookID}" Visibility="Collapsed"/>
<DataGridTextColumn Header="Title" Binding="{Binding Title, Converter={StaticResource StringToOffset}}" Width="3*" IsReadOnly="True" />
<DataGridTextColumn Header="Sequence" Binding="{Binding Sequence}" Width="3*" IsReadOnly="True" />
<DataGridTextColumn Header="Number" Binding="{Binding Number}" Width="1*" IsReadOnly="True" CellStyle="{StaticResource Numeric}" />
</DataGrid.Columns>
</DataGrid>
What I want, is to insert the following code into the style tag. Is it possible?
<DataGrid.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GIView}" >
<GroupStyle.Panel>
<ItemsPanelTemplate>
<DataGridRowsPresenter/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</DataGrid.GroupStyle>

I needed to do this for a ListBox so I created new control that inherited from ListBox and added a bindable and stylable property. The same could be done for a DataGrid.
public class MyListBox : ListBox
{
public GroupStyle DefaultGroupStyle
{
get { return (GroupStyle)GetValue(DefaultGroupStyleProperty); }
set { SetValue(DefaultGroupStyleProperty, value); }
}
// Using a DependencyProperty as the backing store for DefaultGroupStyle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DefaultGroupStyleProperty =
DependencyProperty.Register("DefaultGroupStyle", typeof(GroupStyle), typeof(MyListBox), new UIPropertyMetadata(null, DefaultGroupStyleChanged));
private static void DefaultGroupStyleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((MyListBox)o).SetDefaultGroupStyle(e.NewValue as GroupStyle);
}
private void SetDefaultGroupStyle(GroupStyle defaultStyle)
{
if (defaultStyle == null)
{
return;
}
if (this.GroupStyle.Count == 0)
{
this.GroupStyle.Add(defaultStyle);
}
}
}

Related

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; }
}
}

Set DataGrid column width to Star in style

how I can set in XAML width of DataGrid header to *?
This code doesn't work:
<DataGrid>
<DataGrid.Resources>
<Style x:Key="CbClmn" TargetType="DataGridColumnHeader">
<Setter Property="Width" Value="30" />
</Style>
<Style x:Key="TxtClmn" TargetType="DataGridColumnHeader">
<Setter Property="Width" Value="*" />
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridCheckBoxColumn HeaderStyle="{StaticResource CbClmn}" />
<DataGridTextColumn Header="Title" HeaderStyle="{StaticResource TxtClmn}" />
<DataGridCheckBoxColumn Header="Selected" HeaderStyle="{StaticResource CbClmn}" />
</DataGrid.Columns>
</DataGrid>
Style TxtClmn generates exception '*' string cannot be converted to Length.
Look in the object browser:
//DataGridColumn
public System.Windows.Controls.DataGridLength Width { set; get; }
//DataGridColumnHeader
public double Width { set; get; }
That's why u can't do that. It's of double type.

Create DataGrid column separator

Is there a way to create a visual separator between two particular columns in a DataGrid? It doesn't need to be fancy, maybe just a double line or a thicker border.
In case it's something like this
You can achieve using a custom style:
<Window.Resources>
<Style x:Key="DataGridColumnSeparatorStyle" TargetType="DataGridCell">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Rectangle VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Fill="Gray"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<DataGrid x:Name="dataGrid" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Start"/>
<DataGridTextColumn Header="End"/>
<!-- Separator column -->
<DataGridTemplateColumn MinWidth="0" Width="2" CellStyle="{StaticResource DataGridColumnSeparatorStyle}"/>
<DataGridTextColumn Header="Start"/>
<DataGridTextColumn Header="End"/>
</DataGrid.Columns>
</DataGrid>
If you generate the columns in code-behind, either by autogenerate columns or other, you can still create the separator column by getting the resource from the XAML:
DataGridTextColumn s1 = new DataGridTextColumn() { Header = "Start" };
DataGridTextColumn s2 = new DataGridTextColumn() { Header = "Start" };
DataGridTextColumn e1 = new DataGridTextColumn() { Header = "End" };
DataGridTextColumn e2 = new DataGridTextColumn() { Header = "End" };
DataGridTemplateColumn column = new DataGridTemplateColumn();
column.MinWidth = 0;
column.Width = 2;
var separatorStyle = (Style)FindResource("DataGridColumnSeparatorStyle");
column.CellStyle = separatorStyle;
dataGrid.Columns.Add(s1);
dataGrid.Columns.Add(e1);
dataGrid.Columns.Add(column);
dataGrid.Columns.Add(s2);
dataGrid.Columns.Add(e2);
I think you have two options. The simplest option will probably be to use a Style and set your DataGrid.CellStyle to the defined style.
<Style x:Key="DataGridBorder" TargetType="DataGridCell">
<Setter Property="BorderBrush" Value="LightGray" />
<Setter Property="BorderThickness" Value="1,1,1,1" />
</Style>
...
<DataGrid CellStyle="{StaticResource DataGridBorder}">
...
The other option is to use a CellTemplate in conjunction with the DataGridTemplateColumn.
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border BorderBrush="LightGray" BorderThickness="1,1,1,1" Margin="-6,-6,-6,-6">
<Grid Margin="6,6,6,6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Whatever}" Grid.Column="0" TextTrimming="CharacterEllipsis" VerticalAlignment="Center" />
</Grid>
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
I haven't tested either of these, and you may have to play with the margins a little bit.
Try this. When navigating through cells using keyboard Tab key, it won't focus the seperator column:
<DataGridTemplateColumn MinWidth="2" MaxWidth="2" IsReadOnly="True" CanUserResize="False">
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="Gray" />
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="BorderThickness" Value="2" />
</Style>
</DataGridTemplateColumn.HeaderStyle>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="Focusable" Value="False" />
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
Source

Resources