WPF Datagrid clipboard get cell value - wpf

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?

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

data trigger with enum

I have an Enum:
namespace LibiqCommonStructures
{
using System;
public enum ItemStatusType
{
NotImplemented,
New,
Processed,
Missing,
NotUsed,
Failed
}
}
<igWPF:XamDataGrid DataSource="{Binding Path=Images}" ActiveDataItem="{Binding SelectedItem}" MaxHeight="300">
<igWPF:XamDataGrid.Resources>
<Style TargetType="{x:Type igWPF:DataRecordCellArea}" BasedOn="{StaticResource {x:Type igWPF:DataRecordCellArea}}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Record.DataItem.ItemStatus}" Value="{x:Static libIq:ItemStatusType.Missing}">
<Setter Property="Background" Value="Tomato"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</igWPF:XamDataGrid.Resources>
<igWPF:XamDataGrid.FieldLayoutSettings>
<igWPF:FieldLayoutSettings SelectionTypeRecord="Single" AutoGenerateFields="False" AllowDelete="False"/>
</igWPF:XamDataGrid.FieldLayoutSettings>
<igWPF:XamDataGrid.FieldSettings >
<igWPF:FieldSettings Width="Auto" AllowEdit="False" />
</igWPF:XamDataGrid.FieldSettings>
<igWPF:XamDataGrid.FieldLayouts>
I want to change the background color of the row when the enum is ItemStatusType.Missing
i can see missing the in the binding but the color of the row doesn't change.
maybe something is wrong with my x:static?
Just put the enum string in the Value. It will work:
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Record.DataItem.ItemStatus}" Value="Missing">
<Setter Property="Background" Value="Tomato"></Setter>
</DataTrigger>

Set 2 type controls(TextBox and ComboBox ) in same DataGridTemplateColumn in XAML

I have a DataGrid.
Inside it contains some columns; 2 are related to this question, one is a DataGridTextColumn(x:Name="varTypeColumn") which show Variable type, another is a DataGridTemplateColumn(x:Name="varValueColumn") which could be a TextBox or ComboBox inside.
If varTypeColumn is Bool type, varValueColumn should show a ComboBox which contains 2 items: True, False. If varTypeColumn is Int Type, varValueColumn should show a TextBox which let user input string.
So my question is that, is it possible to do it in xaml? I find some implementation which do it in .cs code, it try to get Row and get the Cell, at last set TextBox/ComboBox instance to Cell's Content property. It can work, but if the DataGrid contains big number items(e.g., more than 5000), it is very very slow to display it.
below is the code part:
private void InitEditors()
{
for (int i = 0; i < _devLinkCollectionView.Count; i++)
{
DataGridRow row = devLinkDataGrid.GetRow(i);
InitEditor(row);
}
}
private void InitEditor(DataGridRow row)
{
DevLink link = row.Item as DevLink;
if (link != null)
{
if (link.HasErrors)
{
ToolTipService.SetShowOnDisabled(row, true);
row.IsEnabled = false;
return;
}
// Create binding first
Binding binding = new Binding("DefaultValue")
{
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.LostFocus,
Source = link
};
DataGridCell cell = devLinkDataGrid.GetCell(row, 4);
switch (link.VariableType)
{
case CarelStandardDataType.Bool:
ComboBox comboBox = new ComboBox();
comboBox.ItemsSource = new[] {string.Empty, "TRUE", "FALSE" };
comboBox.SetBinding(ComboBox.SelectedValueProperty, binding);
cell.Content = comboBox;
break;
default:
TextBox textBox = new TextBox();
textBox.Style = (Style)FindResource("TextBoxInError");
cell.Content = textBox;
binding.ValidationRules.Add(new DevLinkValidationRule(link));
textBox.SetBinding(TextBox.TextProperty, binding);
break;
}
}
}
maybe this is what you want.
<Style x:Key="DataGridCellStyle1" TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Grid Height="21.96">
<ComboBox x:Name="cbCondition1">
<ComboBoxItem Content="1"/>
<ComboBoxItem Content="2"/>
</ComboBox>
<TextBox x:Name="tbCondition2" Text="text"/>
</Grid>
<ControlTemplate.Triggers>
<!--In your case, you should use custom Converter, just return type, or maybe you'd better add typeProperty in your model-->
<DataTrigger Binding="{Binding TypeColumn}" Value="Int">
<Setter Value="Visible" TargetName="cbCondition1" Property="Visibility"/>
<Setter Value="Hidden" TargetName="tbCondition2" Property="Visibility"/>
</DataTrigger>
<DataTrigger Binding="{Binding TypeColumn}" Value="Bool">
<Setter Value="Hidden" TargetName="cbCondition1" Property="Visibility"/>
<Setter Value="Visible" TargetName="tbCondition2" Property="Visibility"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static DataGrid.FocusBorderBrushKey}}"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource SampleDataSource}}">
<DataGrid Margin="160,106.5,119,139" ItemsSource="{Binding Collection}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding TypeColumn}"/>
<DataGridTemplateColumn CellStyle="{StaticResource DataGridCellStyle1}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
==================sample data===============================
<SampleData:SampleDataSource xmlns:SampleData="clr-namespace:Expression.Blend.SampleData.SampleDataSource">
<SampleData:SampleDataSource.Collection>
<SampleData:Item TypeColumn="Int" ValueColumn="Row1"/>
<SampleData:Item TypeColumn="Bool" ValueColumn="Row2"/>
</SampleData:SampleDataSource.Collection>
</SampleData:SampleDataSource>

Setting DataGrid.GroupStyle in style

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

Resources