XAML - Converter within Style - c#

I would like to include a Converter for TextBlock' Text property within a Style. I have the following code:
<Page.Resources>
<converters:StringFormatConverter x:Key="StringFormatConverter" />
<Style x:Key="StringFormatStyle" TargetType="TextBlock">
<Setter Property="Text">
<Setter.Value>
<Binding>
<Binding.Converter>
<converters:StringFormatConverter />
</Binding.Converter>
</Binding>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
...
<TextBlock Text="some text" Style="{StaticResource StringFormatStyle}" />
<TextBlock Text="{Binding AppName}" Style="{StaticResource StringFormatStyle}" />
The problem is that the Convert method of my Converter isn't being called. The style is applied (100% sure of that).
What could be the problem of the converter not being applied to the TextBlock' Text property?
Long story short: I have several double properties in my ViewModel and I would like to display them formatted using a Converter.
PS: My StringFormatConverter looks like this:
public class StringFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
{
return null;
}
if (parameter == null)
{
return value;
}
return string.Format((string)parameter, value);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}

Well, you can't.
In style you set text to some invalid binding with only a converter. And later you set it to static text.
If you want to add specific handling to a value and want to avoid code duplicates, use attached variable.
Also note that you're creating two converters actually. One in style and one in resources. So just reference the one from resources, don't need to create it in style again.
Here's attached property sample.
public class TextConv: DependencyObject
{
public static readonly DependencyProperty TextProperty =
DependencyProperty.RegisterAttached(
"Text",
typeof(string),
typeof(TextConv),
new PropertyMetadata(null, OnValueChanged)
);
public static void SetText(UIElement element, string value)
{
element.SetValue(TextProperty, value);
}
public static string GetText(UIElement element)
{
return (string)element.GetValue(TextProperty);
}
private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
obj.SetValue(TextBlock.TextProperty, args.NewValue.ToString() + "Hello!");
}
}
And in xaml:
<TextBlock local:TextConv.Text="some text" />
It might not work out of the box but you get the general idea.

If you set the Text property explicitly, that will override the property setter in the style.
Use your converter in the Binding instead of the Style.
Text="{Binding AppName, Converter={StaticResource StringFormatConverterKey}}"

Related

WPF Treeview use property value as Binding Path

I'm trying to create a Treeview using an ObservableCollection of a custom class called MachineComponentFault, which includes a string property called FaultText, and I'd like to make the text localized.
I'm using WPF Runtime Localization from Codeproject to localize texts at runtime, and it usually works as follows:
<TextBlock Text="{Binding Path=NameInResources, Source={StaticResource Resources}}"/>
The problem is that I can't seem to figure out how to set the value of the property to the path, so that it can retrieve the translation. This is what I managed thus far:
<TreeView Name="myTreeView" VirtualizingPanel.IsVirtualizing="True" ItemsSource="{Binding Faults}">
<TreeView.Resources>
<DataTemplate DataType="{x:Type MassComponents:MachineComponentFault}">
<StackPanel Orientation="Horizontal" >
<TextBlock Name="Text1" Text="{Binding FaultText}"/>
<TextBlock Name="Text2" Text="{Binding Path=FLT_PTC_1, Source={StaticResource Resources}}"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
Essentially Text1 shows FLT_PTC_1 at Runtime, while Text2 shows "Motor Overheat", which is the value of FLT_PTC_1 in Resources.resx (which can be translated). The issue is that I can't seem to be able to do what Text2 does using FaultText Property.
Is there a way to do it?
EDIT:
Solved it using mm8 solution, while maintaining the WPF Runtime Localization. The solution isn't pretty at all, since it consists in creating a Binding on a dummy class and then retrieving the binding value as a string, which seems a bit convoluted, but it's the best solution I could find.
public class ResourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string resourceName = value as string;
if (!string.IsNullOrEmpty(resourceName)) //look up the resource here:
{
Binding b = new Binding(resourceName); //Create Binding using as Path the value of FaultText
b.Source = CultureResources.ResourceProvider; //Get the resources from WPF Runtime Localization ObjectDataProvider
BindingOperations.SetBinding(_dummy, Dummy.ValueProperty, b); //Set the Binding to the dummy class instance
return _dummy.GetValue(Dummy.ValueProperty); //Retrieve the value of the Binding from the dummy class instance and return it
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
//Initialize Dummy class
private static readonly Dummy _dummy = new Dummy();
//Create a dummy class that accepts the Binding
private class Dummy : DependencyObject
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(Dummy), new UIPropertyMetadata(null));
}
}
XAML same as mm8 proposed.
You could bind to the FaultText property and use a converter to look up the resource. Something like this:
public class ResourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string resourceName = value as string;
if (!string.IsNullOrEmpty(resourceName)) //look up the resource here:
return Resource1.ResourceManager.GetString(resourceName);
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML:
<TextBlock Name="Text2">
<TextBlock.Text>
<Binding Path="FaultText">
<Binding.Converter>
<local:ResourceConverter />
</Binding.Converter>
</Binding>
</TextBlock.Text>
</TextBlock>

WPF TemplateBinding does not work for every property

I have created custom control, but TemplateBinding does not work for every property?
Template Binding does start working if I use dummy Converter that just forwards original values.
Simplified example that has the same issue:
<Style TargetType="{x:Type controls:ElipticProgressBar}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:ElipticProgressBar}">
<ControlTemplate.Resources>
<converters:DebugConverter x:Key="DebugConverter"/>
</ControlTemplate.Resources>
<StackPanel>
<!--BarColor works always-->
<!--BarTickness works-->
<Label Background="{TemplateBinding BarColor}" Content="{TemplateBinding BarTickness}"/>
<!--BarTickness does not works-->
<TextBlock Background="{TemplateBinding BarColor}" Text="{TemplateBinding BarTickness}"/>
<!--BarTickness works-->
<TextBlock Background="{TemplateBinding BarColor}" Text="{Binding BarTickness, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"/>
<!--BarTickness works-->
<TextBlock Background="{TemplateBinding BarColor}" Text="{TemplateBinding BarTickness, Converter={StaticResource DebugConverter}}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
back code:
public class ElipticProgressBar : Control
{
static ElipticProgressBar()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ElipticProgressBar), new FrameworkPropertyMetadata(typeof(ElipticProgressBar)));
}
public static readonly DependencyProperty BarTicknessProperty = DependencyProperty.Register(
"BarTickness", typeof(int), typeof(ElipticProgressBar), new FrameworkPropertyMetadata(default(int)));
public int BarTickness
{
get { return (int)GetValue(BarTicknessProperty); }
set { SetValue(BarTicknessProperty, value); }
}
public static readonly DependencyProperty BarColorProperty = DependencyProperty.Register(
"BarColor", typeof(Brush), typeof(ElipticProgressBar), new FrameworkPropertyMetadata(default(Brush)));
public Brush BarColor
{
get { return (Brush)GetValue(BarColorProperty); }
set { SetValue(BarColorProperty, value); }
}
}
usage:
controls:ElipticProgressBar BarTickness="30" BarColor="Orange"
DebugConverter:
public class DebugConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
{TemplateBinding} is an optimized version of a binding that has some limitations.
In this case you need to change the type of the BarThickness property to string to be able to bind it directly to the Text property of a TextBlock using a {TemplateBinding}.
The docs state that a {TemplateBinding} is
an optimized form of a Binding for template scenarios, analogous to a Binding constructed with {Binding RelativeSource={RelativeSource TemplatedParent}}
with some limitations - most importantly that it works only within the visual tree!
What that means for your specific case: while you cannot use
<TextBlock Text="{TemplateBinding BarTickness}" />
you can (and have to) use
<TextBlock Text="{Binding BarTickness, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}" />
because TextBlock.Text property is of type string while the BarTickness is not.
{TemplateBinding} does not do that kind of converting; that's why you need to use the more generic {Binding} or a converter, whose both methods
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
both convert the actual value to an object.

C# WPF Binding Path Data with Converter

i have a problem converting a string to a icon. The icon Geometry is in a ResourceDictionary. The ValueConverter is not called (i tried to debug in the Convert Method of the Converter). Here is my code:
xaml:
<Window.Resources>
<local:StatusToPathDataConverter x:Key="PathConverter"/>
</Window.Resources>
<Grid>
<Path Width="20"
Height="20"
Stretch="Uniform"
Fill="Black"
Data="{Binding Path=Status,
UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource PathConverter}}"/>
</Grid>
cs:
public partial class MainWindow :Window {
public MainWindow() {
InitializeComponent();
}
public string Status
{
get { return (string)GetValue(StatusProperty); }
set { SetValue(StatusProperty, value); }
}
public static readonly DependencyProperty StatusProperty =
DependencyProperty.Register("Status", typeof(string), typeof(MainWindow));
}
public class StatusToPathDataConverter :IValueConverter {
private static ResourceDictionary iconDictionary;
public ResourceDictionary IconDictionary
{
get
{
if(iconDictionary == null) {
iconDictionary = new ResourceDictionary();
iconDictionary.Source = new Uri("/WPFBindingTest;component/Resources/IconDictionary.xaml", UriKind.RelativeOrAbsolute);
}
return iconDictionary;
}
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
var status = (string)value;
if(statinStatus == null)
return null;
switch(status.ToLower()) {
case "test":
return IconDictionary["TestIcon"];
// ...
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
You're not binding to anything. You need to tell the Binding to go to the Window to find the Status property. The recommended way to do that is to use RelativeSource, as below:
<Path
Width="20"
Height="20"
Stretch="Uniform"
Fill="Black"
Data="{Binding Path=Status,
RelativeSource={RelativeSource AncestorType=Window},
Converter={StaticResource PathConverter}}"
/>
As #Clemens notes, UpdateSourceTrigger=PropertyChanged doesn't make any sense on this binding and should not be there. That attribute tells the Binding when it should update the binding's source property. The source property is Window.Status, in this case.
However, the Path.Data property does not update the property it's bound to. A Path displays a Geometry; it doesn't edit a Geometry. UpdateSourceTrigger exists for control properties that update viewmodel properties, like TextBox.Text. That's the most common use for UpdateSourceTrigger=PropertyChanged: By default TextBox.Text updates the source property when the TextBox loses focus, but sometimes you want it to update on each keystroke.
Set the DataContext of the window to itself for the binding to work and the Convert method of the converter to get called:
public MainWindow() {
InitializeComponent();
DataContext = this;
}
If the binding to the source property fails the converter will never be invoked.

Xamarin Forms XAML - Boolean attribute set from XAML

I don't understand something about how to set object attribute for xaml about boolean..
I have a MainPage.xaml like this where I set ProportionalSize to true:
<ContentPage.Resources>
<ResourceDictionary>
<converter:BooleanConverter x:Key="Boolean"/>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<!-- Background during loading of start -->
<AbsoluteLayout>
<local:CustomImage Source="{extension:ImageResource HomeBG.png}"
ProportionalWidth="100" ProportionalHeight="100" ProportionalSize="{True, Converter={StaticResource Boolean}}"
AbsoluteLayout.LayoutBounds="0.5, 0.5, 1, 1"
AbsoluteLayout.LayoutFlags="All"/>
</AbsoluteLayout>
</ContentPage.Content>
I use a customImage for some reason, this is the class
public class CustomImage : Image
{
private bool _ProportionalSize;
public bool ProportionalSize
{
get { return this._ProportionalSize; }
set
{
this._ProportionalSize = value;
TrySize();
}
}
}
Because neither true nor True works, I made a BooleanConverter
public class BooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool)value;
}
}
However, it still doesn't work...
Additional information: Position 19:75. MarkupExtension not found for true
ProportionalSize="{True, Converter={StaticResource Boolean}}"
Does I'm doing something wrong?
Just set the value, you don't need to use markup extension syntax (those "{}" brackets) or a converter:
ProportionalSize="True"
If you're not actually binding to a value that will change... Don't use a converter or property. It looks like you just want to set true one time in XAML. You can use the x:Arguments attribute for that.
<x:Arguments>
<x:Boolean>True</x:Boolean>
</x:Arguments>
Bigger example from a DataTrigger.
Usecase - The grid has a different value binded to IsVisible, but we want to override that if the administrator is logged in. So in addition to the regular binding, we can put in a datatrigger that uses the x:Arguments of x:Boolean to set it to true. - Without a converter... without a property.
<Grid.Triggers>
<DataTrigger Binding="{Binding IsAdmin}"
TargetType="{x:Type Grid}"
Value="True">
<Setter Property="IsVisible">
<Setter.Value>
<x:Arguments>
<x:Boolean>True</x:Boolean>
</x:Arguments>
</Setter.Value>
</Setter>
</DataTrigger>
</Grid.Triggers>
Try to use BindableProperty:
public static readonly BindableProperty ProportionalSizeProperty =
BindableProperty.Create(nameof(ProportionalSize),
typeof(bool),
typeof(CustomImage),
default(bool),
propertyChanged: OnProportionalSizeChanged);
public bool ProportionalSize
{
get { return (bool)GetValue(ProportionalSizeProperty); }
set { SetValue(ProportionalSizeProperty, value); }
}
private static void OnProportionalSizeChanged(BindableObject bindable, object oldValue, object newValue)
{
var customImage = bindable as CustomImage;
if (customImage != null)
{
customImage.TrySize();
}
}

How to pass a static value to IValueConverter in XAML

I would like to use static texts fetched from a web service in my WP7 app. Each text has a Name (the indetifier) and a Content property.
For example a text could look like this:
Name = "M43";
Content = "This is the text to be shown";
I would then like to pass the Name (i.e. the identifier) of the text to an IValueConverter, which would then look up the the Name and return the text.
I figured the converter to look something like this:
public class StaticTextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
return App.StaticTexts.Items.SingleOrDefault(t => t.Name.Equals(value)).Content;
}
return null;
}
}
Then in the XAML:
<phone:PhoneApplicationPage.Resources>
<Helpers:StaticTextConverter x:Name="StaticTextConverter" />
</phone:PhoneApplicationPage.Resources>
...
<TextBlock Text="{Binding 'M43', Converter={StaticResource StaticTextConverter}}"/>
However, this does not seem to work and I am not sure that I pass in the value to the converter correctly.
Does anyone have some suggestions?
I finally found the answer. The answer was a mix between that of #Shawn Kendrot and another question I asked here: IValueConverter not getting invoked in some scenarios
To summarize the solution for using the IValueConverter I have to bind my control in the following manor:
<phone:PhoneApplicationPage.Resources>
<Helpers:StaticTextConverter x:Name="TextConverter" />
</phone:PhoneApplicationPage.Resources>
<TextBlock Text="{Binding Converter={StaticResource TextConverter}, ConverterParameter=M62}" />
Since the ID of the text is passed in with the converter parameter, the converter looks almost the same:
public class StaticTextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter != null && parameter is string)
{
return App.StaticTexts.Items.SingleOrDefault(t => t.Name.Equals(parameter)).Content;
}
return null;
}
}
However, as it turns out, the binding and thus the converter is not invoked if it does not have a DataContext. To solve this, the DataContext property of the control just has to be set to something arbitrary:
<TextBlock DataContext="arbitrary"
Text="{Binding Converter={StaticResource TextConverter}, ConverterParameter=M62}" />
And then everything works as intended!
The problem lies in your binding. It will check the DataContext, and on this object, it will try to evaluate the properties M62 and ValueboxConsent on that object.
You might want to add static keys somewhere in your application where you can bind to:
<TextBlock Text="{Binding Source="{x:Static M62.ValueboxConsent}", Converter={StaticResource StaticTextConverter}}" />
Where M62 is a static class where your keys are located.. like so:
public static class M62
{
public static string ValueboxConsent
{
get { return "myValueBoxConsentKey"; }
}
}
If you want to use a value converter, you'll need to pass the string to the parameter of value converter
Xaml:
<TextBlock Text="{Binding Converter={StaticResource StaticTextConverter}, ConverterParameter=M43}"/>
Converter:
public class StaticTextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter != null)
{
return App.StaticTexts.Items.SingleOrDefault(t => t.Name.Equals(parameter)).Content;
}
return null;
}
}
xmlns:prop="clr-namespace:MyProj.Properties;assembly=namespace:MyProj"
<TextBlock Text="{Binding Source={x:Static prop:Resources.MyString}, Converter={StaticResource StringToUpperCaseConverter}}" />

Categories

Resources