WPF converter for labels' content - wpf

I'm trying to override the output of a label, say it contained "Account" and a client wants account rendered as "Member" (so kind of think of this as a localisation converter?)
My Question; is this possible with "hardcoded" content? or MUST i create a static file containing all label content (with iNotifiyStatic of course)? *for binding?
xaml:
<Label Style="{StaticResource LabelLeft}" Content="Account Name:"></Label>
Resource File: Including all attempts made, from multiple sources heres the most meaningful one.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters ="clr-namespace:Company.Resources.Converters">
<converters:LocationLabelsConverter x:Key="LocationLabelsConverter" />
<Style x:Key="LabelLeft" TargetType="{x:Type Label}" >
<Setter Property="Margin" Value="10 0 0 0"></Setter>
<Setter Property="Height" Value="22"></Setter>
<Setter Property="Padding" Value="0 0 0 0"></Setter>
<Setter Property="VerticalContentAlignment" Value="Center"></Setter>
<!-- Att1 -->
<!--<Setter Property="TextBlock.Text" Value="{Binding RelativeSource={RelativeSource self},
Path=Content,
Converter={StaticResource LocationLabelsConverter}}"></Setter>-->
<!-- Att2 -->
<!--<Setter Property="Content">
<Setter.Value>
<Binding Path="Content" RelativeSource="{RelativeSource self}">
<Binding.Converter>
<converters:LocationLabelsConverter/>
</Binding.Converter>
</Binding>
</Setter.Value>
</Setter>-->
<!-- Att3 -->
<!--<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self},
Path=Content,
Converter={StaticResource LocationLabelsConverter}}">
<Setter Property="Content" Value="Test123"/>
</DataTrigger>
</Style.Triggers>-->
</Style>
And here's the converter:
[ValueConversion(typeof(string), typeof(string))]
public sealed class LocationLabelsConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value != null)
{
return "hello sweety";// (string)value; //The goal here in the end is to run it through a method to replace string with correct text.
}
else return null;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return (string)value;
}
}

you can apply converter like this:
<Label Content="{Binding Source='Account Name:', Converter={StaticResource LocationLabelsConverter}"/>

Related

WPF RelativeSource UpdateSourceTrigger=PropertyChanged not worked

I'm using this DataTrigger:
<Window x:Class="_11_5_Style_demo4_DataTrigger.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:_11_5_Style_demo4_DataTrigger"
mc:Ignorable="d"
Title="MainWindow" Height="130" Width="300">
<Window.Resources>
<local:L2BConverter x:Key="cvtr"/>
<!--TextBox DataTrigger-->
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding
RelativeSource={x:Static RelativeSource.Self},
Path=Text.Length,
Converter={StaticResource cvtr},
UpdateSourceTrigger=PropertyChanged}"
Value="false">
<DataTrigger.Setters>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="BorderThickness" Value="1"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<TextBox Margin="5"/>
<TextBox Margin="5,0"/>
<TextBox Margin="5"/>
</StackPanel>
</Window>
I expected that the TextBox will have the red border when typed more than 6 characters, and UpdateSourceTrigger=PropertyChanged just not work when there are more than 6 characters in the TextBox. It updated only when lost focus.
Here is the Converter:
public class L2BConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int textLength = (int)value;
return textLength < 6 ? true : false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I've googled, but didn't find relative problem. Can anybody explains why this not work, am I not using it right?
Your trigger is working correctly, but your expectations of the result of its work are not correct. The trigger correctly sets the border brush, but the problem is that the color of the border while the TextBox has focus is not taken from the TextBox.BorderBrush brush, but from the TextBox Template constant. And you can't change it with a trigger. You need to change the template of the TextBox itself or apply another way to solve your problem.
You can make sure that the trigger works correctly, for example, by changing the frame thickness:
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding
RelativeSource={x:Static RelativeSource.Self},
Path=Text.Length,
Converter={StaticResource cvtr}}"
Value="false">
<DataTrigger.Setters>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="BorderThickness" Value="10"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
Another way to implement such validation is to use a ValidationRule. But its use is only possible in a binding that you don't have. You can use a little "voodoo magic" for this:
public class LengthValidate : ValidationRule
{
public LengthValidate() :base(ValidationStep.UpdatedValue, true) { }
public int LengthLimit { get; set; }
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
int limit = LengthLimit;
if(value is BindingExpression expression)
{
value = expression.GetSourceValue();
}
if (value is not string text)
{
text= value?.ToString()?? string.Empty;
}
return text.Length <= limit
? ValidationResult.ValidResult
: new ValidationResult(false, $"String length exceeds limit={limit}.");
}
}
The GetSourceValue method from the BindingExpressionHelper class is used.
Style with this rule:
<Style TargetType="TextBox">
<Setter Property="Tag">
<Setter.Value>
<Binding Path="Text" RelativeSource="{RelativeSource Self}">
<Binding.ValidationRules>
<local:LengthValidate LengthLimit="6"/>
</Binding.ValidationRules>
</Binding>
</Setter.Value>
</Setter>
</Style>

WPF - Binding data template

I'm trying to bind <DataTemplate> on Telerik RadDiagram ShapeStyle:
<telerik:RadDiagram x:Name="Diagram"
ShapeStyle="{StaticResource NodeStyle}"
GraphSource="{Binding GraphSource}"/>
This style looks like this:
<Grid.Resources>
<Style x:Key="NodeStyle" TargetType="telerik:RadDiagramShape">
<Setter Property="Position" Value="{Binding Position, Mode=TwoWay}" />
<Setter Property="Content" Value="{Binding}" />
<Setter Property="Geometry" Value="{telerik:CommonShape ShapeType=RectangleShape}" />
<Setter Property="ContentTemplate" >
<Setter.Value>
<MultiBinding Converter="{StaticResource StyleConverter}">
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=UserControl}"/>
<Binding Path=""/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
...
</Grid.Resources>
StyleConverter:
class StyleConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
FrameworkElement targetElement = values[0] as FrameworkElement;
if (values[0] is BilledMsisdnViewModel)
return targetElement.FindResource("BilledMsisdnControl");
else if(values[0] is BilledImeiViewModel)
return targetElement.FindResource("ImeiControl");
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Those resources (BilledMsisdnControl and ImeiControl) are defined in <UserControl.Resources>:
<UserControl.Resources>
<ResourceDictionary>
<local:BilledMsisdnControl x:Key="BilledMsisdnControl"/>
<local:ImeiControl x:Key="ImeiControl"/>
<local:StyleConverter x:Key="StyleConverter"/>
</ResourceDictionary>
</UserControl.Resources>
And they are created by me user controls.
So my goal here is according to binded view model type select user controll (this part works) and then use it as ContentTemplate (this don't work). I'm getting just object namespace nad name for ImeiControl or BilledMsisdnControl in diagram node.
How can I fix this?
According to the documentation you should use style selector
public class NodeStyleSelector : StyleSelector
{
public Style BilledMsisdnStyle { get; set; }
public Style BilledImeiStyle { get; set; }
public override Style SelectStyle(object item, DependencyObject container)
{
if (item is BilledMsisdnViewModel)
return BilledMsisdnStyle;
else if (item is BilledImeiViewModel)
return BilledImeiStyle;
else return base.SelectStyle(item, container);
}
}
Define styles with ContentTemplate. Inside ContentTemplate add your user controls.
<Style x:Key="BilledMsisdnStyle" TargetType="telerik:RadDiagramShape">
<Setter Property="Position" Value="{Binding Position, Mode=TwoWay}" />
<Setter Property="Content" Value="{Binding}" />
<Setter Property="Geometry" Value="{telerik:CommonShape ShapeType=RectangleShape}" />
<Setter Property="ContentTemplate" >
<Setter.Value>
<DataTemplate>
<local:BilledMsisdnControl/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BilledImeiStyle" TargetType="telerik:RadDiagramShape">
<Setter Property="Position" Value="{Binding Position, Mode=TwoWay}" />
<Setter Property="Content" Value="{Binding}" />
<Setter Property="Geometry" Value="{telerik:CommonShape ShapeType=RectangleShape}" />
<Setter Property="ContentTemplate" >
<Setter.Value>
<DataTemplate>
<local:ImeiControl/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Add shape style selector
<styleselectors:NodeStyleSelector x:Key="CustomShapeStyleSelector"
BilledMsisdnStyle="{StaticResource BilledMsisdnStyle}"
BilledImeiStyle="{StaticResource BilledImeiStyle}" />
Use ShapeStyleSelector for your RadDiagram.
<telerik:RadDiagram x:Name="Diagram"
ShapeStyleSelector="{StaticResource CustomShapeStyleSelecto}"
GraphSource="{Binding GraphSource}"/>

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}}"/>

stackpanel visibility based on label content not working

I have a stack panel that I want to make visible based on a label's content. Just not sure why it isnt working for me. What's highlighted in bold is what I want to hide. Any suggestion?
<StackPanel Orientation="Horizontal">
<Label Nane="lblCarrier" Content="{Binding Path=Carrier}" />
**<StackPanel Orientation="Horizontal">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding Content, ElementName=lblCarrier}" Value="">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Label x:Name="lblCarrierGrade" Content="Grade Carrier:" />
<TextBox x:Name="txtCarrierGrade1" />
<TextBox x:Name="txtCarrierGrade2" />
</StackPanel>**
It could be that the Content is null rather than String.Empty.
You could try using TargetNullValue
<DataTrigger Binding="{Binding Content, ElementName=lblCarrier,TargetNullValue=''}" Value="">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
Why not using a converter? Add a class file to you project like this:
class VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return string.IsNullOrEmpty(value as string) ? Visibility.Hidden : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
In your Window definition add this:
xmlns:myNamespace="clr-namespace:[YourProjectName]"
Then somewhere in the resources add this
<myNamespace:VisibilityConverter x:Key="myConverter"/>
Now you can use it:
<Style TargetType="StackPanel">
<Setter Property="Visibility"
Value="{Binding Content, ElementName=lblCarrier,
Converter = {StaticResources myConverter}}"/>

WPF Resource Font Size - Relative to Another Resource

My question is very similar to Wpf custom control template - relative font size ... but I'm trying to set the font size in one resource relative to that of another resource. I implemented the solution posted by Thomas, but I can't figure out how to make the Relative source point to another resource.
<my:MathConverter x:Key="MathConverter" />
<Style x:Key="propertyText">
<Setter Property="Control.Foreground" Value="Gray" />
<Setter Property="Control.FontSize" Value="12" />
<Setter Property="Control.Padding" Value="10,2,2,2" />
</Style>
<Style x:Key="headerText">
<!-- I want this to be the same as propertyText +2 -->
<Setter Property="Control.FontSize" Value="FontSize="{Binding
RelativeSource={RelativeSource AncestorType={x:Type Window}},
Path=FontSize,
Converter={StaticResource MathConverter},
ConverterParameter=2}" />
</Style>
Here is the line I'm having trouble with. I want it to point to propertyText instead:
RelativeSource={RelativeSource AncestorType={x:Type Window}},
For completeness, here is the code for the converter :
public class MathConverter : IValueConverter
{
public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
{
return (double)value + double.Parse( parameter.ToString() );
}
public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
{
return null;
}
}
Based on Markus Hütter's reply. Here is the XAML for the solution:
<system:Double x:Key="baseFontSize">12</system:Double>
<my:MathConverter x:Key="MathConverter" />
<Style x:Key="propertyText">
<Setter Property="Control.Foreground" Value="Gray" />
<Setter Property="Control.FontSize" Value="{StaticResource ResourceKey=baseFontSize}" />
<Setter Property="Control.Padding" Value="10,2,2,2" />
</Style>
<Style x:Key="headerText">
<!-- I want this to be the same as propertyText +2 -->
<Setter Property="Control.FontSize"
Value="{Binding Source={StaticResource ResourceKey=baseFontSize},
Converter={StaticResource MathConverter},
ConverterParameter=2}" />
</Style>
easiest would be:
create a resource
<system:Double x:Key="propertyTextFontSize">12</system:Double>
and use a StaticReference in the setters both pointing to this resource but the second one with the binding and converter.

Resources