I would like to combine the DisplayNames from two different ViewModels, but only IF the first is not equal to a NullObject.
I could easily do this in either a converter or a parent view model, but am
This displays nothing at all:
<TextBlock Grid.Column="2" Grid.Row="0" >
<TextBlock.Inlines>
<Run Text="{Binding HonorificVm.DisplayName}"/>
<Run Text="{Binding PersonNameVm.DisplayName}"/>
</TextBlock.Inlines>
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding HonorificVm.Honorific}" Value="{x:Static model:Honorific.NullHonorific}">
<Setter Property="Text" Value="PersonNameVm.DisplayName"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Why?
I would split it into two TextBlocks and only change the visibility using a trigger. By using the Inlines and trying to change the Text in the triggers you probably run into precedence problems and the Inlines cannot be extracted to a Setter.
e.g.
<StackPanel Grid.Column="2" Grid.Row="0" Orientation="Horizontal">
<TextBlock Text="{Binding HonorificVm.DisplayName}" Margin="0,0,5,0">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding HonorificVm.Honorific}"
Value="{x:Static model:Honorific.NullHonorific}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text="{Binding PersonNameVm.DisplayName}" />
</StackPanel>
An alternative would be a MultiBinding instead of Inlines:
<TextBlock Grid.Column="2" Grid.Row="0">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text">
<Setter.Value>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="HonorificVm.DisplayName" />
<Binding Path="PersonNameVm.DisplayName" />
</MultiBinding>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding HonorificVm.Honorific}"
Value="{x:Static model:Honorific.NullHonorific}">
<Setter Property="Text" Value="{Binding PersonNameVm.DisplayName}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Related
I have a ComboBox and I'd like to be able to change the style of the text inside the ComboBoxItem based on an attribute of the ComboBoxItem.
<ComboBox ItemsSource="{Binding Countries}">
<ComboBox.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" Focusable="False">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Name}" Style="{StaticResource MyStyle}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</ComboBox.ItemTemplate>
I've been trying to set the TextBlock Style from MyStyle to a different one if the Country's attribute IsWestern is true, but I am not managing. I figure I need a Trigger somewhere, but I can't figure it out and added a variety, but none worked so far.
use DataTrigger to change ContentTemplate:
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Name}" Style="{StaticResource MyStyle}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsWestern}" Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Name}" Style="{StaticResource WesternStyle}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
alternatively include <DataTrigger Binding="{Binding Path=IsWestern}" Value="True"> in TextBlock Style (MyStyle) and change required TextBlock properties there:
<Style x:Key="MyStyle" TargetType="TextBlock">
<Setter Property="LineStackingStrategy" Value="BlockLineHeight"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsWestern}" Value="True">
<Setter Property="Padding" Value="20,0,0,0"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
I want to use a NumercUpDown value designed inside a ControlTemplate in another NumericUpDown DatatTrigger, to set up its maximum value based on a condition.
Code -1
<ControlTemplate x:Key="OrderInfo" TargetType="ContentControl">
<TextBlock Grid.Row="0" Grid.Column="0" Style="{StaticResource TextBlockStyle}">Limit Price:</TextBlock>
<i:NumericUpDown Grid.Row="0" Grid.Column="1" x:Name="Price" i:Skin.IsPrice="True" RoundingDecimalPlaces="{Binding Source={StaticResource PriceFormat}, Path=MaxDecimalPlaces}" DisplayFormat="{Binding Source={StaticResource PriceFormat}, Path=StringFormat}" Minimum="0" Increment="{Binding Path=PriceIncrement.Value, TargetNullValue=1}" IncrementCount="{Binding Path=PriceIncrementCount.Value}">
<i:NumericUpDown.Style>
<Style TargetType="i:NumericUpDown" BasedOn="{StaticResource BasicStyle}">
<Setter Property="Value" Value="{Binding Path=Price.Value, ValidatesOnExceptions=True, ValidatesOnDataErrors=True}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Price.IsAvailable}" Value="False">
<Setter Property="Value" Value="{Binding Path=Price.EstimatedPrice, Mode=OneWay}" />
</DataTrigger>
</Style.Triggers>
</Style>
</i:NumericUpDown.Style>
</i:NumericUpDown>
</Grid>
</ControlTemplate>
Want to use the above Price element in the below Data Trigger
<i:NumericUpDown Grid.Row="0" Grid.Column="3" x:Name="CompletionPrice"
Value="{Binding Path=ExternalAlgoProperties[(i:Description)CompletionPrice].Value,
ValidatesOnExceptions=True, ValidatesOnDataErrors=True}" IsEnabled="True"
RoundingDecimalPlaces="0" Increment="1" Minimum="0">
<i:NumericUpDown.Style>
<Style TargetType="i:NumericUpDown" BasedOn="{StaticResource BasicStyle}">
<Setter Property="Maximum" Value="0"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Side.Code,ConverterParameter={x:Static i:SideCodes.Sell}, Converter={StaticResource EqualsConverter},UpdateSourceTrigger=PropertyChanged}" Value="True">
<Setter Property="Maximum" Value="{Binding ElementName=Price,Path=Text,UpdateSourceTrigger=PropertyChanged}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</i:NumericUpDown.Style>
</i:NumericUpDown>
USe Ancestor Binding to access
<DataTrigger
Binding="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type i:NumericUpDown }},
Path=value}"
Value="True">
//Set property
</DataTrigger>
On my Grid I Have TextBlock and Button.
If Button is not visible I want my TextBlock.HorizontalAlignment to be set to Center.
If Button is visible I want my TextBlock.HorizontalAlignment to be set to Right. Here is my code:
<TextBlock Grid.Row="0" VerticalAlignment="Center" Name="myTextBlock" Text="{Binding TileTextId}" TextWrapping="Wrap" TextAlignment="Center" >
<TextBlock.Triggers>
<DataTrigger Binding="{Binding ElementName=myButton, Path=IsVisible}" Value="True">
<Setter Property="HorizontalAlignment" Value="Right" />
</DataTrigger>
</TextBlock.Triggers>
</TextBlock>
I get the error:
'HorizontalAlignment' member is not valid because it does not have a qualifying type name.
So I tried to add TextBlock.HorizontalAlignment, like this:
<TextBlock Grid.Row="0" VerticalAlignment="Center" Name="myTextBlock" Text="{Binding TileTextId}" TextWrapping="Wrap" TextAlignment="Center" >
<TextBlock.Triggers>
<DataTrigger Binding="{Binding ElementName=myButton, Path=IsVisible}" Value="True">
<Setter Property="TextBlock.HorizontalAlignment" Value="Right" />
</DataTrigger>
</TextBlock.Triggers>
</TextBlock>
I get the error:
XamlParseException
How should I do that?
Don't try to use TextBlock.Triggers, instead go for a Style with Style.Triggers.
<StackPanel>
<TextBlock Text="TextBlock Content" Margin="5">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=myButton,Path=IsVisible}" Value="True">
<Setter Property="HorizontalAlignment" Value="Right"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<Button x:Name="myButton" Content="Click Me!" Margin="5"/>
</StackPanel>
Take note of the documentation, as it mentions, why style triggers are needed here.
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.
Try to do this with Style
<TextBlock Grid.Row="0" VerticalAlignment="Center" Name="myTextBlock" Text="{Binding TileTextId}" TextWrapping="Wrap" TextAlignment="Center" >
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=myButton, Path=IsVisible}" Value="True">
<Setter Property="HorizontalAlignment" Value="Right" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
I have following ControlTemplate which works fine
<ControlTemplate x:Key="TotalCostsStatisticTemplate">
<StackPanel x:Name="ContentHolderPanel" Visibility="Collapsed"
Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock x:Name="ValueTextBlock" VerticalAlignment="Center"
Style="{DynamicResource PhasingValueTextStyle}">
<TextBlock.Text>
<MultiBinding Converter="{ttConverters:CustomDisplayFormatConverter}">
<Binding Path="FormatSettings" />
<Binding Path="AvailableStatistics.CostsFormat"/>
<Binding Path="TotalCosts" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ShowTotalCosts}" Value="True">
<Setter TargetName="ContentHolderPanel" Property="Visibility" Value="Visible"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
But if I add another DataTrigger then the MultiBinding on TextBlock.Text doesn't work when control using this template is loaded for first time i.e. the converter CustomDisplayFormatConverter fires only once with all values as UnsetValue and doesn't fire again (it works fine on reloading the window again).
<ControlTemplate x:Key="TotalCostsStatisticTemplate">
<StackPanel x:Name="ContentHolderPanel" Visibility="Collapsed"
Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock x:Name="ValueTextBlock" VerticalAlignment="Center"
Style="{DynamicResource PhasingValueTextStyle}">
<TextBlock.Text>
<MultiBinding Converter="{ttConverters:CustomDisplayFormatConverter1}">
<Binding Path="FormatSettings" />
<Binding Path="AvailableStatistics.CostsFormat"/>
<Binding Path="TotalCosts" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ShowTotalCosts}" Value="True">
<Setter TargetName="ContentHolderPanel" Property="Visibility" Value="Visible"/>
</DataTrigger>
<!--Trigger causing problem (breaking TextBlock.Text multi binding on first load) -->
<DataTrigger Binding="{Binding IsCostsComplete}" Value="False">
<Setter TargetName="ValueTextBlock" Property="Foreground"
Value="{DynamicResource ManagerErrorBrush}"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
If I place this DataTrigger inside TextBlock.Style it works fine!
Any idea what's wrong?
Update:
Here's how its getting used
<ControlTemplate x:Key="PhasingStatisticValuesTemplate">
<StackPanel x:Name="ContentHolderPanel" Orientation="Horizontal" Visibility="{Binding IsValid, Converter={StaticResource boolToVisibilityConverter}}">
<!-- Other control elements based on various templates -->
<Control Margin="20,10" Template="{DynamicResource TotalCostsStatisticTemplate}"/>
</StackPanel>
</ControlTemplate>
This PhasingStatisticValuesTemplate is used in another ControlTemplate, which is then used inside a DataTemplate, so there is a long hierarchy of ControlTemplates -
<ControlTemplate x:Key="PhasingStatisticsTemplate">
<Grid>
<ScrollViewer Style="{DynamicResource CompactHorizontalScrollViewerStyle}" Name="ScrollContainer">
<Grid Name="ScrollViewerGrid" Background="Transparent">
<ContentControl Template="{DynamicResource PhasingStatisticValuesTemplate}" Name="ScrollViewerContent" />
</Grid>
</ScrollViewer>
</Grid>
</ControlTemplate>
Another useful info. might be that existing style on that TextBlock also updated Foreground, not sure how that can cause this behavior -
<Style x:Key="PhasingValueTextStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsComplete}" Value="False">
<Setter Property="Foreground" Value="{DynamicResource ManagerErrorBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
Can anyone suggest the best way to display a Textblock (with a text such as "List Empty") so that it's visibility is bound to the Items.Count.
I have tried the following code and can't get it to work, so think that I must be doing it wrong.
<ListBox x:Name="lstItems"
ItemsSource="{Binding ListItems}">
</ListBox>
<TextBlock Margin="4" FontStyle="Italic" FontSize="12" Text="List is empty" Visibility="Collapsed">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=lstItems, Path=Items.Count}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
The problem in your code is that setting the value of Visibility in the text block itself has higher priority than setting it in the style. So, even when the trigger occurs, the setting inside the trigger has no effect. Change the XAML to:
<TextBlock Margin="4" FontStyle="Italic" FontSize="12" Text="List is empty" >
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=lstItems, Path=Items.Count}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Where the setting of Visibility is all in the style and it works (at least in my demo project).