Changing TextBlock.Text in trigger didn't work - wpf

I have the next code in my view:
<Style x:Key="documentFileNameStyle">
<Setter Property="TextBlock.Foreground" Value="Gray"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Untitled}" Value="True">
<Setter Property="TextBlock.FontStyle" Value="Italic"/>
<Setter Property="TextBlock.Text" Value="no file name"/>
</DataTrigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="documentTemplate">
<TextBlock Text="{Binding Path=FileName}" Style="{StaticResource documentFileNameStyle}"/>
</DataTemplate>
But setting TextBlock.Text to a string didn't work. TextBlock.FontStyle changes to Italic, so whole trigger works properly. What is wrong?

Local assignment of Properties has a higher precedence than setting the values in triggers.
Also you are using Binding (Path=FileName) to set the Text-Property of the TextBlock. So changing the Text in Triggers doesn´t effect the Property.
As you are using Binding. I would change the Property "FileName" to return "no file name" if the Property "Untitled" is "true".

Related

wpf visibility based on a condition

I want to show a StackPanel based on a particular condition. In this example I've used the BorderThickness property:
<ContentControl x:Name="gridDati" VirtualizingPanel.VirtualizationMode="Recycling" VirtualizingPanel.ScrollUnit="Item" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Items}" Value="{x:Null}">
<Setter Property="BorderThickness" Value="0" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Items.Count}" Value="0">
<Setter Property="BorderThickness" Value="12" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="pnlLoading" Visibility="Visible">
<Label Content="">
<Label.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=BorderThickness, ElementName=gridDati, UpdateSourceTrigger=PropertyChanged}" Value="0">
<Setter Property="TextBlock.Text" Value="" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=BorderThickness, ElementName=gridDati, UpdateSourceTrigger=PropertyChanged}" Value="12">
<Setter Property="TextBlock.Text" Value="STAND BY" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</StackPanel>
Basically when in the code behind I apply a template on gridDati, while the item counter is still zero, the border is set correctly to 12. After that it turns to zero (item binded) and this behevior is what I want.
So, I also would like to show a StackPanel at the same condition, so I used a DataTrigger but seems that is not fired at all. How can I "link" these two condition? so show a stackpanel when I have items in the datagrid?
This is the proper way to declare the Label so you get the desired result.
<Label>
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Items.Count}" Value="0">
<Setter Property="Content" Value="STAND BY"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
But there are a few points I need to explain to make sure you understand what I changed and why. I'm going to go through the XAML from the inside out.
First, I changed the Setter to use the right property name. You're using a Label, and your old Setter had Property="TextBlock.Text". TextBlock.Text is not a valid property name for a Label (no such property exists), so that wasn't going to work. The property you want is called Content.
Moving up one level to the DataTrigger. Instead of binding to gridDati that is binding to Items, I just bound directly to Items. You could do it the other way, but in my opinion it would be unusual and it might cause unforeseen bugs.
Next, you'll notice I removed the first DataTrigger. WPF dependency properties can be set in a number of different ways, and there is an order of precedence for which value will be taken over others. The default value (lowest precedence) for a Label's content is for it to be empty. When the DataTrigger applies the Setter, it overrides that value (it has higher precedence). When the DataTrigger condition is no longer fulfilled (Items.Count != 0), WPF stops applying the Setter and the value reverts back to the default, because there is no longer any value of higher precedence overriding it. So you don't need to add a second DataTrigger resetting to default, wit ill do that automatically.
Moving up further you'll see I changed the opening Style tag to <Style TargetType="Label">. It's common practice to set the TargetType of a Style. Doing this also gives you IntelliSense options for Setters in that Style, which might have helpped you catch the mistake you made by trying to use TextBlock.Text as a property name.
Finally, I removed Content="" from the opening Label tag. Setting the value of a property directly on an element in XAML has a very high precedence, which overrides all Styles and DataTriggers. As long as this was there, nothing you did in any Style would change anything for the Label's Content.

Binding to the Items properties of an ItemsControl

I'm looking to a way to bind a Button Text or IsEnabled property for example to the IsChecked property in an ItemsControl
Here is my simplified source code :
<StackPanel>
<ItemsControl ItemsSource="{Binding Tasks}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Margin="10,0,10,0" Text="{Binding Name}"/>
<CheckBox IsChecked="{Binding InProgress}"/>
<CheckBox IsChecked="{Binding Done}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button/>
</StackPanel>
I would like (for example) if all the "Done" CheckBoxes are Checked, to set Button Text to some value or to enable it. I thought doing this with Data Binding in Xaml using DataTriggers but I don't know how to do it.
Ca anyone give me a full xaml solution ?
New Answer
Sorry, I misunderstood the question. I would expose another property from your DataContext that simply returns true/false if all the items in the collection are checked or not, and base your Button's Text/IsEnabled off that property using a DataTrigger
<Style TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsAllChecked}" Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
Old Answer
DataTriggers simply take a binding, and check if the result is equal to some value.
<Style TargetType="{x:Type TextBox}">
<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding Done}" Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
If you need to test more than one condition, you need a MultiDataTrigger
<Style TargetType="{x:Type TextBox}">
<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=InProgress}" Value="True" />
<Condition Binding="{Binding Path=Done}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
Note that the default value of setter is part of the style. This is important because if you set the default value on the <TextBox> then it has a higher priority than triggered values, so triggered values are unable to change the current value.
You can change this line...
<Button/>
To this
<Button Content={Binding ButtonContent}/>
And set the 'ButtonContent' property in your ViewModel whenever your 'Done' check box gets checked.
Usually, the best way to do that is to synthesize the state you want to apply to your button into a property of your ViewModel.
The WPFy way in that case is for your ViewModel to expose a command that implements the action of your button.
The command in turns can react to changes in your data to signal if it is available or not, and thus will automaticaally disable/enable your button. An added bonus is that you can easily implement a menu for example, that will be properly enabled/disabled at no cost once you have your command.
It's been a long time since I had any chance to touch WPF, so I do not have any really useful links at hand, but if I were you I would learn about commands in WPF, they are a very important part of the MVVM architecture.

TextBlock trigger instead of using converter

I want to show a number on screen. If that number is 0 I don't want it to show at all.
<TextBlock Text="{Binding Path=Class.Count}" FontSize="20" FontWeight="Bold">
<TextBlock.Triggers>
<DataTrigger Binding="{Binding Path=Class.Count}" Value="0">
<Setter Property="TextBlock.Text" Value=""/>
</DataTrigger>
</TextBlock.Triggers>
</TextBlock>
I attempted the above piece of code after a regular trigger failed to solve my problem. I do not want to write a converter to act upon one specific number. Is there a way to create a trigger that will hide the number if it is 0?
EDIT: When I try to use a regular trigger or a data trigger I get a xaml parse error telling me I need to use an event trigger.
I tried to set the value in the setter to a number to make sure that having a blank value was not causing the problem
You are fighting with the binding to set the Text Property.
I'd make the control collapsed/hidden instead of setting the text to String.Empty.
Less confusion.
EDIT
<TextBox>
<TextBox.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Name.Length}" Value="0">
<Setter Property="UIElement.Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
OR
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding Name.Length}" Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Only a Style allows DataTriggers.
The xaml parser wants to check the property's existence so it needs the ownertype. Visibility is declared on the UIElement class. There are two ways of specifying that as I have shown in both examples.

Conditionals in WPF

I have the following question: I have a Boolean variable in a configuration file. If it is true I want a property in textbox control to be setup according to the value of that variable.
Try the solution above but it does not work. What am I doing wrong?
This is a fragment code:
bool isKeyboardAvtive = true; //read from configuration file
<Style x:Key="StylesTextBox" TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=isKeyboardActive}" Value="True">
<Setter Property="k:TouchScreenKeyboard.TouchScreenKeyboard" Value="True"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=isKyboardActive}" Value="False">
<Setter Property="k:TouchScreenKeyboard.TouchScreenKeyboard" Value="False"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<TextBox Style="{StaticResource StylesTextBox}" Margin="0,5" x:Name="txtUserName" Height="40" Width="150" />
IsKeyboardActive needs to be a public property of the DataContext for the binding to work. Also, you don't need a trigger there, just a binding :
k:TouchScreenKeyboard.TouchScreenKeyboard="{Binding IsKeyBoardActive}"
If you use the standard VS-generated settings, you can also bind to the settings directly :
xmlns:prop="clr-namespace:YourApplication.Properties"
...
k:TouchScreenKeyboard.TouchScreenKeyboard="{Binding IsKeyBoardActive, Source={x:Static prop:Settings.Default}}"
Or even better, using this markup extension :
xmlns:local="clr-namespace:YourApplication"
...
k:TouchScreenKeyboard.TouchScreenKeyboard="{local:SettingBinding IsKeyBoardActive}"

Databinding to XML in a DataTrigger in WPF

In a WPF application, I have correctly bound a DataTemplate to an XML node that looks like:
<answer answer="Tree", correct="false" score="10" />
In my application, I have a TextBlock with the answer in it. At first, I want it invisible, but when the correct attribute in the XML file changes to "true", it must become visible.
My DataTemplate is hooked up correctly, because everything else works. For example, if I change the answer attribute in the XML file (just for testing), it changes in my WPF view. But I'm having troubles with the visibility. This is my XAML:
<TextBlock Text="{Binding XPath=#answer}" Visibility="Hidden">
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding XPath=#correct}" Value="true">
<Setter Property="TextBlock.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
I'm guessing the Databinding in the DataTrigger isn't working correctly. Anyone have a clue?
I have run into the same problem with databound ToggleButtons. Try removing the Visibility="False" and replacing it with another DataTrigger that handles the incorrect case.
I think the issue is that the Visibility property is hard-coded. Try setting the Visibility in the style:
<TextBlock Text="{Binding XPath=#answer}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding XPath=#correct}" Value="true">
<Setter Property="TextBlock.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Sure, it works if you give a specific else case instead of just false. As in my case, it was {x:Null} and value. So when its value to bind is present, it will be true and TextBlock.Visibilty will be set using setters value and when binding path does not have any value inside it, i.e. null in my case, its simply {x:Null} :)

Resources