Trigger with target name - wpf

I have a couple text boxes and collection of radio buttons in WPF. I want to use the trigger to set the IsChecked property of oly one radio button which has a name if any text boxes get focus. I check a few examples but I could not find what I looking for.
Remember, we are using MVVM pattern and no code behind.
I tried the following codes and have this compile error:
TargetName property cannot be set on a Style Setter
<UserControl.Resources>
<Style x:Name="myTest" TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="RadioButton.IsChecked" Value="True" TargetName="myRadioButton"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
I read other posts and DataTrigger fix the problem.
<Style x:Name="myTest2" TargetType="RadioButton" >
<Style.Triggers>
<DataTrigger Binding="{Binding IsFocused, ElementName=myTextBox}" Value="True">
<Setter Property="IsChecked" Value="True" ></Setter>
</DataTrigger>
</Style.Triggers>
</Style>

Write styles for each RadioButton providing respective TextBox as triggering element. Following is an example for 3 TextBoxes & 3 RadioButtons.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox x:Name="txtBox0" Grid.Row="0"/>
<TextBox x:Name="txtBox1" Grid.Row="1"/>
<TextBox x:Name="txtBox2" Grid.Row="2"/>
<StackPanel Grid.Row="3" Orientation="Horizontal">
<RadioButton GroupName="grp1" Content="txt1">
<RadioButton.Style>
<Style TargetType="RadioButton">
<Style.Triggers>
<DataTrigger Binding="{Binding IsFocused, ElementName=txtBox0}" Value="True">
<Setter Property="IsChecked" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</RadioButton.Style>
</RadioButton>
<RadioButton GroupName="grp1" Content="txt2">
<RadioButton.Style>
<Style TargetType="RadioButton">
<Style.Triggers>
<DataTrigger Binding="{Binding IsFocused, ElementName=txtBox1}" Value="True">
<Setter Property="IsChecked" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</RadioButton.Style>
</RadioButton>
<RadioButton GroupName="grp1" Content="txt3">
<RadioButton.Style>
<Style TargetType="RadioButton">
<Style.Triggers>
<DataTrigger Binding="{Binding IsFocused, ElementName=txtBox2}" Value="True">
<Setter Property="IsChecked" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</RadioButton.Style>
</RadioButton>
</StackPanel>
</Grid>

From MSDN:
You can set this property to the name of any element within the scope of where the setter collection (the collection that this setter is part of) is applied. This is typically a named element that is within the template that contains this setter.
TargetName is mostly used within control templates and not simply within styles like you are attempting to use it. What you can do is to bind your RadioButton's IsChecked DP to the IsMouseOver DP of the TextBox.

Create a ControlTemplate and add your trigger to ControlTemplate.Triggers
<ControlTemplate.Triggers>
<Trigger Property="HasText" Value="True">
<Setter Property="Visibility" TargetName="LabelText" Value="Hidden" />
</Trigger>
</ControlTemplate.Triggers>

I think you're looking for the GotFocus Event.
In XAML:
<TextBox x:Name="textBox1" GotFocus="tb_GotFocus"/>
<TextBox x:Name="textBox2" GotFocus="tb_GotFocus"/>
<TextBox x:Name="textBox3" GotFocus="tb_GotFocus"/>
<RadioButton x:Name="myRadioButton"/>
Then in your C# your event handler could look something like this
private void tb_GotFocus(object sender, RoutedEventArgs e)
{
myRadioButton.IsChecked = true;
}
If any of the TextBoxes gets focused it will check the RadioButton named myRadioButton.

Related

Set Image border when item is selected in a listbox

I ve been playing with this for a while and I can't make it work. Basically, I have a listbox with an image and a label. What I'd like is to change the color of the border of the image if that item is selected (the listbox is set to multiple selection)
This is I have so far...
<DataTemplate x:Key="ListBox_DataTemplate">
<Grid HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="220"/>
</Grid.RowDefinitions>
<Border x:Name="thumbBorder" BorderThickness="8"
CornerRadius="8">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding isSelected,
ElementName=lb_images}" Value="True">
<Setter Property="BorderBrush
Value="SteelBlue"/>
</DataTrigger>
<DataTrigger Binding="{Binding isSelected,
ElementName=lb_images}" Value="False">
<Setter Property="BorderBrush"
Value="Yellow"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Image Width="170" Height="190" Source="{Binding Thumbnail}"
HorizontalAlignment="Center"
VerticalAlignment="Top"
x:Name="thumb"/>
</Border>
However, nothing happens when I select the item. IIm really stuck, so any ideas would be welcome.
Thanks
Your DataTrigger should use a RelativeSource Binding to the ListBoxItem container element, and use the correct property path IsSelected:
<DataTemplate x:Key="ListBox_DataTemplate">
<Border BorderThickness="8">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="Yellow"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Value="True">
<Setter Property="BorderBrush" Value="SteelBlue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Image Source="{Binding Thumbnail}"/>
</Border>
</DataTemplate>

Prevent tabbing to TextBlock when Visibility is Collapsed

I'am trying to create a trigger which removes a TextBlock from the tab navigation when the Visibility is Collapsed.
This is the style:
<Style x:Uid="Style_1" TargetType="TextBlock">
<Setter x:Uid="Setter_1" Property="TextOptions.TextFormattingMode" Value="{StaticResource TextFormattingMode}"/>
<Setter x:Uid="Setter_32" Property="TextOptions.TextRenderingMode" Value="{StaticResource TextRenderingMode}"/>
<Setter x:Uid="Setter_2" Property="TextBlock.FontFamily" Value="{StaticResource FontFamily}"/>
<Setter x:Uid="Setter_3" Property="SnapsToDevicePixels" Value="True"/>
<Setter x:Uid="Setter_4" Property="VerticalAlignment" Value="Center"/>
<Setter x:Uid="Setter_74" Property="UseLayoutRounding" Value="True"/>
<Setter x:Uid="Setter_5" Property="Foreground" Value="{StaticResource LabelForeground}"/>
<Setter x:Uid="Setter_7" Property="FontSize" Value="{StaticResource FontSize}"/>
<Style.Triggers>
<DataTrigger x:Uid="DataTrigger_2" Binding="{Binding Source={x:Static cs:ZoomLevel.Instance}, Path=ActualZoomLevelIsDefault}" Value="False">
<Setter x:Uid="Setter_33" Property="TextOptions.TextFormattingMode" Value="Ideal"/>
</DataTrigger>
<DataTrigger x:Uid="DataTrigger_100" Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Visibility}" Value="Collapsed">
<!--<Setter Property="IsEnabled" Value="False"/>-->
<!--<Setter Property="Control.IsTabStop" Value="False"/>-->
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
But the TextBlock still gains the focus if I tab to it. IsKeyboardFocusWithin is true when I focus to the TextBlock. I can set IsEnabled to false in my trigger to make it work, but I wonder why the attached property is not working. So my question is: why is the KeyboardNavigation property not working?
Example:
<TextBlock x:Uid="TextBlock_2" Grid.Column="1" Grid.Row="1" Margin="3,3,0,0" HorizontalAlignment="Right" Visibility="Collapsed">
<Hyperlink x:Uid="Hyperlink_2" Command="{Binding SelectRoutingMethods}">
<Run x:Uid="Run_2" Text="{Binding ContactPreferences}"/>
</Hyperlink>
</TextBlock>
The TextBlock is tabable but still visible in my example.
It's not your TextBlock that gets the focus. It's Hyperlink. I don't know why, it's not even in Visual Tree but it does. It seems like a bug. If you set its property Focusable to false it wont take focus anymore.
Here is complete simplified example:
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="Focusable" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Visibility}" Value="Collapsed">
<Setter Property="Focusable" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox>Temp</TextBox>
<TextBlock Visibility="Visible" Grid.Row="1">
<Hyperlink Focusable="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TextBlock}, Path=Focusable}" Command="{Binding AddOptionalAddressCommand}">test</Hyperlink>
</TextBlock>
<TextBox Grid.Row="2" >Temp</TextBox>
</Grid>
Credits to #Shadowed for finding the issue with hyperlink.
Here is my alternativ solution: set the KeyboardNavigation.IsTabStop on Hyperlink based on the next UIElement visibility up the tree (which will be the TextBlock in this specific case.
<Hyperlink x:Uid="Hyperlink_2" KeyboardNavigation.IsTabStop="{Binding IsVisible,RelativeSource={RelativeSource AncestorType={x:Type UIElement}}}">
As answered by #Shadowed, it was Hyperlink that is getting the Focus. Not sure why this is happening as the Content should not get Focusif the parent is in Collapsed state.
Anyway, I can give you workaround for this.
<StackPanel>
<StackPanel.Resources>
<local:VisibilitytoFocusConverter x:Key="VisibilitytoFocusConv" />
</StackPanel.Resources>
<TextBox >Temp</TextBox>
<TextBlock Visibility="Collapsed" Height="20" KeyboardNavigation.TabNavigation ="{Binding Visibility, RelativeSource={RelativeSource Self}, Converter={StaticResource VisibilitytoFocusConv}}">
<Hyperlink Command="{Binding AddOptionalAddressCommand}" />
</TextBlock>
<TextBox >Temp</TextBox>
</StackPanel>
Converter:
public class VisibilitytoFocusConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo language)
{
return ((Visibility)value) == Visibility.Visible ? "Local" : "None";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo language)
{
return new NotFiniteNumberException();
}
}
Hope that helps.

Editable WPF ListBox contains textbox

I want to have dynamic editable listbox in wpf application. I am using textbox inside listbox, and I am binding Observable collection to that listbox. Now, I want to make textbox editable on mouse click. So, user can make change to textbox and save new textbox text.
<ListBox Name="ListTwo" ItemsSource="{Binding CollectionUrl, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Name="TextBoxList" Text="{Binding Path=urlString}" IsEnabled="False" >
</TextBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You should use IsReadOnly property. In the trigger you should check IsFocused property.
In the following example, I changed foreground color to indicate which TextBox is in the edit mode.
Example:
<ListBox Name="ListTwo" ItemsSource="{Binding CollectionUrl, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Name="TextBoxList" Text="{Binding Path=urlString}" MinWidth="100">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Foreground" Value="Green"/>
<Setter Property="IsReadOnly" Value="False" />
</Trigger>
<Trigger Property="IsFocused" Value="False">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="IsReadOnly" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If you want to allow users save changes after edit value in the TextBox, you can add button and show in the actual editing row:
<ListBox Name="ListTwo" ItemsSource="{Binding CollectionUrl, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<TextBox Name="TextBoxList" Text="{Binding Path=urlString}" MinWidth="100">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Foreground" Value="Green"/>
<Setter Property="IsReadOnly" Value="False" />
</Trigger>
<Trigger Property="IsFocused" Value="False">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="IsReadOnly" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<Button Content="Save" Grid.Column="1" Command="{Binding SaveChanges}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=TextBoxList, Path=IsFocused}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My approach to accessing the text box (which was proving a nightmare) was to use your save button approach and in the Button's Button_Click function I used sender to retrieve the Button's parent, cast to (in your case) Grid. Then you can use that to access the Grid's Children with .Children[0] being your TextBox.
Bit of a Kluge because your code has to 'know' the type of the parent and the index of the child TextBox but these will not change. If necessary the purists can iterate through the Children to identify the required child explicitly.
Hope this helps someone.

DataTrigger on RadioButton IsChecked

I have a scenario where I need to hide some content based on whether a radio button is checked or unchecked. For some reason I can't get this to work the way I expect it to. The behavior is the opposite of what I expect. If I adjust my xaml to accomodate the actual behavior that I'm seeing it everything gets hidden.
Essentially what I have is two radio buttons labeled Fixed and Cycle. When Fixed is checked I want the textbox associated with Fixed to have a visible foreground and the textbox associated with Cycle to have a transparent foreground and vice-versa. What I'm seeing is the exact opposite.
Here's my trigger:
<Grid.Resources>
<Style TargetType="TextBox" x:Key="FixedModeStyle">
<Setter Property="Foreground" Value="Transparent" />
<Setter Property="Width" Value="40" />
<Setter Property="Height" Value="20" />
<Setter Property="Margin" Value="10" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked,
ElementName=rbtFixedMode}" Value="True" >
<Setter Property="Foreground"
Value="{DynamicResource My.Fonts.Global.LightForeground}" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="TextBox" x:Key="CycleModeStyle">
<Setter Property="Foreground" Value="Transparent" />
<Setter Property="Width" Value="40" />
<Setter Property="Height" Value="20" />
<Setter Property="Margin" Value="10" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked,
ElementName=rbtCycleMode}" Value="True" >
<Setter Property="Foreground"
Value="{DynamicResource My.Fonts.Global.LightForeground}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
Here's my radio buttons and associated textboxes:
<RadioButton x:Name="rbtFixedMode" Content="Fixed"
GroupName="AveragingMode"
Foreground="{DynamicResource My.Fonts.Global.LightForeground}"
IsChecked="{Binding AveragingWindowMode,
Converter={StaticResource EnumToBooleanConverter},
ConverterParameter={x:Static Processors:AveragingMode.Fixed}}" />
<DockPanel Grid.Row="1" IsEnabled="{Binding IsChecked, ElementName=rbtFixedMode}">
<TextBox x:Name="txtFixedIntervalLength"
Style="{StaticResource FixedModeStyle}" DockPanel.Dock="Left"
Text="{Binding AveragingWindowFixedLength}" />
</DockPanel>
<RadioButton x:Name="rbtCycleMode" Content="Cycle"
GroupName="AveragingMode" Grid.Row="2"
Foreground="{DynamicResource My.Fonts.Global.LightForeground}"
IsChecked="{Binding AveragingWindowMode,
Converter={StaticResource EnumToBooleanConverter},
ConverterParameter={x:Static Processors:AveragingMode.Cycles}}" />
<DockPanel Grid.Row="3" IsEnabled="{Binding IsChecked, ElementName=rbtCycleMode}">
<TextBox x:Name="txtCycleIntervalLength"
Style="{StaticResource CycleModeStyle}" DockPanel.Dock="Left"
Text="{Binding AveragingWindowCycleLength}"/>
<TextBlock x:Name="txbCycles" Text="Cycles" Margin="4,10"
Foreground="{DynamicResource My.Fonts.Global.LightForeground}" />
</DockPanel>
Any ideas?
Just making the text transparent is a bad idea, the user will still be able to edit it while it is transparent. Besides that the code is obfuscated and redundant. Do not create two styles for a TextBox, both containing the same setters; create a base-style and use BasedOn for sub-styles.
Not quite sure where your code goes wrong, i would suggest you clean it up, maybe there is some logical error, also here is a working example:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Page.Resources>
</Page.Resources>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<RadioButton Name="rbCycle" GroupName="g1" Content="Cycle"/>
<RadioButton Name="rbFixed" GroupName="g1" Content="Fixed" Grid.Column="1"/>
<TextBox Grid.Row="1" Text="cycle box">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=rbCycle}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBox Grid.Row="1" Grid.Column="1" Text="fixed box">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=rbFixed}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
</ScrollViewer>
</Page>
If you're using binding to set the values of your radio buttons, don't use groups. If you're using groups, don't use binding. The two don't play well together.
That may not be the cause of what you're seeing. But I bet it is. And it's certainly interfering with your ability to diagnose the problem, because after you start clicking on buttons their binding doesn't work anymore.

WPF switch between regions with toggle button

I have a view, that when loaded contains a toolbar at the top, and two regions underneath.
I am currently using a grid, so:
row 0 contains the toolbar
row 1 contains region1 and
row 2 contains region2.
The toolbar has a toggle button, which when clicked, completely hides region1, and shows region 2, and visa-versa.
What is the best way to achieve this?
I have tried binding the 2 rows heights to get altered on the toggle, but the space is not filled correctly. VerticalAlignment="Stretch" and HorizontalAlignment="Stretch" are both used.
I have also tried enabling and disabling the itemcontrol hosting the region, but that doesn't seem to work at all.
Any pointers as to what I have done are much appreciated! :)
Not sure if I fully understand what you want. Something like this?
<DockPanel>
<ToggleButton Name="viewToggle" DockPanel.Dock="Top">Toggle Region</ToggleButton>
<Grid>
<ContentControl>
<TextBlock>I'm region 1</TextBlock>
<ContentControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=viewToggle, Path=IsChecked}" Value="False">
<Setter Property="ContentControl.Visibility" Value="Visible"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=viewToggle, Path=IsChecked}" Value="True">
<Setter Property="ContentControl.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<ContentControl>
<TextBlock>I'm region 2</TextBlock>
<ContentControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=viewToggle, Path=IsChecked}" Value="True">
<Setter Property="ContentControl.Visibility" Value="Visible"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=viewToggle, Path=IsChecked}" Value="False">
<Setter Property="ContentControl.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
</DockPanel>
I used for this one row that contains region1 and region2 too. region2's Visibility set to Collapsed by default, and when an event occured (a checkbox for me, databound to a bool value) i flipped the Visibility of the regions. My 'regions' contained two different layout, and i don't experienced any issue.
A quick sample code without code-behind:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.Resources>
<Style x:Key="showOnToggled" TargetType="GroupBox" BasedOn="{StaticResource {x:Type GroupBox}}" >
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=toggleButton, Path=IsChecked}" Value="True" >
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="hideOnToggled" TargetType="GroupBox" BasedOn="{StaticResource {x:Type GroupBox}}" >
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=toggleButton, Path=IsChecked}" Value="True" >
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<ToggleButton Content="Switch Region"
Grid.Row="0"
x:Name="toggleButton" />
<GroupBox Header="Region1"
Grid.Row="1"
Style="{StaticResource showOnToggled}" >
<!-- Region1's content -->
</GroupBox>
<GroupBox Header="Region2"
Grid.Row="1"
Style="{StaticResource hideOnToggled}" >
<!-- Region2's content -->
</GroupBox>
</Grid>

Resources