WPF Style Trigger Not Working? - wpf

Good Day,
I have a TextBlock element with a black background and text with a black foreground color. I do not want my users to see the text until a task is completed. Then the text will turn into a greenish color.
My style trigger in xaml looks like:
<Style x:Key="DataImportCompletedStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="#FF000000" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsImportCompleted}" Value="True">
<Setter Property="Foreground" Value="#FF99F999" />
</DataTrigger>
</Style.Triggers>
</Style>
My TextBlock looks like:
<TextBlock x:Name="ImportStatusMesage"
Grid.Row="3"
Margin="5,0,5,10"
Background="Black"
FontSize="18"
Foreground="#FF000000"
Style="{StaticResource DataImportCompletedStyle}"
Text="Data Import Completed" />
And my code behind for the IsImportCompleted boolean property is:
private bool isImportCompleted;
public bool IsImportCompleted
{
get { return isImportCompleted; }
set
{
isImportCompleted = value;
System.Diagnostics.Debug.WriteLine("Import Process Completed...OnPropertyChanged");
OnPropertyChanged("IsImportCompleted");
}
}
which does implement INotifyPropertyChanged. The task works fine and updates the IsImportCompleted property as I am seeing my message in the Output window, but the text doesn't change color.
I thought by using INotifyProperty that the UI would update itself.
I'm using Snoop and verified that the IsImportCompleted is set to true. But still no text color change.
Any advice,
TIA,
coson

I am quoting from the comment of the Asker. which according to him, this solved his problem
Never mind, I figured it out. I'm setting the Foreground property in my XAML which will always override what I set in the trigger based on the precedence rules. Once I took out the Foreground property definition in my TextBlock tag, everything worked!

Related

WPF trigger can't change a property in mvvm

I wanted to make it happened to change property in ViewModel using Trigger tag in xaml.
The code I made was like this.
<Grid x:Name="LogoGrid">
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Height" Value="{Binding LogoHeight, Mode=TwoWay}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=ActualHeight, Converter={StaticResource ImageHeight}}" Value="False">
<Setter Property="Height" Value="150"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Image Source="/Resources/Logo/Logo.png" Style="{StaticResource GameWindowLogoStyle}"/>
</Grid>
I bound data to LogoHeight property in ViewModel and I wanted to set this property to 150 when parents grid is big enough to show Logo Image.
It works fine in the UI. but it didn't change LogoHeight property.
It is the same things keep happening when I use animation with Storyboard tag.
Is it normal things that propery can't be changed by Trigger tag or animation in the Storyboard tag.
Please tell me the way if there is a way to change property in Trigger tag or Storyboard tag.
Thank you.
A Setter in a Style cannot set the property of a view model. It can only set a property of the element to which the Style is being applied, i.e. the Grid in this case.
You may be able to work around this by using a Storyboard that animates the source property:
Setting a ViewModel boolean property inside a storyboard

Binding to WPF parent Grid panel's property fails, yet yields no binding warning messages

I'm trying to something I thought was fairly simple. I want to make the border within my grid appear whenever keyboard focus is inside the grid.
But the binding is not working and I cannot spot what's wrong with it. I'm hoping someone can spot where I went wrong.
I created a simple test app to illustrate the problem. Here's the content of the window
<StackPanel>
<!-- This text box is just so that something else can have focus at times -->
<TextBox Width="200" Height="20"/>
<!--
This grid should show its border whenever keyboard focus is within
I even have an event handler dumping out the new value of when it changes.
-->
<Grid x:Name="DetailGrid"
Width="400" Height="400" Focusable="True"
IsKeyboardFocusWithinChanged="DetailGrid_OnIsKeyboardFocusWithinChanged">
<Border BorderBrush="White" BorderThickness="0" >
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsKeyboardFocusWithin, ElementName=DetailGrid}" Value="True">
<Setter Property="BorderThickness" Value="3"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<!-- This text box is just so that the grid can get keyboard focus within -->
<TextBox Width="200" Height="20" VerticalAlignment="Center"/>
</Border>
</Grid>
</StackPanel>
And the code-behind event handler which confirms that the property has changed to what I expect
private void DetailGrid_OnIsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("DetailGrid keyboard focus within = " + (bool) e.NewValue) ;
}
The "DetailGrid" contains a zero-thickness border around a text box.
The trigger on the border binds to the IsKeyboardFocusWithin of the
parent DetailGrid.
If the user clicks the text box, the IsKeyboardFocusWithin property of the grid gets set to true (I have verified this in code-behind).
The trigger says that when that IsKeyboardFocusWithin property of DetailGrid gets set to true, that border thickness should get increased to 3.
But that's not happening.
I tried changing the Debug output window settings to be much more verbose about the WPF binding but I don't see any problems there.
I tried changing the binding to use FindAncestor but that made no difference.
My gut tells me this has to be something incredibly simple and dumb but I can't see it. What am I missing here?
You should change your Border in XAML to this instead:
<Border BorderBrush="Black" >
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="0"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsKeyboardFocusWithin, ElementName=DetailGrid}" Value="True">
<Setter Property="BorderThickness" Value="3"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<!-- This text box is just so that the grid can get keyboard focus within -->
<TextBox Width="200" Height="20" VerticalAlignment="Center"/>
</Border>
I removed the BorderThickness="0" markup from the Border definition because it has a higher precedence than a value set through a Style Setter so your Setter couldn't override it. I moved this default value to a default Style Setter as you can see.
I also changed the BorderBrush to Black so that you can see it become visible.

Style Triggers not working when object is bound to other object

I am having a problem with the style of a few items that are bound to a set of radio buttons. Basically, I have the following code for my styles:
<Window.Resources>
<Style x:Key="boxStyle" TargetType="TextBox">
<Setter Property="Background" Value="Black"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="Blue"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
Then I have two radio buttons as shown here:
<RadioButton Name="optionA" IsChecked="True">Option A</RadioButton>
<RadioButton Name="optionB'>Option B</RadioButton>
And two text boxes as shown here:
<TextBox Style="{StaticResource boxStyle}" IsEnabled="{Binding ElementName=optionA, Path=IsChecked}"/>
<TextBox Style="{StaticResource boxStyle}" IsEnabled="{Binding ElementName=optionB, Path=IsChecked}"/>
The binding works correctly (when Option A it checked, one box is enabled and the other is not). However, when either of the boxes becomes disabled, it does not follow the style defined above. The background goes to white no matter what I change the style color to.
Anyone have any ideas? Thanks.
The color used when disabled is hard-coded in the template as far as i know, you cannot easily change it unless it references a system-color in which case you can override.
The default Aero theme uses a ListBoxChrome control, not sure if that can be made to change its background accordingly, it has no template so it might be hard to modify it. You could of course throw it out and use whatever you want (which you can modify).

How Biniding Mode(TwoWay) works?

I was trying out some dummy application just to test binding modes. So, just curious to know how did the binding modes work. I have this xaml code-
<Button x:Name="btn"
Height="20"
Width="200"
VerticalAlignment="Top">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="IsEnabled"
Value="{Binding CanEnable, Mode=TwoWay}" />
<Style.Triggers>
<DataTrigger Binding="{Binding TextChanged}" Value="true">
<Setter Property="IsEnabled"
Value="true" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Here by button IsEanbled is binded to my viemodel property "CanEanble" whose default value is false. Now in my trigger i was listening to "TextChanged" property and setting button IsEnabled to true. Button gets enabled as it should be but the property "CanEnable" did not set to true even the biding mode is set to TwoWay..
Why this is happening??
By setting the value in the trigger you basically remove the binding you previously set in the style setter. Take a closer look at the style. You will notice that you basically you set the property IsEnabled twice. First in the style setter, second in the trigger. It is logical that the second value overrides the previous value.
The desired effect can be achieved from code if you set the value of the dependency property using SetCurrentValue method:
SetCurrentValue(Button.IsEnabledProperty, true);
This way the bindings set on this property will not be removed and it will work as expected.

WPF Button Visibility

I am using MVVM architecture to develop a WPF application...
So far everything has been going fine.
I have run into an issue with binding visibility. I want to minimize writing code in the code behind if i can but if it's REQUIRED then I don't mind doing it.
I have a ViewModel. THis model exposes a boolean and 2 commands. A connect command, a disconnect command, and a DeviceCurrentlyConnected Bool.
Basically I have decided to make 2 buttons but have the button visibility based on the boolean.
So i have had a hard time with this. I tried styles with triggers for a long time.
<Button Visibility="Hidden" Content="{x:Static UIStrings:ClientStrings.DeviceBar_DisconnectCommandName}" VerticalAlignment="Center" HorizontalAlignment="Center" Height="{Binding ElementName=this.Content, Path=DesiredHeight}" Margin="10" Name="Disconnect" Command="{Binding DisconnectCurrentDeviceCommand}">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding DataCotext.DeviceConnected, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
I can not get styles to work at all.
Basically the functionality that I want is:
DeviceConnected = false:
Display a button with content Connect and command bound to the ConnectCommand.
DeviceConnected = true:
Display a button with content disconnect and command bound to the DisconnectCommand.
for a button to be displayed and bound to the connect device when no device is currently connect and for a button to be displayed when a device is connected that is bound to the disconnect command and to say the word disconnect.
Write up a bool to visibility converter and then use the converter on your buttons. Five minute recipe for a decent BoolToVisibilityConverter is a good post to read up on creating/using a visibility converter.
What I've done in the past is use a bool to visibility converter and passed in the button's IsEnabled property as the parameter to the converter. Since the button is dis/enabled by the command in the model with the CanExecute method, you can then use the IsEnabled property to set the visibility of the button with the converter.
The reason that your trigger doesn't work is that the style is overridden by the attribute on the button itself.
You can use a converter as Metro Smurf suggests, alternatively you can move the visibility attribute into the style so that the trigger works properly
Just add:
<Style.Setters>
<Setter Property="Visibility" Value="Hidden" />
</Style.Setters>
To the style and then remove the attribute.

Resources