Binding from DataTrigger - c#

Trying to bind to the Tag property of a textbox from a datatrigger, but doesnt seem to get the binding corrrect
Partial xaml
<TextBox Grid.Column="2" Tag="Enter password" Text="{Binding UserName, Mode=TwoWay}" Name="textBox1" Width="200" Height="25" HorizontalAlignment="Left" >
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value="">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Text="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type TextBox}},
Path=Tag}"
Foreground="Gray"/>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
Its the part
<TextBlock Text="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type TextBox}},
Path=Tag}"
that is not working. I have tried a couple of solutions, but textbox is always ending up empty.

Related

How to change the display data format of the WPF DatePicker

I got this sample code from this forum.
<DatePicker SelectedDate="{Binding MainReportEndDate, Mode=TwoWay}"
DataContext="{Binding DataContext, RelativeSource= {RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}}"
DisplayDateStart="1/01/20" DisplayDateEnd="12/31/22"
FirstDayOfWeek="Monday"">
<DatePicker.Resources>
<Style TargetType="{x:Type DatePickerTextBox}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<TextBox x:Name="PART_TextBox"
Text="{Binding Path=SelectedDate,
RelativeSource={RelativeSource AncestorType={x:Type DatePicker}},
StringFormat={}{0:MMM yy}}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DatePicker.Resources>
</DatePicker>
But it doesn't work. I want the same result as the one in the picture "Jul 20"
Try this
<TextBox x:Name="PART_TextBox"
Text="{Binding Path=SelectedDate, StringFormat='dd MMM yyyy',
RelativeSource={RelativeSource AncestorType={x:Type DatePicker}}}" />

Different Content (System.Windows.DataTemplates) displayed by ContentControl rather than required Icon

I am working on a combo box with icons.This combo box consist of items which have a name and an icon with the respective type it belongs to.
While my attempt has been successful to a certain extent I am getting a string which says "System.Windows.DataTemplate" to where it's supposed to show the respective icon.
I feel like there is something wrong in the way that I am calling the content controller.
you should use DataTemplate resources to set ContentTemplate property, not Content
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type dc:ComboBoxItem}}, Path=DataContext, Converter={StaticResource FormBuilderClient_TypeOfConverter}}" Value="{x:Type models:FileSettingsModel}" >
<Setter Property="ContentTemplate" Value="{StaticResource FileIcon}" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type dc:ComboBoxItem}}, Path=DataContext, Converter={StaticResource FormBuilderClient_TypeOfConverter}}" Value="{x:Type models:ServerSettingsModel}" >
<Setter Property="ContentTemplate" Value="{StaticResource ServerIcon}" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type dc:ComboBoxItem}}, Path=DataContext, Converter={StaticResource FormBuilderClient_TypeOfConverter}}" Value="{x:Type models:HomeSettingsModel}" >
<Setter Property="ContentTemplate" Value="{StaticResource HomeIcon}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
however it should be simpler to use DataTemplates for specific type, rather than x:Key. also define them in ComboBox resources.
<ComboBox.Resources >
<DataTemplate DataType="{x:Type models:FileSettingsModel}">
<Path Stretch="Uniform" Stroke="Black" Fill="Blue" Height="15" Width="25" Data="..."/>
</DataTemplate>
<DataTemplate DataType="{x:Type models:ServerSettingsModel}" >
<Path Stretch="Uniform" Stroke="Black" Fill="OrangeRed" Height="15" Width="25" Data="..."/>
</DataTemplate>
<DataTemplate DataType="{x:Type models:HomeSettingsModel}">
<Canvas>
<Path Canvas.Top="0" Canvas.Left="-2" Height="15" Width="25"
Stretch="Uniform" Stroke="Black" Fill="Green" Data="..."/>
<Path Canvas.Top="7" Canvas.Left="9" Height="16" Width="23"
Stretch="Uniform" Stroke="Black" Fill="Yellow" Data="..."/>
</Canvas>
</DataTemplate>
</ComboBox.Resources>
this way ContentControl should pick correct template without any triggers:
<ContentControl Content="{Binding}"/>

Retrieve GroupName in Listview Grouping in WPF

I've setup a ListView with grouping and I would like to retrieve the GroupName when I right click on the group in MVVM. I've placed a ContextMenu on my group style, and I was trying to use the EventToCommand from System.Windows.Interactivity to get the underlying item.
Here the relevant part:
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="False" Template="{StaticResource CustomizedExpander}" Background="#FFEBEBEB" BorderThickness="0" ContextMenu="{StaticResource GroupContextMenu}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseRightButtonDown">
<i:InvokeCommandAction Command="{Binding Path=OnCategorySelected}" CommandParameter="{Binding Name}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<!--N.B. The "Name" property is part of the CollectionViewSource-->
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="#FF767676" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding ItemCount}" Foreground="#FF454545" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
<TextBlock Text=" item(s)" Foreground="#FF767676" FontStyle="Italic" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
I don't know if it's the right way to do it but it seems the command is not triggered at all.
Any suggestion?
UPDATE:
The whole thing was much simpler than I thought. The interaction part was not required at all. Fixing the binding is enough to get the underlying category when the context menu is shown:
<ListView ItemsSource="{Binding Modifications}" SelectedItem="{Binding SelectedItem}">
<ListView.Resources>
<ContextMenu x:Key="ItemContextMenu">
<MenuItem Header="Execute" Command="{Binding Path=DataContext.OnExecuteScript, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Background="WhiteSmoke" Visibility="{Binding CanExecute, Converter={StaticResource BooleanToVisibilityConverter}}" />
<MenuItem Header="Execute up to this" Command="{Binding Path=DataContext.OnExecuteScriptUpToThis, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Background="WhiteSmoke" Visibility="{Binding CanOnlyBeExecutedSequentially, Converter={StaticResource BooleanToVisibilityConverter}}" />
<MenuItem Header="Drop" Command="{Binding Path=DataContext.OnExecuteDrop, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Visibility="{Binding Drop, Converter={StaticResource BooleanToVisibilityConverter}}" Background="WhiteSmoke" />
<MenuItem Header="Dump" Command="{Binding Path=DataContext.OnExecuteDump, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Visibility="{Binding CanDump, Converter={StaticResource BooleanToVisibilityConverter}}" Background="WhiteSmoke" />
</ContextMenu>
<ContextMenu x:Key="GroupContextMenu">
<MenuItem Header="Dump all" Command="{Binding Path=DataContext.OnExecuteDumpAll, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" CommandParameter="{Binding Name}" Background="WhiteSmoke" />
</ContextMenu>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Type" Width="120" DisplayMemberBinding="{Binding Type}" />
<GridViewColumn Header="Object Name" Width="Auto" DisplayMemberBinding="{Binding DisplayName}" />
<GridViewColumn Header="" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Deploy}" IsHitTestVisible="False" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Object Name" Width="300" DisplayMemberBinding="{Binding ObjectName}" />
</GridView>
</ListView.View>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource MetroListViewItem}" >
<Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Drop}" Value="True">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="ContextMenu" Value="{StaticResource GroupContextMenu}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="False" Template="{StaticResource CustomizedExpander}" Background="#FFEBEBEB" BorderThickness="0">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<!--N.B. The "Name" property is part of the CollectionViewSource-->
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="#FF767676" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding ItemCount}" Foreground="#FF454545" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
<TextBlock Text=" item(s)" Foreground="#FF767676" FontStyle="Italic" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
First of all, i think i've figured out why your Command isnt firing.
Since you are in an Template, the DataContext has Changed. Therefore your CommandBinding should look like this:
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.OnCategorySelected}" CommandParameter="{Binding}"/>
Also, your CommandParameter should not be Name because you'll only get a string in the end instead the whole Object.
If you use my above code you will get the whole CollectionViewGroup instead. In said CollectionViewGroup you'll find all the Items in the group. If you are fine with getting just the Groupname you can proceed with you implementation of course.
I dont really understand why you are using a ContextMenu and what it does (Since you dont posted that code), but if you are interested in how you can display the grouped Items in such a ContextMenu, you can do it like this:
<Expander IsExpanded="False" Background="#FFEBEBEB" BorderThickness="0" >
<Expander.ContextMenu>
<ContextMenu ItemsSource="{Binding Items}"/>
</Expander.ContextMenu>
</Expander>
Since we now know, what we have to deal with (It's still an CollectionViewGroup) we can set the Items of it as ItemsSource of the ContextMenu.
Hope this helps!

Trigger to set Property in ItemTemplate

I have the following definition of a UserControl
<UserControl x:Class="Logger.View.AdvancedToggleButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:converter="clr-namespace:Logger.Converter"
xmlns:commonControls="clr-namespace:Logger.CommonControls"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<UserControl.Resources>
<converter:CountToVisibilityConverter x:Key="CountToVisibilityConverter" />
</UserControl.Resources>
<ItemsControl ItemsSource="{Binding LogElements, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.Resources>
<Style TargetType="{x:Type ToggleButton}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="container"
BorderThickness="1" Margin="1,2,1,1" Padding="2,2,4,2" BorderBrush="Black">
<ContentPresenter x:Name="contentPresenter" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" TargetName="container" Value="{Binding SelectionBrush, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"/>
<Setter Property="BorderThickness" TargetName="container" Value="1,1,1,0"></Setter>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Background" TargetName="container" Value="{Binding SelectionBrush, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"/>
<Setter Property="Background" Value="{Binding SelectionBrush, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ToggleButton Margin="2,1"
IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Command="{Binding DataContext.ToggleCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"
CommandParameter="{Binding}">
<ToggleButton.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" Margin="4,0,5,0" x:Name="tbText"/>
<Border Grid.Column="1" Margin="0,-1,-3,-1" MinWidth="20"
VerticalAlignment="Stretch" HorizontalAlignment="Center"
Background="Yellow" BorderBrush="LightGray" BorderThickness="1,0,0,0"
Visibility="{Binding WarningCount, Converter={StaticResource CountToVisibilityConverter}}"
Cursor="Hand">
<commonControls:ActionLink HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding WarningCount}"
FontFamily="Consolas" Margin="2,0" Command="{Binding DataContext.NavigateToWarningCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"/>
</Border>
<Border Grid.Column="2" Margin="3,-1,-3,-1" MinWidth="20"
VerticalAlignment="Stretch" HorizontalAlignment="Center"
Background="Red" BorderBrush="LightGray" BorderThickness="1,0,0,0"
Visibility="{Binding ErrorCount, Converter={StaticResource CountToVisibilityConverter}}"
Cursor="Hand" >
<commonControls:ActionLink HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding ErrorCount}"
FontFamily="Consolas" Foreground="White" Margin="2,0" />
</Border>
</Grid>
</ToggleButton.Content>
</ToggleButton>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
Everything just works fine. Now I've added another Property to this UserControl to set the BackgroundColor of the Checked ToggleButton-TextBlock (tbText) to a given SolidColorBrush.
My problem now is, that I have no idea how to correctly set the Trigger for this.
I tried to add the Trigger to the ControlTemplate.Triggers when the IsChecked is True. But I don't know how to access tbText from this point. Is this the correct location for the Trigger or do I have to add another Trigger at a lower level?
Your ToggleButton.IsChecked property is data bound to your IsSelected property so by default, you also want to set the TextBlock.Background when this IsSelected property is true. You can do that like this:
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" Margin="4,0,5,0"
x:Name="tbText">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Background" Value="{Binding YourColourBrush,
RelativeSource={RelativeSource AncestorType={x:Type
YourPrefix:YourUserControl}}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
Alternatively, you could just data bind directly to the ToggleButton.IsChecked property like this:
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" Margin="4,0,5,0"
x:Name="tbText">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource
AncestorType={x:Type ToggleButton}}}" Value="True">
<Setter Property="Background" Value="{Binding YourColourBrush,
RelativeSource={RelativeSource AncestorType={x:Type
YourPrefix:YourUserControl}}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>

How to get this binding for item instead of TreeViewItem?

I got the following code :
<HierarchicalDataTemplate x:Key="AssignedRate" ItemsSource="{Binding Children}" DataType="{x:Type local:UnitRateCatElement}">
<ContentControl>
<ContentControl.Template>
<ControlTemplate>
<StackPanel Tag="{Binding DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">
<TextBlock Text="{Binding Category.Description}" />
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Unit Rate"
Command="{Binding Path=PlacementTarget.Tag.AddUnitRateCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</ControlTemplate>
</ContentControl.Template>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsDefined, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}}}" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBlock Text="Hello, it works!" />
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</HierarchicalDataTemplate>
The binding in the line : <DataTrigger Binding="{Binding Path=IsDefined, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}}}" Value="True">
is incorrect (VS says so). How can I get this to work? The class local:UnitRateCatElement does have a IsDefined property. But I cannot get the binding right to point to that object. How can I get this binding right?
Try
Binding="{Binding Path=DataContext.IsDefined, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}}}"
I think you should be doing this -
<DataTrigger Binding="{Binding Path=DataContext.IsDefined,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type TreeViewItem}}}" Value="True">
as your RelativeSource binding points to the TreeViewItem, which doesn't have IsDefined property, it is present in the DataContext of the TreeViewItem.
Update:
For your second problem (trigger not working), this is happening because you are setting the Template explicitly i.e. Local Value which is having higher precedence then Triggers; this should work -
<ControlTemplate x:Key="DefaultTemplate">
<StackPanel Tag="{Binding DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">
<TextBlock Text="{Binding Category.Description}" />
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Unit Rate"
Command="{Binding Path=PlacementTarget.Tag.AddUnitRateCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</ControlTemplate>
<ControlTemplate x:Key="DefinedTemplate">
<TextBlock Text="Hello, it works!" />
</ControlTemplate>
<HierarchicalDataTemplate x:Key="AssignedRate" ItemsSource="{Binding Children}"
DataType="{x:Type local:UnitRateCatElement}">
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="Template" Value={StaticResource DefaultTemplate}>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DataContext.IsDefined, RelativeSource=
{RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}}}"
Value="True">
<Setter Property="Template" Value={StaticResource DefinedTemplate}>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</HierarchicalDataTemplate>

Categories