WPF DataGridColumn conditional control - c#

I am currently displaying a hyperlink in my data grid with the following DataGridHyperLinkColumn definition:
<DataGridHyperlinkColumn Header="Item" Binding="{Binding Item, Mode=OneWay}">
<DataGridHyperlinkColumn.ElementStyle>
<Style>
<EventSetter Event="Hyperlink.Click" Handler="ButtonItemInfo_OnClick"/>
</Style>
</DataGridHyperlinkColumn.ElementStyle>
</DataGridHyperlinkColumn>
I want to change this to conditionally display a hyperlink or a label (or textblock). So if the bound value is "SH", I want to display the label. Otherwise I want the hyperlink.
How can I accomplish this?

I would bind the Hyperlink to a command and conditionally return false in CanExecute. You can style the hyperlink for disabled state.
<DataGrid ItemsSource="{Binding Items}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Item">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink Command="{Binding DataContext.Navigate, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding}">
<TextBlock Text="{Binding}" />
</Hyperlink>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
And the ViewModel:
public ObservableCollection<string> Items
{
get;
set;
}
public ICommand Navigate
{
get
{
return new RelayCommand(
(param) => DoNavigate(param as string), // execute
(param) => // can execute
{
var link = param as string;
return link != "SH";
});
}
}
If you really need a textbox for some rows you can use a DataTrigger in the columns cell style.
<DataGrid ItemsSource="{Binding Items}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Item">
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Template">
<Setter.Value>
<!-- Template for normal rows -->
<ControlTemplate>
<TextBlock>
<Hyperlink Command="{Binding DataContext.Navigate,
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding}">
<TextBlock Text="{Binding}" />
</Hyperlink>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value="SH">
<Setter Property="Template">
<Setter.Value>
<!-- Template for SH rows -->
<ControlTemplate>
<TextBlock Text="{Binding}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>

Related

Unable to bind DataTemplateColumn element to another DataTemplateColumn

I am trying to set an element in each DataGrid row by setting another element in the same row.
If IsChecked of the ToggleButton is True, image in the ContentControl will become visible.
I have cannibalised this example to try to get this to work. This example seems similiar too and I hope I am not duplicating anything.
This is the code snippet from my implementation of the DataGrid:
<Grid>
<StackPanel>
<Grid Margin="0" Grid.Column="0" Grid.Row="3">
<DataGrid
ItemsSource="{Binding Path=. , Mode=OneWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
AutoGenerateColumns="False"
Height="Auto"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
x:Name="Filter_grid"
Grid.Row="1">
<DataGrid.Columns >
<DataGridTextColumn Header="CAN ID" Binding="{Binding Information.CAN_ID}" Width="50" />
<DataGridTextColumn Header="Messagen Name" Binding="{Binding Information.CAN_ID_description}" Width="300" />
<DataGridTextColumn Binding="{Binding Information.Status}" Width="50" />
<DataGridTemplateColumn Header = "Filter ON" Width="SizeToCells" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding Information.Tick}">
<ContentControl.Style>
<Style TargetType = "ContentControl" >
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding = "{Binding Path=IsChecked, ElementName=Filter_on}" Value="True">
<Setter Property = "Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn x:Name="F_column" Header ="Select">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton x:Name="Filter_on" Content="Switch" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</StackPanel>
</Grid>
I am unable to get the ElementName to find the ToggleBox and get Cannot find source for binding with reference 'ElementName=Filter_on'.
I have tried doing similiar with
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridTemplateColumn}}, Path=F_column.Filter_on}" Value="True">, or using x:Reference which throws me an exception I can't decipher.
First things first: you should never do this. XAML is very flexible and allows you to do some terribly clever things, but just because you can doesn't mean you should. The correct solution in this case is to create a view model for each row element with a boolean property that both the button and your data trigger can bind to. Apart from being more flexible it's also much easier to test, debug and log etc.
That said, what you are asking is technically possible. DataGrids are actually quite complex due to various optimizations they employ etc, so you still need to use an intermediate property, but since you don't want to use a view model you'll have to use the Tag property in the button's DataGridCell instead (which can be used for arbitrary user data):
Bind the button's IsChecked property to do a one-way-to-source binding to it's parent DataGridCell's Tag property.
Bind the DataTrigger to find the parent DataGridCellsPanel, and then bind directly to the appropriate child's Tag property i.e. Children[1].Tag.
Put it together and you get this:
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header = "Filter ON" Width="SizeToCells" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Style>
<Style TargetType = "ContentControl" >
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding = "{Binding Path=Children[1].Tag, RelativeSource={RelativeSource AncestorType={x:Type DataGridCellsPanel}}}" Value="True">
<Setter Property = "Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
<TextBlock Text="Content goes here" />
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn x:Name="F_column" Header ="Select">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton x:Name="Filter_on" Content="Switch" IsChecked="{Binding Path=Tag, RelativeSource={RelativeSource AncestorType=DataGridCell}, Mode=OneWayToSource}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
But seriously...just use a view model.

Change button in datagrid edit mode with templateselector

I would like to change the behavior of a button in an DataGridTemplateColumn by CellTemplateSelector if the row is newly added or in edit mode.
The button should normaly labeld "DELETE" but when in edit mode it should be labeld "INSERT" and call an other method.
Here what I have found on other examples. But I don't figure out, how to access the DataGrid(Row) in the DataGridButtonSelector.
in my XAML:
<Window.Resources>
<local:DataGridButtonSelector x:Key="DataGridButtonSelector" />
</Window.Resources>
...
<DataGrid x:Name="dgMassnahmen" AutoGenerateColumns="False" Margin="10,10,10.667,46.667" ColumnWidth="SizeToCells" RowHeight="62" RowEditEnding="dgMassnahmen_RowEditEnding" CanUserResizeColumns="False" CanUserResizeRows="False" CanUserReorderColumns="False" CanUserSortColumns="False" AddingNewItem="dgMassnahmen_AddingNewItem">
<DataGrid.Resources>
<DataTemplate x:Key="insertTemplate">
<Button x:Name="btnDelete" Content="Einfügen" Click="Button_Click_2" Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridRow}, Path=DataContext, Converter={StaticResource GridViewButtonVisibilityConverter}}"></Button>
</DataTemplate>
<DataTemplate x:Key="deleteTemplate">
<Button x:Name="btnDelete" Content="Entfernen" Click="btnDelete_Click" Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridRow}, Path=DataContext, Converter={StaticResource GridViewButtonVisibilityConverter}}">
</Button>
</DataTemplate>
</DataGrid.Resources>
...
<DataGridTemplateColumn Width="1*" Header="" CellTemplateSelector="{StaticResource DataGridButtonSelector}">
...
and my DataGridButtonSelector.cs:
class DataGridButtonSelector : DataTemplateSelector
{
public DataTemplate insertTemplate { get; set; }
public DataTemplate deleteTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
// HERE I STUCK!
if (//THE ROW IS IN EDIT MODE (AND SELECTED))
return
element.FindResource("insertTemplate") as DataTemplate;
else
return
element.FindResource("deleteTemplate") as DataTemplate;
}
}
you can simply use a trigger and set whatever you want in it:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="agggsdf" ></TextBlock>
<Button Name="f"></Button>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=IsEditing}" Value="True">
<Setter TargetName="f" Property="Content" Value="remove"></Setter>
<Setter TargetName="f" Property="Style">
<Setter.Value>
<Style TargetType="Button">
<EventSetter Event="Click" Handler="Button_Click"></EventSetter>
</Style>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=IsEditing}" Value="False">
<Setter TargetName="f" Property="Content" Value="insert"></Setter>
<Setter TargetName="f" Property="Style">
<Setter.Value>
<Style TargetType="Button">
<EventSetter Event="Click" Handler="f_Click"></EventSetter>
</Style>
</Setter.Value>
</Setter>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>

Binding inside DataTemplate don't work

I am won to change View with DataTriger, but when bindings inside Triger don't work.
What i'm do wrong?
my style in resources
<Style x:Key="AgentPositionContentTemplateSelector" TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate >
<!--Agent name dont changed, Binding to "Agent" property dont work -->
<TextBlock Background="BlueViolet" Text="{Binding Agent}"></TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<!--But this binding work perfect! DataTemplate changed!-->
<DataTrigger Binding="{Binding Agent.PositionId, Converter={StaticResource IntToPositionDictionaryConverter} }"><!--Int to Enum-->
<DataTrigger.Value>
<enum:PositionDictionary>Merchandiser</enum:PositionDictionary>
</DataTrigger.Value>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<!--And this binding don't work also!-->
<TextBlock Background="Aqua" Text="{Binding Agent}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
My View:
<ListBox Name="AgentsListBox" IsSynchronizedWithCurrentItem="True" SelectionMode="Single"
DisplayMemberPath="Agent" Grid.Column="0" ItemsSource="{Binding CCRTeamRows}"><!--SelectedItem="{Binding SelectedAgent}"-->
</ListBox>
<!--This also work correct-->
<TextBlock Text="{Binding Agent, Mode=OneWay, UpdateSourceTrigger=PropertyChanged }"></TextBlock>
<TextBlock Text="{Binding Position, Mode=OneWay, UpdateSourceTrigger=PropertyChanged }"></TextBlock>
<TextBlock Text="{Binding Claster, UpdateSourceTrigger=PropertyChanged }"></TextBlock>
<ContentControl Style="{DynamicResource AgentPositionContentTemplateSelector}" />
Please take me on right way.
In the DataTemplates you'll need to use RelativeSource binding up to the ListBoxItem and then use Path=DataContext.Agent:
Text="{Binding Path=DataContext.Agent, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}"

Change Binding depending on RadioButton

I have a textbox with an Binding to an ObservableCollection with a triple tuple and want to change the Binding depending on two radiobuttons:
XAML:
<ItemsControl ItemsSource="{Binding DataInformation, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox x:Name="xTextbox" Grid.Row="0"
IsReadOnly="True"
Text="{Binding Path=Item2 , Mode=OneWay}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl >
<RadioButton Grid.Row="1"
x:Name="PatoRadioButton"
Width="150"
Content="Type Pato"
Checked="PatoRadioButtonChecked" />
<RadioButton Grid.Row="2"
x:Name="FifoRadioButton"
Width="150"
Content="Type Fifo"
Checked="FifoRadioButtonChecked" />
VIEWMODEL:
private ObservableCollection<Tuple<int, string, string>> dataInformation;
public ObservableCollection<Tuple<int, string, string>> DataInformation
{
get
{
return DataInformation;
}
set
{
dataInformation = value;
NotifyPropertyChanged("DataInformation");
}
}
QUESTION:
How can I change the Text="{Binding Path=Item2 , Mode=OneWay}" of the TextBox depending on the checked RadioButton?
PatoRadioButton is checked then: Text="{Binding Path=Item2 , Mode=OneWay}"
FifoRadioButton is checked then: Text="{Binding Path=Item3 , Mode=OneWay}"
You could add 2 Triggers to the TextBox of your DataTemplate:
<DataTemplate>
<TextBox x:Name="xTextbox" Grid.Row="0" IsReadOnly="True">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked,
ElementName=PatoRadioButton}" Value="True">
<DataTrigger.Setters>
<Setter Property="Text" Value="{Binding Path=Item2,
Mode=OneWay" />
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger Binding="{Binding IsChecked,
ElementName=FifoRadioButton}" Value="True">
<DataTrigger.Setters>
<Setter Property="Text" Value="{Binding Path=Item3,
Mode=OneWay}" />
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
The triggers will set the Binding of the TextBlock when the IsChecked property is changed.

DataGrid's CellEditingTemplate and focus in edit mode

I am having an issue with WPFToolkit DataGrid when a column is customized supplying both CellTemplate and CellEditingTemplate. If you take a look below, you will see my editing template has a single CheckBox. All is fine in a functional sense but when F2 is hit to edit the cell, one must also hit TAB in order for the CheckBox to receive focus. Ideally, one would hit F2 and SPACE to toggle the value. Currently, one must hit F2, TAB, SPACE. I have tried setting TabIndex to no avail. I am running out of ideas.
<WPFToolkit:DataGridTemplateColumn Header="Turn"
MinWidth="60">
<WPFToolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Height="16">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding CanTurn}" Value="True">
<Setter Property="Source" Value="/Images/16/Tick.png" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</DataTemplate>
</WPFToolkit:DataGridTemplateColumn.CellTemplate>
<WPFToolkit:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=CanTurn}" HorizontalAlignment="Center" HorizontalContentAlignment="Center" />
</DataTemplate>
</WPFToolkit:DataGridTemplateColumn.CellEditingTemplate>
</WPFToolkit:DataGridTemplateColumn>
Try this
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<CheckBox Name="checkbox" IsChecked="{Binding Path=CanTurn}" HorizontalAlignment="Center" HorizontalContentAlignment="Center" />
<DataTemplate.Triggers>
<Trigger SourceName="checkbox" Property="IsVisible" Value="True">
<Setter TargetName="checkbox" Property="FocusManager.FocusedElement" Value="{Binding ElementName=checkbox}" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
Or this...
<DataGridTemplateColumn Header="Long" IsReadOnly="False" Width="100">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<CheckBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}" IsChecked="{Binding Path=CanTurn}" HorizontalAlignment="Center" HorizontalContentAlignment="Center" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
if you want to set the focus on edit and select the text given by a Binding try this.
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Parameter0, Mode=TwoWay}" Loaded="TbLoaded" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
code behind:
private void TbLoaded(object sender, EventArgs e)
{
TextBox tb = sender as TextBox;
if (tb == null) return;
tb.SelectAll();
FocusManager.SetFocusedElement(this, tb);
}

Categories

Resources