XAML trigger template to set visibilty based on another element - wpf

I have several StackPanels that change visibility based on ToggleButtons. The code below works if I replace Tag with btn1 on the DataTrigger-lines.
How do I use the value of the Tag property?
<Window x:Class="MyTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestApp">
<Window.Resources>
<Style x:Key="panelStyle" TargetType="{x:Type StackPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Tag, Path=IsChecked}" Value="False">
<Setter Property="StackPanel.Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=Tag, Path=IsChecked}" Value="True">
<Setter Property="StackPanel.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<WrapPanel>
<ToggleButton Content="One" Name="btn1" />
<ToggleButton Content="Two" Name="btn2" />
<StackPanel Style="{StaticResource panelStyle}" Tag="{Binding btn1}">
<Label Content="Data to panel 1" />
</StackPanel>
<StackPanel Style="{StaticResource panelStyle}" Tag="{Binding btn2}">
<Label Content="Data to panel 2" />
</StackPanel>
</WrapPanel>
</Window>
This question is very similar, but I'm missing details on how to pass an element name.
XAML - Generic textbox stylewith triggers / parameters?

Your bindings are incorrect.
In your DataTemplate the bindings should be:
<DataTrigger Binding="{Binding Path=Tag.IsChecked, RelativeSource={RelativeSource Self}}" Value="False">
<Setter Property="StackPanel.Visibility" Value="Collapsed" />
</DataTrigger>
Here the RelativeSource with a mode of Self tells the binding engine that the object to bind against is object to which the style is being applied (e.g. your StackPanel). The PropertyPath of Tag.IsChecked tells the binding engine to look for a property called IsChecked from the object stored in Tag.
Finally the bindings in your StackPanel should be:
<StackPanel Style="{StaticResource panelStyle}" Tag="{Binding ElementName=btn1}">
<Label Content="Data to panel 1" />
</StackPanel>
Here ElementName creates a binding to another element in the logical tree. If you do not explicitly assign to any properties in a Binding as in your original example:
Tag="{Binding btn1}"
The value specified is assigned to the Path property. So this would be the same as:
Tag="{Binding Path=btn1}"
Also note, that using Tag is not considered best practice since it's type is of object and its use is unrestricted, and hence can take on any number of different meanings throughout your project (which often makes it difficult to understand, especially when used in Templates that are located far away from their actual use).
Hope this helps!

Use Converter: set the visibility of StackPanel:
<StackPanel Visivility="{Binding IsChecked, ElementName=btn1, Converter={StaticResource BooleanToVisibilityConverter}}">
...
</StackPanel>

Related

FocusElement does not change focus when I set it using a DataTrigger

I am making a window that shows the user multiple options, with each a radiobutton and a textbox.
When opening this window, the focus should be on the textbox corresponding to the radiobutton that is checked when opening the window (this is all preset, no need to worry about this).
It is important that it gets fixed either in xaml or in the code-behind.
I have used a DataTrigger to change the FocusedElement to the right textbox, however it gets overwritten after it is set.
The relevant code is in the DataTrigger below. The color gets changed correctly, however the FocusElement does not.
I have tried all the options that I have found on stackoverflow and google. The issue does not lie in the DataTrigger or the setting of the FocusedElement. I think it lies in the fact that it gets overridden at the end. I have used Snoop to see the changes in the Keyboard.FocusedElement and it does not show any change.
<DataTemplate x:Uid="DataTemplate_1" >
<RadioButton x:Uid="RadioButton_1" GroupName="Options" IsChecked="{Binding IsSelected}" Margin="4,0,0,0" Name="RadioBtn">
<StackPanel x:Uid="StackPanel_1" Orientation="Horizontal" >
<Label x:Uid="Label_1" Visibility="{Binding IsUserInput, Converter={cs:OppositeVisibilityConverter}}" Content="{Binding Description, Mode=OneWay}" />
<Label x:Uid="Label_2" Visibility="{Binding IsUserInput, Converter={cs:VisibilityConverter}}" Content="Anders, nl: " />
<TextBox x:Uid="MemAnders" Visibility="{Binding IsUserInput, Converter={cs:VisibilityConverter}}" Text="{Binding AlternativeText}"
Name="MemAnders" MinWidth="400" IsTabStop="False"
>
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=MemAnders}"/>
<Setter Property="Background" Value="LightGoldenrodYellow" />
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected}" Value="False">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=RadioBtn}"/>
<Setter Property="Background" Value="LightBlue" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel >
</RadioButton >
</DataTemplate >
The textbox corresponding to the checked radiobutton should be focused. Instead another (parent?) object is focused.
Anyone know a workaround for this?
The work-around turned out to be setting focus to the textbox in the Window_Loaded function.
I used an algorithm provided by Find a WPF element inside DataTemplate in the code-behind to find the correct textbox element. Then I did TextBox.Focus(); and that fixed it.

WPF set default Visibility value when editing

I have Visibility bound to a bool, which works perfectly. However when editing the page the Border is not visible. I have to delete the Visibility Binding, make my changes and redo the Visibility Binding.
I'm pretty sure I saw there is a way to set a "editing default", but I cannot find that link anymore (or remember what it was called). Can someone explain how to set the default to visible so I can see it whilst editing, but not affect it's operation at runtime?
<Border Grid.Column="2" BorderBrush="HotPink" BorderThickness="2" MinHeight="100" MinWidth="100"
Visibility="{Binding ElementName=GenerateWorkOrders, Path=IsChecked, Converter={StaticResource booleanToVisibility}, UpdateSourceTrigger=PropertyChanged}">
<Label Content="Not Visible While Editing"/>
</Border>
The problem is that the default value of IsChecked of GenerateWorkOrders CheckBox is false
If IsChecked have Binding, you can use FallbackValue:
<CheckBox x:Name="GenerateWorkOrders" IsChecked="{Binding SomeProperty, FallbackValue=True}" />
Another way is to avoid the binding, you can use the DesignerProperties.IsInDesignMode Attached Properties that indicate if you in design mode (More inforamtion).
You can use this property in behavior, or in XAML only approach:
<Border Grid.Column="2" BorderBrush="HotPink" BorderThickness="2" MinHeight="100" MinWidth="100">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Visibility" Value="{Binding ElementName=GenerateWorkOrders, Path=IsChecked, Converter={StaticResource booleanToVisibilityConverter}, UpdateSourceTrigger=PropertyChanged}" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(componentModel:DesignerProperties.IsInDesignMode)}" Value="true">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Label Content="Not Visible While Editing"/>
</Border>

How to apply a KeyBinding in a CellTemplate of a DataGridTemplateColumn?

I've got a bit of a curly problem that I cannot find any complete and definitive information on...
My requirement is very simple: I want to apply a KeyBinding to a DataGridTemplateColumn. The purpose of this is to execute a command when the Delete key is hit for that column - a different command is executed if Delete is hit anywhere else on the grid. Both Delete commands exist in the VM of the grid.
While I could have a single keybinding defined at the grid level and then in the command test which column the active cell belongs to, this is a messy solution because:
that doesn't scale well when the grid is used on several different pages
the column in question is defined as a resource in a ResourceDictionary
Besides, I've seen nothing that indicates that this isn't possible... yet it eludes me.
I believe my problem is because focus is not automatically applied to the CellTemplate when that cell becomes the grid's current cell. There is a focus rectangle on the cell, but no focus events get triggered inside the cell (I've checked this). While readonly controls like TextBlocks can receive focus, if they don't then the keybinding cannot be triggered. The commands for the keybinding are correctly bound, so binding is not an issue.
The grid is defined like this:
<DataGrid x:Name="MyGrid"
ItemsSource="{Binding Path=FilteredSortedData}"
>
<DataGrid.Columns>
<StaticResource ResourceKey="StatusColumn" />
<StaticResource ResourceKey="MySpecialDeleteColumn" />
<StaticResource ResourceKey="...etc..." />
</DataGrid.Columns>
</DataGrid>
Then the column:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
x:Class="MyNamespace.GridColumns"
>
<FrameworkElement x:Key="GridColProxy" Name="GridColProxy" DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, Mode=OneWay}" />
<DataGridTemplateColumn x:Key="MySpecialDeleteColumn"
x:Shared="False"
Header="My Special Delete Column"
IsReadOnly="False"
>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel >
<!-- keybinding doesn't work at this level -->
<StackPanel.Style >
<Style TargetType="StackPanel">
<Setter Property="localBehaviours:InputBindingsBehaviour.AttachedKeyBindings">
<Setter.Value>
<localBehaviours:KeyBindingObservable >
<KeyBinding Key="Delete" Command="{Binding Path=DataContext.DeleteItemsCommand, Source={StaticResource GridColProxy}, Mode=OneWay, NotifyOnTargetUpdated=True}" />
</localBehaviours:KeyBindingObservable>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Style>
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding SomeProperty}"
>
<TextBlock.Style>
<Style TargetType="TextBlock" >
<Style.Triggers>
<DataTrigger Binding="{Binding Path=MySpecialFlag, FallbackValue=false}" Value="True">
<Setter Property="localBehaviours:InputBindingsBehaviour.AttachedKeyBindings">
<Setter.Value>
<localBehaviours:KeyBindingObservable >
<KeyBinding Key="Delete" Command="{Binding Path=DataContext.DeleteItemsCommand, Source={StaticResource GridColProxy}, Mode=OneWay, NotifyOnTargetUpdated=True}" />
</localBehaviours:KeyBindingObservable>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<Image Source="{Binding ...}"
Grid.Column="1"
/>
</Grid>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}" >
<Style.Triggers>
<!-- placing the keybinding here to apply to the grid cell doesn't have any effect... -->
<DataTrigger Binding="{Binding Path=MySpecialFlag, FallbackValue=false}" Value="True">
<Setter Property="localBehaviours:InputBindingsBehaviour.AttachedKeyBindings">
<Setter.Value>
<localBehaviours:KeyBindingObservable >
<KeyBinding Key="Delete" Command="{Binding Path=DataContext.DeleteItemsCommand, Source={StaticResource GridColProxy}, Mode=OneWay, NotifyOnTargetUpdated=True}" />
</localBehaviours:KeyBindingObservable>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</ResourceDictionary>
Notes:
ignore the localBehaviours:KeyBindingObservable, it is simply an attached property to assist with intercepting and inspecting keybindings before assigning them to the InputBindings property of the attached control. This works great, there is no issue here.
I have shown every place I have tried the keybindings, none of them have worked (been triggered). The DataTrigger bindings work correctly, so just ignore those.
CellEditingTemplate has been omitted for brevity
So the question I have is:
how can I force the CellTemplate items to receive (keyboard) focus, preferably with XAML? Or is there a way to apply the keybinding at cell level so that it works regardless of the template being shown?
If a XAML solution is not possible then a small amount of event triggered (PreviewKeyDown, etc) code in the code behind of the ResourceDictionary could be used.

How to set the Control Visibilty of the Second Control depending on the Binding Value of the first

I have a Custom Wpf Control i.e. combobox:WpfTwComboBox. I want to set the visibility using a property called DisableProviderSelector.
The usual use of triggers is not helping. The scenario here is when the above control i.e. WindowsFormsHost is made visible or collapsed, I want the opposite to happen to the below custom control.
<StackPanel Grid.Row="3" Grid.Column="2" Height="25" Orientation="Horizontal"
Width="375" HorizontalAlignment="Left">
<WindowsFormsHost Height="25" Width="375">
<WindowsFormsHost.Style>
<Style TargetType="WindowsFormsHost">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DisableProviderSelector}" Value="true">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=DisableProviderSelector}" Value="false">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</WindowsFormsHost.Style>
<commonControls:ProviderSelectorControl RequiredLevel="Save" ModifiedByUser="providerSelectorControl1_ModifiedByUser" x:Name="providerSelectorControl1"/>
</WindowsFormsHost>
<combobox:WpfTwComboBox x:Name="PortalProviderSelector"
SelectedValue="{Binding SelectedPortalProvider}"
ItemsSource="{Binding Path=PortalProvidersCollection}"
DisplayMemberPath="FullName" Width="350" Height="25"
RequiredLevelFlag="Save">
</combobox:WpfTwComboBox>
</StackPanel>
Can anyone please help me on how to set the visibility here?
So DisableProviderSelector is a bool when set to True WindowsFormsHost needs to be Collapsed and ComboBox needs to be Visible. Reverse when bool is false.
So as far as the ComboBox is concerned if bool is True it's Visible and when False it's Collapsed. Thus just bind the ComboBox directly to the Property and use a BooleantoVisibilityConverter
xaml:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
...
<combobox:WpfTwComboBox x:Name="PortalProviderSelector"
Width="350"
Height="25"
DisplayMemberPath="FullName"
ItemsSource="{Binding Path=PortalProvidersCollection}"
RequiredLevelFlag="Save"
Visibility="{Binding DisableProviderSelector,
Converter={StaticResource BooleanToVisibilityConverter}}"
SelectedValue="{Binding SelectedPortalProvider}" />

DataTriggers : How it works

I want to implement a DataTrigger for say, textBox1. When Text inside textBox1 is "ABC" then I want to display "Data matched!" in another TextBox say, textBox2. I have written below xaml code for this but its not working. I am getting below error message.
'Text' member is not valid because it does not have a qualifying type name
XAML code for this is:
<Window x:Class="ControlTemplateDemo.Animation"
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"
Title="Animation" Height="300" Width="607">
<Grid>
<Border Background="White">
<StackPanel Margin="30" HorizontalAlignment="Left" Width="500" Height="209">
<TextBox Name="textBox1">
<TextBox.Triggers>
<DataTrigger Binding="{Binding Path=Text}">
<DataTrigger.Value>
<sys:String>ABC</sys:String>
</DataTrigger.Value>
<Setter TargetName="textBox2" Property="Text" Value="Data matched!"/>
</DataTrigger>
</TextBox.Triggers>
</TextBox>
<TextBox Name="textBox2">
</TextBox>
</StackPanel>
</Border>
</Grid>
</Window>
Is there any problem in binding?
Thanks,
Hemant
You need to give the DataTrigger in a Style for the second TextBox
something like:
<StackPanel>
<TextBox x:Name="inputBox" />
<TextBox Margin="0 25 0 0">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Text"
Value="No Match Found" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=inputBox,
Path=Text}"
Value="ABC">
<Setter Property="Text"
Value="Match Found" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
TextBox.Triggers does not support DataTrigger. I'd guess it's only for EventTriggers as the documentation states
on a side-note, I normally have my bindings in the element that ends up as the target(as much as I can). This way I find it easier to debug at-least personally. If the TextBox has wrong info I instantly check it's binding than every binding in my xaml file to see which element has a wrong binding that ends up updating my TextBox.

Resources