Convert Enum to string inside TextBlock text - wpf

I have simple Enum:
public enum StatusMessage
{
Cancel,
Done,
[Description("In process...")]
InProcess,
[Description("We have delay...")]
Delay,
Waiting
}
And GridViewColumn:
My property:
StatusMessage StatusMsg;
XAML:
<GridViewColumn Width="180" Header="Status" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding StatusMsg}" Foreground="{Binding StatusMsg,Converter={my:StatusMessageToColorConverter}}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
And i have this EnumToStringConverter:
public class EnumToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string EnumString;
try
{
EnumString = Enum.GetName((value.GetType()), value);
return EnumString;
}
catch
{
return string.Empty;
}
}
// No need to implement converting back on a one-way binding
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Now i want to use this Convertor inside my TextBlock :
<TextBlock Text="{Binding StatusMsg, Converter={my:EnumToStringConverter}}" Foreground="{Binding StatusMsg,Converter={my:StatusMessageToColorConverter}}" />
So the problem is that i have this error:
'my:EnumToStringConverter' is used like a markup extension but does
not derive from MarkupExtension.
What is this MarkupExtension ?

You need to declare an instance of the EnumToStringConverter in XAML. It can be a local resource or declared in app.xaml to make it accessible everywhere.
<Window.Resources>
<my:EnumToStringConverter x:Key="DefaultEnumToStringConverter"/>
</Window.Resources>
Then use it like this:
Text="{Binding StatusMsg, Converter={StaticResource DefaultEnumToStringConverter}}"
Note the word "StaticResource" in the converter. That is the markup extension. This one tells WPF to go find the static resource with the key "DefaultEnumToStringConverter". WPF will search up the visual tree of the element looking for a resource with that key. If one isn't found it will check at the application level in app.xaml.
MarkupExtensions are the things at the beginning of an attribute enclosed in the {}, "x", "binding", "static", etc. They are what give WPF the ability to resolve the text attribute in to a useful object instance. You can create your own MarkupExtensions to do some pretty cool things.
In your particular example it is complaining because it is looking for a markup extension named "my", from the inner Converter={my:EnumToStringConverter}.

Related

WPF; Convert simple string into my TextBlock Text failed and due to Cannot find resource named

So i have this TextBlock:
<TextBlock
Name="tbVersion"
Text="{Binding Converter={StaticResource TextConverter}}"/>
Converter:
public class TextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return "bla bla";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And i got this error when try to run my application:
{"Cannot find resource named 'TextConverter'. Resource names are case
sensitive."}
Although there is no compiler errors at all and i declare this:
<Window.Resources>
<Convertors:TextConverter x:Key="TextConverter"/>
</Window.Resources>
Ok so i try to take another Converter that works from another TextBlobk and still got this error that cannot find resource although this exist (and works as i mentioned...) so maybe because my TextBlobk is inside DataTemplate :
<Controls:MetroWindow.TitleTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock
Name="tbVersion"
Text="{Binding Converter={StaticResource TextConverter}}"/>
</StackPanel>
</DataTemplate>
</Controls:MetroWindow.TitleTemplate>
As you can see from the image, i used exactly your code, and is working just fine.
So you may have added the converter to Window.Resources but you are using it in a different ResourceDictionary?
If you want to make them globally usable you should add them to the App.xaml, or programmatically to the Application.Current.ResourceDictionary
EDIT:
Try adding the converter to the DataTemplate.Resources

ComboBox binding works only one way with Color property

I'm implementing an ObservableCollection <-> DataGrid binding.
My class consists of a System.Windows.Media.Color field (named 'Color') and several strings.
My implementation in WPF of the Color column:
<DataGridTemplateColumn Header="Color" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Source={StaticResource ColorProperties}}"
SelectedItem="{Binding Path=Color, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Style="{StaticResource ComboBoxFlatStyle}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="1" Orientation="Horizontal">
<Rectangle Fill="{Binding}" Height="10" Width="10" Margin="2"/>
<TextBlock Text="{Binding}" Margin="2,0,0,0"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
And ColorProperties:
<ObjectDataProvider x:Key="ColorProperties" ObjectType="{x:Type color:ColorHelper}"
MethodName="GetColorNames"/>
The ColorHelper class (I'll try to find the OP for credit):
public static class ColorHelper
{
public static IEnumerable<string> GetColorNames()
{
foreach (PropertyInfo p
in typeof(Colors).GetProperties(
BindingFlags.Public | BindingFlags.Static))
{
yield return p.Name;
}
}
}
This works just fine, I can see the combo-box with all the colors:
Now I added a few objects to the ObservableCollection, and their respective fields are populated in the DataGrid. All except the Color.
The default Color is Black, but when I run the application, the ComboBox stays empty.
When I select a color from the ComboBox, the ObservableCollection changes accordingly. However if I change the object itself, the ComboBox stays with its original value.
For troubleshooting, I added
<DataGridTextColumn Header="Color" Binding="{Binding Color, UpdateSourceTrigger=LostFocus}"
Width="100"/>
This column gets populated with the string representation of the color, and when I change the value from either side, it changes accordingly.
This fact suggests that there's something missing in the ComboBox implementation.
Am I missing some sort of translation method? Seems weird since it actually works in one way (from ComboBox to object).
I've searched everywhere for a solution, without success.
EDIT:
I implemented Color<->String converter, still doesn't work..
public class StringToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Color content = (Color)value;
return content.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string content = (string)value;
return (Color)ColorConverter.ConvertFromString(content);
}
}
WPF change:
<ComboBox ItemsSource="{Binding Source={StaticResource ColorProperties}}"
SelectedItem="{Binding Path=Color, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay,
Converter={StaticResource StringToColorConverter}}"
Style="{StaticResource ComboBoxFlatStyle}">
Any ideas?
Thanks!
The list bound to Combobox is a list of strings, whereas the actual object is not just a string. you need a converter that converts from string to your desired object.
Rest all seems fine, system dosent know how does string Black translates to Color Black
-- one way binding works because, color object knows how to get color from a string, that is why when we set Background="String" it get the corresponding object using FromName
OK i found out what was wrong. It looks like getting the actual name from a color is not that simple. ToString returns the hex string, and not the actual color name.
So I used the answer from http://www.blogs.intuidev.com/post/2010/02/05/ColorHelper.aspx to create the converter.
public class ColorToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Color KnownColor;
Color content = (Color)value;
Type ColorType = typeof(System.Windows.Media.Colors);
PropertyInfo[] ColorsCollection = ColorType.GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (PropertyInfo pi in ColorsCollection)
{
KnownColor = (Color)pi.GetValue(null);
if (KnownColor == content)
return pi.Name;
}
return String.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string content = (string)value;
return (Color)ColorConverter.ConvertFromString(content);
}
}

How to implement a Converter

I have a list of objects that I'm binding to the screen. One of the properties is isPurchased. It is a Boolean type.
I don't have a lot of experience with converters so I'm finding this a little difficult. I have 2 questions.
The 1st question is regarding syntax. I copied this example from here.
public class purchasedConverter : IValueConverter
{
public object Convert(inAppPurchases value, Type targetType, object parameter, string language)
{
return;
}
}
If the isPurchased == true then I'd like to set the background color to my stackpanel to a different color.
I changed object value to inAppPurchases value on the Convert method. However, no matter what I tried I could not get a reference to a Background.
I think I want to return Background="somecolor"
My 2nd question (assuming I can do the 1st part), is I'm using StandardStyles.xaml which comes with the Microsoft WinRT projects So my converter would exist there.
<StackPanel Grid.Column="1" VerticalAlignment="Top"
Background="CornflowerBlue" Orientation="Vertical" Height="130"
Margin="0,0,5,0"/>
However, like I said I've tried this before but I wasn't able to figure out how to add the convert to my .xaml file. Where would I reference the converter? Is it on the StandardStyls.xaml or the main .xaml that I'm viewing?
Any help is appreciated.
Background property of StackPanel is type of Brush (Panel.Background msdn) , so we can return object of type SolidColorBrush from Convert method.
Your converter should look like this:
class PurchasedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// isPurchased is bool so we can cast it to bool
if ((bool)value == true)
return new SolidColorBrush(Colors.Red);
else
return new SolidColorBrush(Colors.Orange);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Next you must create instance of this converter in XAML:
<Window.Resources>
<con:PurchasedConverter x:Key="pCon" />
</Window.Resources>
And now you can use this converter to binding Background property in StackPanel:
<StackPanel VerticalAlignment="Top" Orientation="Vertical" Height="130"
Background="{Binding isPurchased, Converter={StaticResource pCon}}"
Margin="0,0,5,0" >
</StackPanel>

WPF: multiple controls binding to same property

Hello
I'm trying to change several controls' property according to some environment variables and i want to avoid creating a property for each control in the datacontext, so i thought using a converter which sets the property according to control name. Goal is to use one property for all controls:
<Grid.Resources>
<local:NameToStringConverter x:Key="conv" />
</Grid.Resources>
<TextBlock Name="FordPerfect"
Text="{Binding ElementName="FordPerfect" Path=Name, Converter={StaticResource conv}, Mode=OneWay}"/>
<TextBlock Name="Arthur"
Text="{Binding ElementName="Arthur" Path=Name, Converter={StaticResource conv}, Mode=OneWay}"/>
<TextBlock Name="ZaphodBeeblebrox"
Text="{Binding ElementName="ZaphodBeeblebrox" Path=Name, Converter={StaticResource conv}, Mode=OneWay}"/>
and ...
public class NameToStringConverter : IValueConverter
{
public object Convert(
object value, Type targetType,
object parameter, CultureInfo culture)
{
if (MyGlobalEnv.IsFlavor1 && ((string)value).Equals("ZaphodBeeblebrox")) return "42"
if (MyGlobalEnv.IsFlavor2 && ((string)value).Equals("ZaphodBeeblebrox")) return "43"
if (MyGlobalEnv.IsFlavor1 && ((string)value).Equals("Arthur")) return "44"
return "?";
}
public object ConvertBack(
object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotSupportedException("Cannot convert back");
}
}
I'm sure there's a better and more elegant way... Any ideas?
The point of oneway databinding is just to decouple UI (XAML) from code (CS). Here, your code and UI are tied so tightly together that trying to do this through databinding is really not buying you anything. You might simplify things by writing a method that takes the data value and applies it correctly to each control - still tightly coupled (bad) but at least the code is condensed and easy to follow (less bad).
What you should probably do though is not rely on the control name but define a ConverterParameter. See the bottom 1/3 of this article http://www.switchonthecode.com/tutorials/wpf-tutorial-binding-converters
You may bind directly to environment variable in your situation :
<Window xmlns:system="clr-namespace:System;assembly=mscorlib" ...>
<TextBlock Text="{Binding Source={x:Static system:Environment.OSVersion}}"/>

WPF Localized TreeView with HierarchicalDataTemplate

Here's the thing:
I have a simple WPF Windows application, in which I've included a TreeView, which is being constructed with the help of HierarchicalDataTemplate and fed with some hierarchical data.
The hierarchical data structure is made of FakeRec class, which contains child items in a List<FakeRec>. Each item contains a Title string property.
So in my XAML, I have:
...
<HierarchicalDataTemplate ItemsSource="{Binding Items}" DataType="{x:Type local:FakeRec}">
...
<TextBlock Grid.Column="0" Text="{Binding Path=Title}"/>
...
</HierarchicalDataTemplate>
...
This works fine, and in the generated TreeView I see the title of each node.
Now I want to make this whole tree localizable.
I have my resources in FakeDirResources.Resx (in a separate assembly, but that does not matter).
If I do this:
...
<HierarchicalDataTemplate ItemsSource="{Binding Items}" DataType="{x:Type local:FakeRec}">
...
<TextBlock Grid.Column="0" Text="{Binding Path=Title, Source={StaticResource FakeDirResources}}"/>
...
</HierarchicalDataTemplate>
...
My tree is blank (obviously, because in my FakeDirResources.resx file I don't have a resource with key Title, but I need to use the Title of the other binding, resolve it through the resources, and then somehow bind the result to the tree.
Note that if i just place a TextBlock on the window, without relation to the tree or to the HierarchicalDataTemplate, I can bind it without problem to the resources, like so:
<TextBlock Text="{Binding Path=games, Source={StaticResource FakeDirResources}}"/>;
This works great, fetching the string, and if I change the System.Threading.Thread.CurrentThread.CurrentUICulture and refresh my provider, this string gets changed to to the new language.
The question is how do I combine the two? What am I missing?
I guess there has to be some trick (and with my short experience with WPF it's probably not a straight-forward trick).
Cheers!
Alon.
Potentially you could work through this with an IValueConverter:
public class KeyResourceConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
var key = System.Convert.ToString(value);
var lookup = parameter as ResourceManager;
return lookup.GetString(key, culture);
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Used like so:
<TextBlock Text="{Binding Path=Title,
Converter={StaticResource keyResource}
ConverterParameter={x:Static local:FakeDirResources.ResourceManager}}"
/>

Resources