WPF Treeview use property value as Binding Path - c#

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>

Related

Accessing UserControl Resource Dictionary from code

I' am trying to read/load a UserControl Resources from my Home.xaml to a ResourceDictionary object so I can dynamicly display it inside content control and can't manage to find a solution.
I dont want to read it from my App.xaml, just the Home.xaml.
In my View folder I have Home.xaml:
<UserControl.Resources>
<converters:CheckBoxConverter x:Key="CheckBoxConv"></converters:CheckBoxConverter>
<DataTemplate x:Key="Punomoc">
<CheckBox Margin="10" Content="Izradi punomoć"
IsChecked="{Binding UnosIOS.Punomoc}">
</CheckBox>
</DataTemplate>
<DataTemplate x:Key="Naknada">
<CheckBox Margin="10,0" Content="Naplata naknade"
IsChecked="{Binding UnosIOS.NaplataNaknade}">
</CheckBox>
</DataTemplate>
</UserControl.Resources>
<Grid>
...
```
...
```
In my ConvertersFolder I have:
public class CheckBoxConverter : IValueConverter
{
private static readonly ResourceDictionary ControlResourceDictionary;
static CheckBoxConverter()
{
ControlResourceDictionary = new ResourceDictionary
{
Source = new Uri("pack://application:,,,/View/Home.xaml", UriKind.Absolute)
};
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
DataTemplate dataTemplate =ControlResourceDictionary["Punomoc"] as DataTemplate;
return dataTemplate;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
But its returning error:
''The invocation of the constructor on type 'IzdavanjeIOS.Converters.CheckBoxConverter' that matches the specified binding constraints threw an exception.' Line number '23' and line position '10'.'
Also tried with URI string : MyAppName/View/Home.xaml
I have setup MVVM and dont want to access resources from code-behind unless its the only choice, it seems to me that it should be possible like above, but maybe I am giving the wrong URI?

SelectedItem binding on ComboBox not showing selected value

I'm trying to build a settings page to allow the user to choice which action to execute on item swipe, like the Outlook app.
To do this I created an enum containing the available actions, and I'm binding it to a ComboBox.
Everything works, the user can choose the action and his choice is saved correctly. The problem is that the ComboBox doesn't show the selected item when I navigate to the page, it shows it only after selection.
This means that if user changes selection then the ComboBox is updated, but the selected item is shown as blank upon navigation.
Here's my code:
(XAML)
<ComboBox x:Uid="LeftActionComboBox"
Grid.Row="0"
HorizontalAlignment="Stretch"
SelectedItem="{Binding LeftSwipeActionType, Mode=TwoWay, Converter={StaticResource StringToSwipeActionTypesConverter}}"
ItemsSource="{Binding LeftSwipeActionType, Converter={StaticResource EnumToStringListConverter}}"/>
(VM Property)
public SwipeActionTypes LeftSwipeActionType
{
get { return _settings.LeftSwipeActionTypeProperty; }
set
{
_settings.LeftSwipeActionTypeProperty = value;
// RaisePropertyChanged causes a StackOverflow, but not using it is not the problem since the ComboBox is empty only before set
}
}
(Converter StringToSwipeActionTypesConverter, localization-ready)
// Returns localized string value for the Enum
public object Convert(object value, Type targetType, object parameter, string language)
{
var enumValue = (SwipeActionTypes) value;
switch (enumValue)
{
case SwipeActionTypes.Copy:
return App.ResourceLoader.GetString("CopySwipeActionName");
case SwipeActionTypes.Delete:
return App.ResourceLoader.GetString("DeleteSwipeActionName");
default:
throw new ArgumentOutOfRangeException();
}
}
// Parses the localized string into the enum value
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
var stringValue = (string) value;
if (stringValue.Equals(App.ResourceLoader.GetString("CopySwipeActionName")))
{
return SwipeActionTypes.Copy;
}
if (stringValue.Equals(App.ResourceLoader.GetString("DeleteSwipeActionName")))
{
return SwipeActionTypes.Delete;
}
return null;
}
(Converter EnumToStringListConverter)
public object Convert(object value, Type targetType, object parameter, string language)
{
var valueType = value.GetType();
return Enum.GetNames(valueType).ToList();
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return value;
}
Any idea on why this is failing?
The reason you are getting a StackOverflow exception is because every time you change LeftSwipeActionType property you are changing the ItemsSource of the ComboBox which changes the SelectedItem which fires INotifyPropertyChanged which changes the ItemsSource and so on and so on.
Once you stop using the same property for ItemsSource and SelectedItem then the correct initial selection will be set.
Rather than use a converter to create your ItemsSource you should just create is in your ViewModel
public MyViewModel(type enumType)
{
SourceForItems = Enum.GetValues(enumType);
}
public IEnumerable SourceForItems { get; private set; }
First of all, here is whats wrong with your approach:
You are binding your ItemsSource to the same property as the SelectedItem, even tough you are using a converter this can cause an infinite update circle - and you don't want that.
Generating the same static list of elements over and over again seems a bit wasteful. Instead of passing an instance of a type, lets just pass the type itself to the converter:
EnumToMembersConverter.cs
public class EnumToMembersConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return Enum.GetValues((Type)value).ToList();
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return DependencyProperty.UnsetValue;
}
}
XAML
ItemsSource="{Binding Source={x:Type whateverNamespace:SwipeActionTypes}, Converter={StaticResource EnumToMembersConverter}}"
This will give you all Values of SwipeActionTypes, therefore you can bind it directly, without converting back again.
SelectedItem="{Binding LeftSwipeActionType, Mode=TwoWay}"
There is nothing wrong with using a ComboBox for types other than string, so lets make this your base for further steps:
<ComboBox x:Uid="LeftActionComboBox"
Grid.Row="0"
HorizontalAlignment="Stretch"
SelectedItem="{Binding LeftSwipeActionType, Mode=TwoWay}"
ItemsSource="{Binding Source={x:Type whateverNamespace:SwipeActionTypes}, Converter={StaticResource EnumToMembersConverter}}"/>
The reason you wrote all those converts is probably because the ComboBox showed strange values instead of readable strings. No worries, we already have your converter, you just need to invert it (Convert SwipeActionTypes to String) and apply it to a TextBox:
<ComboBox x:Uid="LeftActionComboBox"
Grid.Row="0"
HorizontalAlignment="Stretch"
SelectedItem="{Binding LeftSwipeActionType, Mode=TwoWay}"
ItemsSource="{Binding Source={x:Type whateverNamespace:SwipeActionTypes}, Converter={StaticResource EnumToMembersConverter}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=., Converter = {StaticResource SwipeActionTypesStringConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Note, I didn't run this code so you might need to adjust the used namespaces accordingly

Binding inside of a binding

Ok I have a Observable collection containing string defined like so.
public ObservableCollection<string> OCGroundType { get; set; }
This collection is having resources key so what I'm trying to do is with this code
<ListBox HorizontalAlignment="Left" Height="110" Margin="156,23,0,0" VerticalAlignment="Top" Width="314" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=OCGroundType}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=, Source={StaticResource Resources}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So what I'm trying to do is give the path the value of the itemsource is that possible?
Edit
That's what the staticresource is 'binded' to , a resourceDictionary
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cultures="clr-namespace:Marcam.Cultures"
xmlns:properties="clr-namespace:Marcam.Properties">
<ObjectDataProvider x:Key="Resources" ObjectType="{x:Type cultures:CultureResources}" MethodName="GetResourceInstance"/>
</ResourceDictionary>
What the ObjectType is 'binded' to is a method who return the current Resources.resx, If I've understanded well how it's work because I've based this code on this WPF Localization
It's not possible, because a Binding is not a DependencyObject, so its properties cannot be bound.
However, you could achieve the desired result by using a converter that fetches the appropriate value from the resources.
EDIT: first, you need a converter like this:
public class ResourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string key = (string)value;
return Properties.Resources.ResourceManager.GetObject(key, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Then, declare an instance of this converter in the XAML resources:
<local:ResourceConverter x:Key="resourceConverter" />
And finally, just use this converter in your binding:
<Label Content="{Binding Path=., Converter={StaticResource resourceConverter}}"/>
Assuming Resources are mere string values declared under resources section of Window or UserControl, you can achieve that using IMultiValueConverter.
First of all you need to pass resource key and second ResourceDictionary of Window/UserControl wherever your resources are defined.
XAML will look like this:
<Label>
<Label.Content>
<MultiBinding Converter="{StaticResource ResourceToValueConverter}">
<Binding/>
<Binding Path="Resources" RelativeSource="{RelativeSource
Mode=FindAncestor, AncestorType=Window}"/>
</MultiBinding>
</Label.Content>
</Label>
Ofcourse you need to add instance of ResourceToValueConverter in XAML.
Second here goes your converter code:
public class ResourceToValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return ((ResourceDictionary)values[1])[values[0]];
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
UPDATE
As you edited the question to include that resource is ObjectDataProvider, you can modify converter code.
You have ObjectDataProvider in your converter which you can get and call it's method to get the resource file(.resx) file. Get string value from the resource file and return it.
var provider = ((ResourceDictionary)values[1])["Resources"] as ObjectDataProvider;
var output = provider.Data;
return // Get string from resource file and return it.

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

Is it possible to define converter for binding inline (without resource)?

Is it possible to define type converter for binding inline (without resource)?
Something like this:
<Button Visibility="{Binding ElementName=checkBox, Path=IsChecked, Converter={new BooleanToVisibilityConverter}" />
You can create and expose your converter through a custom MarkupExtension which will give you the inline declaration you're looking for:
public class BooleanToVisibilityConverterExtension : MarkupExtension, IValueConverter
{
private BooleanToVisibilityConverter converter;
public BooleanToVisibilityCoverterExtension() : base()
{
this.converter = new BooleanToVisibilityConverter();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return this.converter.Convert(value, targetType, parameter, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return this.converter.ConvertBack(value, targetType, parameter, culture);
}
}
Now you can use the MarkupExtension inline to create a new converter:
<Button Visibility="{Binding Converter={local:BooleanToVisibilityConverter} ...}" .. />
It's not possible using binding syntax. But it is possible using element syntax:
<Button.Visibility>
<Binding ElementName="checkBox" Path=IsChecked>
<Binding.Converter>
<BooleanToVisibilityConverter />
</Binding.Converter>
</Binding>
</Button.Visibility>
But why would you want to do this? It would mean every binding instance will create a new converter. That's not efficient from a memory point of view.
You could do something like this. This should work.
Pseudocode:
public static class ConverterHost
{
public static readonly MyCoolConverter converter = new MyCoolConverter ();
}
and in the XAML, somethign like this:
{Binding Converter={x:Static conv:ConverterHost.converter }}
Hope this helps.
Regards.
No, you have to define the converter as a resource somewhere (window, usercontrol, etc.) before you can use it in a binding.

Categories

Resources