not to set Hyperlink when NavigateUri is null - wpf

I am using Hyperlink in TextBlock. The problem I am facing is when NavigateUri is null, I don't want to set Hyperlink or use default style, so that there is no difference between TextBlock and Hyperlink. How to do this?
The code that I am using is this:
<TextBlock TextWrapping="Wrap">
<Hyperlink NavigateUri="{Binding Path=Href}" RequestNavigate="Hyperlink_RequestNavigate">
<Run Text="{Binding Path=Body}"/>
</Hyperlink>
</TextBlock>
Sometimes Href is null. That time I don't have to set NavigateUri.

The solution I used is using DataTrigger to check Href value, if is equals to Null, set the related properties to imitate TextBlock's style
<Style TargetType="{x:Type Hyperlink}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Href}" Value="{x:Null}">
<Setter Property="Foreground" Value="Black" />
<Setter Property="TextBlock.TextDecorations" Value="{x:Null}" />
<Setter Property="Cursor" Value="Arrow" />
</DataTrigger>
</Style.Triggers>
</Style>
Null value:
!Null Value:

Related

WPF override property specified on element from style trigger

I have two TextBlock Run elements with different colors (one is explicitly set on the element). I want them both to change color to red when the value Foo is zero, using the same style. Is this possible somehow? I would rather not duplicate the Style. This is what I want to work:
<Style x:Key="ForegroundStyleTrigger" TargetType="Run">
<Style.Triggers>
<DataTrigger Binding="{Binding Foo}" Value="0">
<Setter Property="Foreground" Value="{StaticResource RedBrush}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<TextBlock>
<Run Text="{Binding Foo, Mode=OneWay}"
Style="{StaticResource ForegroundStyleTrigger}"/>
<Run Text="{Binding Bar, Mode=OneWay}"
Foreground="Blue"
Style="{StaticResource ForegroundStyleTrigger}"/>
</TextBlock>
But since the locally defined color (the one defined on the element) takes precedence over style triggers, nothing happens and the text stays blue for that text run.
Question: Can I override a TextBlocks runs color from a style resource?
If not, how can achieve the expected result without duplicating the style resource?
Can I override a TextBlocks runs color from a style resource?
No, you cannot override a local value using a style setter.
If not, how can achieve the expected result without duplicating the style resource?
If you want the Foreground of the second Run element to be Blue by default and Red only if the Foo source property returns "0" you could create another style and base this one on your existing one:
<Style x:Key="ForegroundStyleTrigger" TargetType="Run">
<Style.Triggers>
<DataTrigger Binding="{Binding Foo}" Value="0">
<Setter Property="Foreground" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="BlueByDefault" BasedOn="{StaticResource ForegroundStyleTrigger}" TargetType="Run">
<Setter Property="Foreground" Value="Blue" />
</Style>
<TextBlock>
<Run Text="{Binding Foo, Mode=OneWay}" Style="{StaticResource ForegroundStyleTrigger}"/>
<Run Text="{Binding Bar, Mode=OneWay}" Style="{StaticResource BlueByDefault}"/>
</TextBlock>
But you cannot set the Foreground property to a local value if you want your style setters to apply.
You would use Style inheritance to achieve what you're asking. The trick then is that all properties must be in the Style as local explicitly set values override implicitly set Style values:
<Style x:Key="ForegroundStyleTrigger" TargetType="Run">
<Style.Triggers>
<DataTrigger Binding="{Binding Foo}" Value="0">
<Setter Property="Foreground" Value="{StaticResource RedBrush}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="BlueForegroundStyleTrigger" BasedOn="{StaticResource ForegroundStyleTrigger}" TargetType="Run">
<Setter Property="Foreground" Value="Blue"/>
</Style>
<TextBlock>
<Run Text="{Binding Foo, Mode=OneWay}"
Style="{StaticResource ForegroundStyleTrigger}"/>
<Run Text="{Binding Bar, Mode=OneWay}"
Style="{StaticResource BlueForegroundStyleTrigger}"/>
</TextBlock>

Change ListView.ItemTemplate on subelement change

let's say we have simple data class:
public class Ex {
public string Prop1 {...} // notify property
public string Prop2 {...} // notify property
}
and an ObservableCollection of objects of this class. I want to have this collection displayed in a ListView with seperated DataTemplated which is distinguished by Ex.Prop2 (if it is null or empty then template01 is used, otherwise template02). This property can be changed in runtime so simple "trick" with ListView.ItemTemplateSelector does not work :(
How to achieve this functionality? Is it possible to achieve it any other way than listening to NotifyPropertyChanged on each object of the collection and than changing manually the template?
Thanks for your help.
Below piece of code which I already have:
<ListView x:Name="lstTerms"
ItemsSource="{Binding Game.Words}"
HorizontalContentAlignment="Stretch"
Grid.IsSharedSizeScope="True">
<ListView.ItemContainerStyle>
<Style>
<Setter Property="Control.Padding" Value="0" />
</Style>
</ListView.ItemContainerStyle>
<!-- checks if element is null or its Prop2 is null or empty. If so, uses NullTemplate -->
<ListView.ItemTemplateSelector>
<local:MySelectTemplate
NormalTemplate="{StaticResource NormalItemTemplate}"
NullTemplate="{StaticResource NullItemTemplate}" />
</ListView.ItemTemplateSelector>
</ListView>
Instead of using a TemplateSelector, you can have a single DataTemplate containing two Grids, which switch visibility dependent on the property values.
Here is an example:
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid Background="LightBlue" Name="normalGrid">
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Prop1}" Value="{x:Null}">
<Setter Property="Grid.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="{Binding Prop1}"></TextBlock>
</Grid>
<Grid Background="Green" Name="nullGrid">
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=normalGrid, Path=Visibility}" Value="Visible">
<Setter Property="Grid.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="{Binding Prop2}"></TextBlock>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Obviously you could replace the TextBlock elements with UserControls representing your two DataTemplates.
If you want, you can also remove the need for the bulky Styles by binding Grid.Visibility to a property (named, for example, IsVisible) on your ViewModel and using a VisibilityConverter.
I usually just use a ContentControl which changes its ContentTemplate based on a DataTrigger. DataTriggers respond to the value getting changed, while DataTemplateSelectors do not
<Style x:Key="SomeStyleKey" TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Prop2}" Value="{x:Null}">
<Setter Property="ContentTemplate" Value="{StaticResource NullTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding Prop2}" Value="">
<Setter Property="ContentTemplate" Value="{StaticResource NullTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
...
<ListView.ItemTemplate>
<DataTemplate>
<ContentControl Style="{StaticResource SomeStyleKey}" />
</DataTemplate>
</ListView.ItemTemplate>
You could also use a Converter that returns String.IsNullOrEmpty(value) if you wanted a single DataTrigger

Changing TextBlock.Text in trigger didn't work

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".

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} :)

WPF: How to make empty TextBlock not to occupy space?

Let's say that I have a simple layout such as this:
<StackPanel>
<TextBlock Text="{Binding Path=Title}" />
<TextBlock Text="{Binding Path=ShortDescription}" />
<TextBlock Text="{Binding Path=LongDescription}" />
</StackPanel>
Now when I have ShortDescription set to null or empty string there's still a gap in place of second TextBlock. Is there some property to prevent an empty textblock from occupying space? Or should I use some other control?
Thanks.
You want to set the visibility of the textbox to "Collapsed".
Visibility can be either:
Visible - Self explanatory
Hidden - Invisible but still takes up space
Collapsed - Invisible and takes up no space
Edit: You should probably set up a trigger, like so:
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
You may want to try this:
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
This should fix the empty space issue based on a Null / Empty Binding.

Resources