XAML custom text in TextBlock - wpf

Good afternoon,
I´m binding a list of doubles (Latlng) to an ItemsControl and in a TextBlock I want to make a custom text when the list binded has a count of 0. With the code I have the TextBlock is empty when the ItemsSource of the ItemsControl is that list.
What am I doing wrong?
Btw the list Latlng is a property of a class.
<ItemsControl Name="icLatLng" ItemsSource="{Binding Latlng}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock FontFamily="Arial" FontSize="14">
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.Text" Value="{Binding}"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl}, Path=Items.Count}" Value="0">
<Setter Property="TextBlock.Text" Value="—"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Thanks in advance.

Welcome to SO.
ItemsControl.ItemTemplate is the template that gets applied to each element in the Items list the control is bound to. If there are no items to begin with, then it won't be created, so anything you do in it won't be seen.
I suspect what you're really trying to do is replace the look of the entire control when the list is empty. If so, you can do that by applying a new template to the ItemsControl itself using a DataTrigger on the HasItems property:
<ItemsControl Name="icLatLng" ItemsSource="{Binding Latlng}">
<ItemsControl.Style>
<Style TargetType="{x:Type ItemsControl}" BasedOn="{StaticResource {x:Type ItemsControl}}">
<Style.Triggers>
<DataTrigger Binding="{Binding HasItems, RelativeSource={RelativeSource Self}}" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<TextBlock Text="-" />
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ItemsControl.Style>
</ItemsControl>

Related

How to use IsEnabledChanged event on element inside style?

I have an element specified inside a style. I need the codebehind to know when IsEnabled changes on one of the elements (the Border in this simplified example), so initially I just set the event like normal:
<UserControl.Resources>
<Style x:Key="TypedValueHelper" TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding Type, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"
Value="Markdown">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Border x:Name="BorderThing"
IsEnabledChanged="BorderThing_IsEnabledChanged"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
Then Visual Studio complained at me and I remembered that you can't set events directly on an element in a style, so I fixed it:
<UserControl.Resources>
<Style x:Key="BorderThing" TargetType="Border">
<EventSetter Event="IsEnabledChanged" Handler="BorderThing_IsEnabledChanged"/>
</Style>
<Style x:Key="TypedValueHelper" TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding Type, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"
Value="Markdown">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Border x:Name="BorderThing"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
But now it's telling me The event 'IsEnabledChanged' is not a RoutedEvent ☹
How do I get my event?

WPF DataTemplate on ListView to visualize different kinds of user controls

I have a ListView which I want to present different kinds of user controls, depending on which view model is set for the list view item.
In xaml:
<ListView ItemsSource="{Binding Items}">
<ListView.Resources>
<DataTemplate DataType="{x:Type viewModels:LabelledTextViewModel}">
<controls:LabelledTextBox/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:FolderChooserViewModel}">
<standardControls:FolderChooser/>
</DataTemplate>
</ListView.Resources>
</ListView>
Now this works fine, but my LabelledTextViewModel can be editable or not editable. How do I say in XAML to check the property "IsEditable" on my viewmodel, and depending on its value show LabelledTextBlockControl or LabelledTextBoxControl?
You can use a DataTrigger in your DataTemplate:
<ListView ItemsSource="{Binding Items}">
<ListView.Resources>
<DataTemplate DataType="{x:Type viewModels:LabelledTextViewModel}">
<Grid>
<controls:LabelledTextBlockControl x:Name="textBlock"/>
<controls:LabelledTextBoxControl x:Name="textBox" Visibility="Collapsed"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsEditable}" Value="True">
<Setter TargetName="textBlock" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="textBox" Property="Visibility" Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:FolderChooserViewModel}">
<standardControls:FolderChooser/>
</DataTemplate>
</ListView.Resources>
</ListView>
For your requirements, you either need to use the DataTemplateSelector Class to make that selection for you, or you could try to name your DataTemplates and set them using a DataTrigger:
<ListView>
<ListView.Resources>
<DataTemplate x:Key="DefaultDataTemplate" DataType="{x:Type viewModels:LabelledTextViewModel}">
<controls:LabelledTextBox/>
</DataTemplate>
<DataTemplate x:Key="AnotherDataTemplate" DataType="{x:Type viewModels:FolderChooserViewModel}">
<standardControls:FolderChooser/>
</DataTemplate>
</ListView.Resources>
<ListView.Style>
<Style TargetType="{x:Type ListView}">
<Setter Property="ItemTemplate" Value="{StaticResource DefaultDataTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsEditable}" Value="True">
<Setter Property="ItemTemplate" Value="{StaticResource AnotherDataTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
</ListView>
This example assumes that you are setting the DataTemplates to the ItemTemplate property... if not, you'll need to change that property to the relevant one.

How to change TreeViewItem background color according to property of bound data?

I have a TreeView where the data is bound to generic derived wrapper classes over my data hierarchy.
My bound wrapper classes include added fields like "IsHilighted" and "IsExpanded".
I would like to change the background of any TreeViewItem according to its bound data property "IsHiglighted". I would like to set the color of Hilighted item to the same (or lighter) color as the default Selected item background color.
Ideally, I would like to not modify existing XAML... I mean being able to eventually add the behavior through code.
UPDATE
I have found a partial solution: I had to add triggers as defined below. Code included below.
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsHilighted}" Value="true">
<Setter Property="Background" Value="SlateBlue"></Setter>
<Setter Property="Opacity" Value="160"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
Still not resolved: How could I bind the color of Hilighted item (see partial solution above) to the "Selected" TreeViewItem background color, i.e. replace "SlateBlue" on partial solution to binding to existing selected item style background color ?
Original TreeView XAML code:
<TreeView Name="TreeViewSelectScopeStudy" MinHeight="24" Margin="7" ItemsSource="{Binding Path=TvItemRootPssTreeViewRoot.ChildsView}" Height="Auto"
VerticalAlignment="Stretch"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<TreeView.Resources>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded}"/>
</Style>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red" />
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Green" />
<HierarchicalDataTemplate DataType="{x:Type scopeSelection:WrapperSimulatedInfoStudy}" ItemsSource="{Binding Path=Childs}">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsSelected}"></CheckBox>
<TextBlock Text="{Binding Path=TvItemName}" Margin="5,0,0,0"></TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type scopeSelection:WrapperSimulatedInfoSimulation}">
<StackPanel Orientation="Horizontal" ToolTip="{Binding Path=Item.InvalidityReason}">
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Item.IsValid}" Value="false">
<Setter Property="Opacity" Value="160"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<CheckBox IsChecked="{Binding Path=IsSelected}" IsEnabled="{Binding Path=Item.IsValid}" ToolTip="{Binding Path=Item.InvalidityReason}"></CheckBox>
<CheckBox IsChecked="{Binding Path=IsHilighted}"></CheckBox>
<TextBlock Text="{Binding Path=TvItemName}" Margin="5,0,0,0" ToolTip="{Binding Path=Item.InvalidityReason}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Item.IsValid}" Value="false">
<Setter Property="Background" Value="LightPink"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
You could define one more property called IsItemSelected and bind it to TreeViewItems IsSelected property (similar to how you have done for IsExpanded).
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded}"/>
<Setter Property="IsSelected" Value="{Binding Path=IsItemSelected}"/>
</Style>
Then you could define a DataTrigger for the IsItemSelected property and set the background color.
<DataTrigger Binding="{Binding Path=IsItemSelected}" Value="true">
<Setter Property="Background" Value="Blue"></Setter>
</DataTrigger>

Combobox ItemTemplate and foreground

I have a comboBox that is bound to a list of strings from my viewModel. What I am trying to do is have the foreground of a comboBox item be set to a different color if a property in my viewModel is true:
<ComboBox x:Name="myComboBox" ItemsSource="{Binding Names}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ...}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsActive}" Value="True">
<Setter Property="Foreground" Value="Navy"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I am not sure what to bind the Text of the TextBlock to. All I want is to display the list of strings. I always end up with a dropdown that has the items but they are not visible. I tried removing the style trigger thinking that maybe I was screwing up there, but that didn't help.
Am I taking the right approach? Will the ComboBox.ItemTemplate correctly look at my viewModel (which is the data context) when searching for IsActive or is that binding incorrect as well?
The DataContext for each ComboBoxItem is a string so
For the TextBlock, bind to the DataContext like Text="{Binding}
For the DataTrigger to be able to find IsActive, use RelativeSource in the binding
<ComboBox x:Name="myComboBox" ItemsSource="{Binding Names}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ComboBox}},
Path=DataContext.IsActive}"
Value="True">
<Setter Property="Foreground" Value="Navy"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

WPF - Access the parent Control from inside a Style Setter ControlTemplate

When making controls non-amendable we display them as a TextBox to keep a consistent style. The problem is that a ComboBox can have any type of data so binding the Text property of the ControlTemplate TextBox is not as simple as using SelectedItem.
<Style TargetType="{x:Type ComboBox}">
<Style.Triggers>
<Trigger Property="IsReadOnly" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBox Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}, Path=????, Converter={StaticResource ResourceKey=ComboToTextConverter}, UpdateSourceTrigger=PropertyChanged}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
The idea I have is to use a Converter and send the whole ComboBox so it can be handled by the Converter code. Is there anyway to do this?
Any other suggestions are welcome!
you need to use the SelectedValue and SelectedValuePath properties:
<Style TargetType="ComboBox" x:Key="cStyle">
<Style.Triggers>
<Trigger Property="IsReadOnly" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<TextBox Text="{Binding RelativeSource=
{RelativeSource TemplatedParent},
Path=SelectedValue}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
and heres your ComboBox
<ComboBox Name="cbox" ItemsSource="{Binding}"
Style="{StaticResource cStyle}"
SelectedValuePath="SomeText"
DisplayMemberPath="SomeText" />
now when you set the IsReadOnly property to true on the ComboBox, it turns into a TextBox with the selected value as its text.

Resources