Set property in Style trigger based on child property change (wpf) [duplicate] - wpf

This question already has answers here:
DataTrigger not firing
(1 answer)
WPF set border background in trigger
(1 answer)
WPF Trigger won't set property if set in Element
(3 answers)
Button IsEnabled Trigger does not work
(1 answer)
Closed 4 months ago.
I would like to achieve something in wpf which not seems to be easy to me.
I want to change the outer border's CornerRadius depending on the inner element's state. If the ToggleButton's IsChecked property is true then change the CornerRadius of the outer element something different than the default one.
I tried - among a lot of other things - this:
<Border x:Name="rootElement"
CornerRadius="5">
<ToggleButton
x:Name="showAdditionalOptions">
...
</ToggleButton>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=showAdditionalOptions}" Value="True">
<Setter Property="CornerRadius" Value="0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
I want to change the CornerRadius of rootElements if IsChecked is true for showAdditionalOptions.
I tried setting the trigger in the ToggleButtons Triggers parts (both Style.Triggers AND Template.Triggers) but from there I can't reach outer elements (TargetName does not work).

I found out what is the problem, but I don't delete the question because trickier than it seems.
The trigger works well. When I changed the Visibility of the same item (rootElement) to collapsed then it really collapsed.
It turned out that the following line overwrites every trigger action:
CornerRadius="5"
Since it is set to 5 (5,5,5,5) nothing happened after the trigger finished running.
Instead of setting the CornerRadius on the very object, the following code shall be used:
<Border x:Name="rootElement">
<ToggleButton
x:Name="showAdditionalOptions">
...
</ToggleButton>
<Border.Style>
<Style TargetType="{x:Type Border}">
<!-- Set default CornerRadius here! -->
<Setter Property="CornerRadius" Value="5"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=showAdditionalOptions}" Value="True">
<Setter Property="CornerRadius" Value="0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>

Related

ElementName binding doesn't work for IsMouseOver in ControlTemplate?

I have a control template and I want to trigger some actions only if mouse is over a certain part of it. Here is the core of my template (simplified for demonstration):
<ControlTemplate TargetType="{x:Type graphicElements:MyTabItem}">
<Grid x:Name="templateRoot">
<Grid x:Name="templateChild" />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, ElementName=templateChild}" Value="True">
<Setter Property="Background" TargetName="templateRoot" Value="Red" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
When I put ElementName as templateRoot it works and turns red. When I put it as templateChild it doesn't work... Why not?
In the simplified version of your code the binding to the templateRoot grid also won't work. The problem is, that WPF needs to perform Hit Tests on the elements to raise certain events or to update the IsMouseOver property. Since you don't have a background brush set for the grids, they will never receive mouse inputs, hence your trigger will never execute. Try this:
<Grid x:Name="templateRoot">
<Grid x:Name="templateChild" Background="Transparent"/>
</Grid>

Trouble binding to a property of templatedparent to set the value inside of a style setter in wpf

My problem:
I haven't been able to figure out how I inside the 'Setter.Value' field of a setter for a property A in a style targeting a particular control can do a binding to a property B of that particualr control. More specifically I want to use the Foreground brush value on a graphical element inside the visual tree of the Content property of a Button. this will ensure that the graphical element always has the foreground color set for this button control.
What I try to achive:
I'm working on a WPF-application where I have three button controls:
DefaultButton
SpecialButton
ExtendedSpecialButton
The DefaultButton is where I define the style of buttons in the application through a style with a ControlTemplate.
The SpecialButton introduces a new property not supposed to be used for general buttons. This property will be represented by one visual state that I define through a style setter. Else from that it shall be identical in apperance to the DefaultButton.
I define the style of this SpecialButton by basing it on the style of the DefaultButton. In this style there is no ControlTemplate only a MultiTrigger-response on the basis of a couple of property conditions setting av a couple of visual properties:
<Style x:Key="SpecialButtonStyle" TargetType="{x:Type MyControls:SpecialButton}" BasedOn="{StaticResource DefaultButtonStyle}">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsActive" Value="false"/>
<Condition Property="IsMouseOver" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" Value="{DynamicResource ButtonDisabledBorder}" />
<Setter Property="Background" Value="{DynamicResource ButtonDisabledBg}" />
</MultiTrigger>
</Style.Triggers>
</Style>
All this worked great.
The next step is also no problem:
I wanted to base the ExtendedSpecialButton on the SpecialButton and set a default shape content inside the button.
<Style x:Key="ExtendedSpecialButtonStyle" TargetType="{x:Type MyControls:ExtendedSpecialButton}" BasedOn="{StaticResource SpecialButtonStyle}">
<Setter Property="Content">
<Setter.Value>
<Rectangle Fill="Black" Height="5" Width="15"></Rectangle>
</Setter.Value>
</Setter>
</Style>
The original style of DefaultButton is still present - the added visual state responding to the IsActiveProperty of the SpecialButton is still with us - and the ExtendedSpecialButton also inherited the visual behaviour created by the MultiTrigger of the SpecialButton.
I also successfully displayed a graphical element that this ExtendedSpecialButton should have.
However I wanted the fill of this graphical element to use the Foreground color. This foreground color is originally styled in the DefaultButton and works just fine for the two first buttons.
The code below is how I currently thought such a binding should be done. But this does not work:
<Style x:Key="ExtendedSpecialButtonStyle" TargetType="{x:Type MyControls:ExtendedSpecialButton}" BasedOn="{StaticResource SpecialButtonStyle}">
<Setter Property="Content">
<Setter.Value>
<Rectangle Fill="{Binding RelativeSource={RelativeSource TemplatedParent}}" Height="6" Width="20"></Rectangle>
</Setter.Value>
</Setter>
</Style>
Does anyone know what I could do to set up the binding so that it does what I intended it to do?

how to hide a button that is bound to a command that cannot execute?

Instead of disabling the button which happens automatically, i would like to hide (or rather collapse the visibility of) the button.
You could use a Style and Triggers, assuming that the command is in charge of setting the Button enabled/disabled:
<Button x:Name="btnMoveUp"
Command="{x:Static local:Window1.MoveItemUp}">
<Button.Style>
<Style TargetType="{x:Type Button}" >
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Note that you can define this Style at a higher scope and share it - I just put it right with the Button for a more compressed example.
The same behavior without style and trigger, if the Visibility property is not bound yet.
Command={Binding MyCommand}
Visibility="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Converter={StaticResource BTVC}}"
Where BTVC is a BooleanToVisibilityConverter (that is a must have).
Use the BooleanToVisibilityConverter and bind to a bool as described here.

changing background color of container when textbox is in focus

I have a simple user control with a TextBox. I want to change the color of user control when the TextBox gets the focus. This is what I have:
<UserControl x:Class="OutLookContactList.ContactSearchControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root" MinHeight="30" Loaded="UserControl_Loaded">
<UserControl.Resources>
<Style x:Key="searchTextBoxStyle" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter TargetName="root" Property="Background" Value="{StaticResource OnMouseOverColor}" />
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
But I get the errot "TargetName property cannot be set on a style Setter". How can I Set the back ground color of user control when text box gets the focus?
Thanks a bunch
Will it work to wrap the contents of your UserControl inside a Border object? If so, you can simply style the Border like so:
<UserControl x:Class="Sample2.ContactSearchControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="75" Width="300">
<Border>
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsFocused, ElementName=txtSearch}" Value="true">
<Setter Property="Background" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel>
<TextBox x:Name="txtSearch" Text="Search" />
<TextBox Text="Other" />
</StackPanel>
</Border>
</UserControl>
Update: (Answering Sheraz' Questions)
I'm not sure why ElementName doesn't work for accessing children within a UserControl. It might have something to do with the way the visual tree is constructed.
As for Trigger vs DataTrigger: Trigger is for dependency properties and DataTrigger is for databound properties (data or other controls). Since you are trying to style the Border, it makes more sense to place the DataTrigger there and have it watch the TextBox than to have the TextBox change the appearance of the Border.
As I understand it, the TargetName property of Setter is only applicable within a DataTemplate or ControlTemplate. (Info from Dr. WPF in this forum post)
If you were changing the background of the text box you need to remove the TargetName property:
<Style x:Key="searchTextBoxStyle" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="Background" Value="{StaticResource OnMouseOverColor}" />
</Trigger>
</Style.Triggers>
</Style>
and change the TextBox that wants this style to be:
<TextBox Style="{StaticResource searchTextBoxStyle}" .... />
However, as you want to change the value of the parent user control this won't give you want you want.
You could certainly do it in the code behind by adding a GotFocus event handler and putting the code to change the background colour in there.
Here's some XAML that works in Kaxaml:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Style>
<Style TargetType="Page">
<Setter Property="Background" Value="#CCCCD0" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=txtSearch, Path=IsFocused}"
Value="true">
<Setter Property="Background" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
</Page.Style>
<TextBox x:Name="txtSearch" Width="100"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Page>
You would change the Page object with your UserControl. I find it much easier to test these sorts of things out in a rapid prototyping tool such as Kaxaml before coding up the UserControl in VS.
Note that you have to set the default colour (in this case #CCCCD0) via a property setter and not via an attribute on the Page itself. This is because the attribute would override the value set by the trigger (because it's a style trigger), so even though the trigger would fire, it would always be trumpted by the local attribute specification, meaning that it wouldn't change. I only point this out because it's a fairly common gotcha.

WPF ComboBox - showing something different when no items are bound

I have a ComboBox, and i want to change its look when the ItemsSource property is null. When it is in that state, i want to show a TextPanel with the text "Retrieving data" in it, and give it a look kind of similar to the watermarked textbox.
I figure to do this i need a ControlTemplate, and a trigger. I have the ControlTemplate here:
<ControlTemplate x:Key="LoadingComboTemplate" TargetType="{x:Type ComboBox}">
<Grid>
<TextBlock x:Name="textBlock" Opacity="0.345" Text="Retrieving data..." Visibility="Hidden" />
</Grid>
<!--
<ControlTemplate.Triggers>
<Trigger Property="ComboBox.ItemsSource" Value="0">
<Setter Property="Visibility" Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
-->
</ControlTemplate>
but my issue is how do i set up the trigger to show this when the ItemsSource property is null? I have tried a couple of different ways, and each way has given me the error message "Value 'ItemsSource' cannot be assigned to property 'Property'. Invalid PropertyDescriptor value.". My ComboBox xaml is this (including the attempted trigger):
<ComboBox Margin="112,35,80,0"
Name="MyComboBox"
Height="22.723"
VerticalAlignment="Top"
DisplayMemberPath="FriendlyName"
SelectedValuePath="Path"
TabIndex="160"
>
<Trigger>
<Condition Property="ItemsSource" Value="0" />
<Setter Property="Template" Value="{StaticResource LoadingComboTemplate}" />
</Trigger>
</ComboBox>
now should the trigger go on the ComboBox, or on the ControlTemplate? How do i access the ComboBox's ItemsSource property? Should i even be using a trigger?
Thanks!
Try putting {x:Null} for the value of the condition instead of 0.
Also I got it working by moving the Trigger to a style and modifing it slightly, see below.
<Style TargetType="ComboBox" x:Key="LoadingComboStyle">
<Style.Triggers>
<Trigger Property="ItemsSource" Value="{x:Null}">
<Setter Property="Template" Value="{StaticResource LoadingComboTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
<ComboBox Style="{StaticResource LoadingComboStyle}" .... >
The reason it only works in a style, is that only EventTriggers are allowed in the triggers collection directly on the Framework Element. For property triggers (like above) you need to use a style (I learn something every day).
See FrameworkElement.Triggers
Note that the collection of triggers established on an element only supports EventTrigger, not property triggers (Trigger). If you require property triggers, you must place these within a style or template and then assign that style or template to the element either directly through the Style property, or indirectly through an implicit style reference.

Resources