Using Path=. and Converter inside Binding - wpf

I have trouble defining a trigger for TreeViewItems. I believe it is just some syntax problem, but I don't know what else to write...
This is the Trigger:
<DataTrigger Binding="{Binding Path=., Converter=IsNodeConverter}" Value="True">
<Setter Property="Focusable" Value="False"/>
</DataTrigger>
Since it is defined inside TreeView.ItemContainerStyle, the DataContext should be the contained item itself. The Item can either be of type Node or Entry and I want to trigger for all Items that are of type Node. So I wrote a converter:
public class IsNodeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Node)
return true;
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Which returns true if it gets a Node as input and false otherwise.
But in the part Binding="{Binding Path=., Converter=IsNodeConverter}" the compiler complains: "IValueConverter cannot convert from string." (original: "Vom TypeConverter-Objekt für IValueConverter wird das Konvertieren aus einer Zeichenfolge nicht unterstützt.") I don't understand this at all: DataContext is an object of type Entry or Node, and Binding Path=. should keep it that way. So what is the problem? What string is the compiler talking about? How do I correct this so that the compiler does not complain?
Here is the full code of the TreeView for reference. The collection ´AllNodesAndEntries´ is an ObservableCollection<object> that contains both Nodes and Entrys.
<TreeView ItemsSource="{Binding AllNodesAndEntries}">
<TreeView.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding Children}" DataType="{x:Type usrLibVM:Node}">
<TextBlock Text="{Binding Name}" Background="LightBlue"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type usrLibVM:Entry}">
<TextBlock Text="{Binding Name}" Background="LightSalmon"/>
</DataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=., Converter=IsNodeConverter}" Value="True">
<Setter Property="Focusable" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>

Your converter is certainly declared in a ResourceDictionary, so it should be referenced as StaticResource:
Binding="{Binding Path=., Converter={StaticResource IsNodeConverter}}"
or shorter:
Binding="{Binding Converter={StaticResource IsNodeConverter}}"

Based on answer in thread Using Value Converters in WPF without having to define them as resources first :
<DataTrigger Value="False">
<DataTrigger.Binding>
<Binding> <!-- <Binding Path="."> is possible but not necessary -->
<Binding.Converter>
<converterNamespace:IsNodeConverter/>
</Binding.Converter>
</Binding>
</DataTrigger.Binding>
<Setter Property="Focusable" Value="False"/>
</DataTrigger>

Related

XAML Datagrid set rowStyle if Value greater than

I've got a Datagrid with 3 columns.
Now i want to change the highlight of the row conditionally.
like: if the value in column 2 is greater than XX change row color to Red.
I've already tried something, but without success:
<DataGrid Name="DataGrid1" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3" AlternatingRowBackground="LightGray" ItemsSource="{Binding}" AutoGenerateColumns="False" FontSize="18" CanUserResizeColumns="False" CanUserReorderColumns="False" CanUserSortColumns="False" CanUserAddRows="False">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Steckzyklen}" Value="< 20">
<Setter Property="Background" Value="#FFFF4848"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Steckplatz" Binding="{Binding Name}" Width="200"/>
<DataGridTextColumn Header="Steckzyklen" Binding="{Binding Steckzyklen}" Width="200"/>
<DataGridTextColumn Header="Austauschdatum" Binding="{Binding Austauschdatum}" Width="200"/>
</DataGrid.Columns>
</DataGrid>
In this line: DataTrigger Binding="{Binding Steckzyklen}" Value="&lt 20"
I tried ti do it with the &lt but without succes.
Someone knows how to make it work? Preferably by only changing something in the XAML.
Thanks in advance!
You can change the Style as follows:
Create a new Converter:
public class IsEqualOrLessThanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int intValue = (int)value;
int compareToValue = Int32.Parse(parameter.ToString() ?? string.Empty);
return intValue <= compareToValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And then:
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Steckzyklen, Converter={StaticResource IsEqualOrLessThanConverter}, ConverterParameter=20}" Value="True">
<Setter Property="Foreground" Value="#FFFF4848"/>
<Setter Property="Background" Value="Yellow"/>
</DataTrigger>
</Style.Triggers>
</Style>

How to Check Text Block Text value in Data Trigger in XAML

I want to check text value of text block if value is xyz. i don not want any operation but if text value is '#FF84312F' i want to set this text to foreground color of Text.
Below is my code.
How can i achieve this . Please Help me.
<TextBlock Text="#FF84312F">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text, RelativeSource={RelativeSource Self}}" Value="*#">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Try this:
<TextBlock Text="#FF84312F">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{Binding Text,RelativeSource={RelativeSource Self}}" />
</Style>
</TextBlock.Style>
</TextBlock>
Or this:
<TextBlock Text="#FF84312F">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Text" Value="#FF84312F">
<Setter Property="Foreground" Value="{Binding Text,RelativeSource={RelativeSource Self}}" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
It will set the Foreground to the value specified by the Text property, either unconditionally or conditionally (using a Trigger).
NOTE:
This answer is based on the comments of the answer provided by mm8
You can use a converter to convert your string to a SolidColorBrush:
Converter class:
public class TextToSolidColorBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var color = Brushes.Black;
try
{
var converted = new BrushConverter().ConvertFromString(value?.ToString());
color = converted != null ? (SolidColorBrush) converted : Brushes.Black;
}
catch (Exception e)
{
// ignored
}
return color;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML:
<Window.Resources>
<local:TextToSolidColorBrushConverter x:Key="TextToSolidColorBrushConverter"/>
</Window.Resources>
<TextBlock Text="Any text">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{Binding Text,RelativeSource={RelativeSource Self}, Converter={StaticResource TextToSolidColorBrushConverter}}" />
</Style>
</TextBlock.Style>
</TextBlock>
based on your Comment:
What i want is . I have to check textblock text and if text block text
is color code then assign that color code to foreground. That's it
This will change the Color of your Textblock Foreground if Binded Text Value is a Color code else default color will shown.
<TextBlock Text="{Binding Text}" Foreground="{Binding Text, RelativeSource=
{RelativeSource Self}}"/>
or
<TextBlock Text="#0FFFFF" Foreground="{Binding Text, RelativeSource=
{RelativeSource Self}}"/>

How to OR a local bool with a viewmodel bool and bind to visibility property

I have the following XAML in a usercontrol that loads a series of gauge usercontrols:
<ItemsControl ItemsSource="{Binding InstanceViper.Gauges}" Grid.Row="7" Grid.ColumnSpan="4">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Name="GaugePanel" VerticalAlignment="Bottom" HorizontalAlignment="Left" Orientation="Horizontal">
</WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:Gauge DataContext="{Binding}" AlwaysShowGauge="False"></local:Gauge>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I am passing in a flag that determines if the gauge should be displayed always or only when in alarm. Now in gauge xaml I want to do something like the following to determine if it should be displayed:
<Border Name="MainBorder" Visibility="{Binding (ShowAlarm || AlwaysShowGauge) ,Converter={StaticResource BooleanToVisibilityConverter}}">
Problem is that the ShowAlarm property is from the gauge viewmodel and AlwaysShowGauge is a local property being passed in in the data template. The ShowAlarm is dynamic but AlwaysShowGauge won't change once set. How can I accomplish this?
Its hard to tell where your border comes from, and where the one of the bindings come from. But you can control the Binding flow, that's not a problem. The main problem is how do you multi trigger, below is just a start. Once you see how you can multi trigger, then, you can play with the binding, either control the flow from one view model or view properties, or play with paths from different sources, to do that is you might have to put the following sample in the DataTemplate and use SourceName and TargetName.. But again, it's hard to tell where the border is.
<converters:OrMultiConverter x:Key="Or" />
<Style x:Key="MainBorderStyle" TargetType="{x:Type Border}">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Value="True">
<Condition.Binding>
<MultiBinding Converter="{StaticResource Or}">
<Binding Path="ShowAlarm"/>
<Binding Path="AlwaysShowGaug"/>
</MultiBinding>
</Condition.Binding>
</Condition>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Visibility" Value="Visible"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
public class OrMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Any(value => value == null || value == DependencyProperty.UnsetValue))
return false;
var boolValues = values.Cast<bool>();
var result = boolValues.Any(b => b);
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

WPF: How to hide the empty Hyperlink?

In the previous question of mine, I have asked how to hide an empty TextBlock, so that it doesn't take space in the panel. I have a new challenge now. How am I supposed to hide an empty Hyperlink:
<TextBlock>
<Hyperlink
NavigateUri="{Binding Path=Email}"
RequestNavigate="Hyperlink_RequestNavigate">
<TextBlock Text="{Binding Path=Email}" />
</Hyperlink>
</TextBlock>
This is what made the hiding possible in the previous question:
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
Providing this style on first TextBlock won't work because the Text property is not set. Providing style on Hyperlink doesn't hide the parent TextBlock and same happens if I try to hide the TextBlock inside the Hyperlink.
I am certain that my style needs to be applied on the Hyperlink, but the trigger inside should target the Visibility property of the 'Hyperlink's parentTextBlock`. What is the style supposed to look like?
Just use DataTrigger on the top level TextBlock to check whether the bound property is an empty string:
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Email}" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
EDIT:
Also you can try binding to the child hyperlink's NavigationUri property:
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Inlines
[0].NavigateUri}" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
The solution provided by Foovanadil that solves the similar issue by implementing visibility converter works the best in my opinion. It is the easiest to implement and can be reused whenever needed.
The converter should be implemented like this:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
public class VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
if (string.IsNullOrEmpty(value as string))
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotSupportedException();
}
}
}
And used like this:
<Window.Resources>
<!-- Visibility converter -->
<converters:VisibilityConverter x:Key="visibleConv" />
</Window.Resources>
...
<TextBlock Visibility="{Binding Something, Converter={StaticResource visibleConv}}">
<Hyperlink NavigateUri="{Binding Something}">
<TextBlock Text="{Binding Something}" />
</Hyperlink>
</TextBlock>
All credits go to the original solution provider: Foovanadil

WPF Datatrigger not firing when expected

I have the following XAML:
<TextBlock Text="{Binding ElementName=EditListBox, Path=SelectedItems.Count}" Margin="0,0,5,0"/>
<TextBlock Text="items selected">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=EditListBox, Path=SelectedItems.Count}" Value="1">
<Setter Property="TextBlock.Text" Value="item selected"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
The first text block happily changes with SelectedItems.Count, showing 0,1,2, etc. The datatrigger on the second block never seems to fire to change the text.
Any thoughts?
Alternatively, you could replace your XAML with this:
<TextBlock Margin="0,0,5,0" Text="{Binding ElementName=EditListBox, Path=SelectedItems.Count}"/>
<TextBlock>
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="items selected"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=EditListBox, Path=SelectedItems.Count}" Value="1">
<Setter Property="Text" Value="item selected"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Converters can solve a lot of binding problems but having a lot of specialized converters gets very messy.
The DataTrigger is firing but the Text field for your second TextBlock is hard-coded as "items selected" so it won't be able to change. To see it firing, you can remove Text="items selected".
Your problem is a good candidate for using a ValueConverter instead of DataTrigger. Here's how to create and use the ValueConverter to get it to set the Text to what you want.
Create this ValueConverter:
public class CountToSelectedTextConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((int)value == 1)
return "item selected";
else
return "items selected";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Add the namespace reference to your the assembly the converter is located:
xmlns:local="clr-namespace:ValueConverterExample"
Add the converter to your resources:
<Window.Resources>
<local:CountToSelectedTextConverter x:Key="CountToSelectedTextConverter"/>
</Window.Resources>
Change your second textblock to:
<TextBlock Text="{Binding ElementName=EditListBox, Path=SelectedItems.Count, Converter={StaticResource CountToSelectedTextConverter}}"/>

Resources