I have a TextBlock and I want to set the property Visibility to Collapsed when the TextBlock has no text. I wonder me, for sure there should be a better way to check if the Lenght of the property Text is equal than 0.
<TextBlock Name="TextBlockHeader" Foreground="White" FontSize="18" FontWeight="Bold" Text="{Binding Header}" Margin="0,0,0,25">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=TextBlockHeader, Path=Text.Length}" Value="0">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Here I have to define a name for the TextBlock and I can reference it in the Datatrigger Binding="{Binding ElementName=TextBlockHeader, Path=Text.Length}"
But how can I achieve the same without having to define a name for the TextBlock?
You would usually use Triggers instead of DataTriggers, and compare the Text property to either null or an empty string.
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="Text" Value="">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
</Style.Triggers>
</Style>
As the TextBlock class seems to coerce the Text property value to be non-null, it may be sufficient to have only the second Trigger for an empty string.
Related
I use Visual Studio 2019 with WPF / MVVM.
I have set a trigger to a textbox to control its visibiliy.
And during runtime this works well, the trigger checks the state of a radiobutton and sets the visibiliy of the textbox accoring to the radiobutton's state.
But during designtime this textbox is not visible.
How could I make this textbox to be visible during designtime ?
This is the XAML I have for the trigger:
<Style x:Key="text" TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Radiobutton1, Path=IsChecked}" Value="true">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=Radiobutton1, Path=IsChecked}" Value="false">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
<TextBox Style="{StaticResource text}" Text="test..... />
I found this article https://social.msdn.microsoft.com/Forums/en-US/cacc5c30-8aa0-43c5-ad07-b063028653a2/designmode-and-visibility?forum=wpf and did some tests using "DesignerProperties.IsInDesignMode" but I can not make this run,I get errors like "datatrigger can not be added to setterbasecollection".
Also I don't know if "DesignerProperties.IsInDesignMode" is the right approach...
I think the answer is even simpler. By adding d:Visibility="Visible", the textbox will be visible at design time.
<TextBox d:Visibility="Visible" Style="{StaticResource text}" Text="test..... />
This is a workaround:
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Radiobutton1, Path=IsChecked}" Value="true">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=Radiobutton1, Path=IsChecked}" Value="false">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding Designtime}" Value="true">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
then in Viewmodel:
public bool Designtime { get; set; }
public ViewModel()
{
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
{
Designtime = true;
}
}
And in the Window Tag
d:DataContext="{d:DesignInstance {x:Type local:ViewModel},IsDesignTimeCreatable=True}"
You can use the Blend namespace IsHidden attribute:
add the Blend namespace if missing: xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
add the d:IsHidden="True" attribute on the element you want to hide at design time
Example:
<TextBox Style="{StaticResource text}" Text="test....." d:IsHidden="True"/>
This behavior seems incredibly odd to me, and I assume I am doing something wrong to get it. I have a ContentControl that uses a DataTemplete to render an TabControl. I want an image to display when there are no tabs open, and hide when there are. But here is the problem:
<Image Name="image1" Stretch="Uniform" Visibility="Hidden" Source="/Affinity;component/Images/affinity_logo.png">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, ElementName=tabcontrolworkspaces}"
Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
This doesn't work... sort of.
I have tested this on Visiblity and Margin (just to be sure). This trigger will alter the property, unless that property is defined in the Image tags. If it is, the trigger will not update that property. So, if I don't define a visibility for the image, and the trigger hides it, it works. The problem is, the default is Visible and the trigger needs to show it when value=0 and hide it otherwise.
Why won't the trigger override properties that are explicitly defined? Isn't that its purpose?
This is the normal Dependency Property Value Precedence. Setting it on Image is at #3, while in the Style trigger is at a lower precedence of #6.
You can do this instead:
<Image Name="image1" Stretch="Uniform" Source="/Affinity;component/Images/affinity_logo.png">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, ElementName=tabcontrolworkspaces}"
Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
Set your Visibility in the Style in addition to in the Trigger
I've encountered this weird behavior with DataTriggers many times, where sometimes DataTrigger Setters won't take effect unless the Setter is also defined in the Style.
Won't work
<Image Visibility="Collapsed">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding Something}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
<Image.Style>
</Image>
Will work
<Image>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding Something}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
<Image.Style>
</Image>
Edit: See the accepted answer for an explanation on why this doesn't work. It has to do with the order in which dependency properties are determined, where properties defined in the <Tag> always take precedence over triggered values.
When a trigger is true it changes the value to the desired value. When it is no longer true it returns the value to the previous value. It won't change it to a value it wans't.
This means that if the original value is visible, and you change it to visible, when the trigger is no longer active, the value will revert back to visible.
<DataTemplate>
<Image Name="ServerImageSetter" Source="{svg2Xaml:SvgImage ServerAdministrator2;component/Images/test1.svg}"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ServerKindTrigger}" Value="0">
<Setter TargetName="ServerImageSetter" Property="Source" Value="{svg2Xaml:SvgImage ServerAdministrator2;component/Images/test2.svg}"/>
</DataTrigger>
<DataTrigger Binding="{Binding ServerKindTrigger}" Value="1">
<Setter TargetName="ServerImageSetter" Property="Source" Value="{svg2Xaml:SvgImage ServerAdministrator2;component/Images/test3.svg}"/>
</DataTrigger>
<DataTrigger Binding="{Binding ServerKindTrigger}" Value="2">
<Setter TargetName="ServerImageSetter" Property="Source" Value="{svg2Xaml:SvgImage ServerAdministrator2;component/Images/test3.svg}"/>
</DataTrigger>
</DataTemplate
In my C# code I switch the kind of my value to 0-2 and that should change the image. But the change doesn't work.
public int ServerKindTrigger
{
get { return _serverKindTrigger; }
set { _serverKindTrigger = value;
RaisePropertyChanged();
}
}
//Here I switch my serverkind and wanna change the image value
switch (_serverKind)
{
case Servers.test: _serverKindTrigger = 0;
Besides setting the backing field _serverKindTrigger instead of the property ServerKindTrigger, and thus not firing the PropertyChangedEvent, you are directly setting the Source property of the Image control:
<Image Source="{svg2Xaml:SvgImage ServerAdministrator2;component/Images/test1.svg}"/>
This so-called local value always has a higher precedence than the values from the Setters in the DataTriggers. For reference, see Dependency Property Value Precedence.
Change your XAML to this:
<Image>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="{svg2Xaml:SvgImage ServerAdministrator2;component/Images/test1.svg}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ServerKindTrigger}" Value="0">
<Setter Property="Source" Value="{svg2Xaml:SvgImage ServerAdministrator2;component/Images/test2.svg}"/>
</DataTrigger>
<DataTrigger Binding="{Binding ServerKindTrigger}" Value="1">
<Setter Property="Source" Value="{svg2Xaml:SvgImage ServerAdministrator2;component/Images/test3.svg}"/>
</DataTrigger>
<DataTrigger Binding="{Binding ServerKindTrigger}" Value="2">
<Setter Property="Source" Value="{svg2Xaml:SvgImage ServerAdministrator2;component/Images/test3.svg}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
You are changing local fields and not the property, which is why it isn't triggering binding updates.
Change your switch/case assignment to assign ServerKindTrigger property.
switch (_serverKind)
{
case Servers.test: ServerKindTrigger = 0;
I am a noob at Xaml triggers.
Do you see any obvious reason why this trigger does not work:
<TextBox Text="{Binding TiretNom}" Margin="1">
<TextBox.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding TiretNom}" Value="">
<Setter Property="TextBox.Background" Value="Tomato"/>
</DataTrigger>
<DataTrigger Binding="{Binding TiretNom}" Value="a">
<Setter Property="TextBox.BorderBrush" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Neither does the backround change when the property TiretNom = "",
nor does the borderbrush gets red if TiretNom = "a"
Thx in advance.
I'm trying to bind some data to a TextBlock. The TextBlock is to display a slider bar value, and when the slider bar value has changed I want the TextBlock text to change colour to red.
My XAML is like this:
<Grid Height="227">
<TextBlock Margin="114,60,112,150" Name="textBlock1" Text="{Binding Path=DispVal}" Width="42" Grid.Column="1" HorizontalAlignment="Center" TextAlignment="Center" FontWeight="Bold">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Black"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChanged}" Value="true">
<Setter Property="TextBlock.Foreground" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsChanged}" Value="false">
<Setter Property="TextBlock.Foreground" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
I have bound the TextBlock using DataContext:
texBlock1.DataContext = m_slider;
I update the m_slider object when my slider bar update handler fires.
However, I don't get any text or colour changes.
you could replace textblock with textbox and make it similar in look and feel by applying the following properties
<Setter Property="IsReadOnly" Value="True"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
Then you can use the TextChanged event in order to update its style any time text is changed by your binding