How can I binding an enum with IValueConverter? - wpf

Here is the enum:
public enum BarcodeType
{ AZTEC, CODABAR, CODE128, CODE93, CODE39, DATA_MATRIX, EAN13, EAN8, ITF, MAXICODE, PDF417, QRCODE, RSS14, RSSEXPANDED, UPCA, UPCE, UPC_EAN_EXTENSION }
And I bind the enum to the ComboBox like this:
<Page x:Class="KongGamLung.ToolProperty.BarCodeProperty"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:KongGamLung.ToolProperty"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:Model="clr-namespace:KongGamLung.Models"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="BarCodeProperty">
<Page.Resources>
<ObjectDataProvider x:Key="dataFromEnum" MethodName="GetValues"
ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="Model:BarcodeModel+BarcodeType"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<local:BarcodeTypeConverter x:Key="BarcodeTypeConverter"/>
</Page.Resources>
<ComboBox x:Name="BarcodeTypeCB" ItemsSource="{Binding Source={StaticResource dataFromEnum},Converter={StaticResource BarcodeTypeConverter}}">
</ComboBox>
</Page>
And here is code-behind:
public class BarcodeTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Enum.GetName(value.GetType(), value).ToString().Replace("_", " ");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The code works well while without the code of the IValueConverter.
I use the IValueConverter for I want to replace the character of '_' in enum to ' ' to make it looks better.
I code the IValueConverter as what https://social.msdn.microsoft.com/Forums/vstudio/en-US/43db6b07-f886-4214-8076-5a5ec2360616/valueconverter-that-converts-an-enum-value-to-its-corresponding-string-value?forum=wpf said. But finally, it throws a System.ArgumentException error.
How can I solve it? Would you please help me? Thank you.

Don't use a converter on ItemsSource, it's changes the type of data that you're binding to. If you need to change the appearance of the enum then specify an ItemTemplate instead and use your converter there:
<ComboBox ItemsSource="{Binding Source={StaticResource dataFromEnum}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=., Converter={StaticResource BarcodeTypeConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Personally I'd bind to an intermediate view model class instead that contains both the enum and the text, that makes it much easier down the track to support things like localization (i.e. multiple languages at runtime).

Related

How can I binding data to ComboBox and rewrite items template?

I wanna make a combo box which likes the photoshop to show all the fonts in computer and display it.
I should display the font name, the sample of the font, so I have to rewrite the style of the items.
I wrote the code as this:
<ItemsControl Grid.Column="1" Margin="10,10,0,10">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<ComboBox></ComboBox>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
But visual studio reports an error:
How can I solve it and custom a combo box by my self? Thank you.
A simple sample. Edited according to owner's comment.
XAML
<Window x:Class="WpfApp1.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:WpfApp1"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ComboBox Width="200" VerticalAlignment="Center"
HorizontalContentAlignment="Stretch"
ItemsSource="{x:Static Fonts.SystemFontFamilies}">
<ComboBox.Resources>
<local:GetFamilyName x:Key="getName"/>
</ComboBox.Resources>
<ComboBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<TextBlock Text="{Binding Converter={StaticResource getName}}"/>
<TextBlock Text="Sample" TextAlignment="Right"
FontFamily="{Binding}"/>
</DockPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</Window>
Convertor Code
namespace WpfApp1
{
public class GetName : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value as FontFamily)?.FamilyNames.Values.First();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}

WPF ObjectDataProvider Parameters

I have an ObjectDataProvider for getting a list of enum members:
<ObjectDataProvider x:Key="GetEnumContents" MethodName="GetValues" ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="Data:Status"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
which I then use with:
<ComboBox SelectedItem="{Binding Status, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding Source={StaticResource GetEnumContents}}" />
In the same window, I'd then like to have a combo box for a different enum. How do I pass the enum type from the ComboBox declaration?
I've seen solutions to similar problems something like:
Path="MethodParameters[0]"
but here I don't want to bind the parameter to anything, I just want to hard-code it in the ComboBox declaration.
Any ideas?
ObjectDataProvider doesn't support that kind of functionality, but you can "fake" it with the clever abuse usage of a Binding and an IValueConverter.
First, the IValueConverter:
class EnumConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Enum.GetValues((Type)parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Here is how you use it:
<Window
x:Class="EnumTest.MainWindow"
[...snip...]
xmlns:local="clr-namespace:EnumTest"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Window.Resources>
<local:EnumConverter x:Key="EnumConverter" />
</Window.Resources>
<StackPanel>
<ComboBox ItemsSource="{Binding Converter={StaticResource EnumConverter}, ConverterParameter={x:Type local:MyEnum1}}" />
<ComboBox ItemsSource="{Binding Converter={StaticResource EnumConverter}, ConverterParameter={x:Type local:MyEnum2}}" />
</StackPanel>
</Window>
Some Test Enums:
enum MyEnum1
{
Red,
Green,
Blue,
}
enum MyEnum2
{
Cat,
Dog,
Fish,
Bird,
}
This produces the following output:
This takes advantage of the fact that you can pass an extra parameter to an IValueConverter, which I use to pass the Type of the enumeration to the converter. The converter just calls Enum.GetNames on that argument, and returns the result. The actual Binding will actually be bound to whatever the DataContext of the ComboBox happens to be. The EnumConverter just happily ignores it and operates on the parameter instead.
UPDATE
It works even better by binding directly to the type, skipping the ConverterParameter entirely, like so:
<ComboBox ItemsSource="{Binding Source={x:Type local:MyEnum1}, Converter={StaticResource EnumConverter}}" />
With adjustments to the converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Enum.GetValues((Type)value);
}
Same result with less typing, and easier to understand, code.

How can I add a resource to a silverlight custom control?

I am trying to trim the selected value of a silverlight custom control combobox. I have found that using the IValueConverter class should be the way to go. So I create this in my library
public class StringTrimmer : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.ToString().Trim();
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
this is the xaml for the combobox
<UserControl x:Class="SilverlightControlLibrary.SilverlightComboBoxControl"
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"
mc:Ignorable="d" Height="25" Width="122">
<ComboBox Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120"
Margin="0,0,0,0" Name="ModuleIDFilterCB"
ItemsSource="{Binding Path=Screen.LicenseModuleIDs, Mode=TwoWay}"
DisplayMemberPath="LicenseModuleName"
SelectedItem="{Binding Screen.bufferProp, Converter={StaticResource StringTrimmer},Mode=TwoWay}"
/>
</UserControl>
Except that the resource "StringTrimmer" for the SelectedItem can't be resolved. I tried adding this reference to the xaml and it still didnt work.
xmlns:c="clr-namespace:SilverlightControlLibrary"
EDIT: I also tried this
xmlns:custom="clr-namespace:SilverlightControlLibrary"
along with
SelectedItem="{Binding Screen.bufferProp, Converter= {StaticResource custom:StringTrimmer}, Mode=TwoWay}"
to no avail..
This http://msdn.microsoft.com/en-us/library/cc189061%28v=vs.95%29.aspx is what microsof has to say about XAML namespaces
and this http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.convert.aspx is for the IValueConverter
Where am I going wrong?
You have to add static resource (UserControl.Resources section) to reference your converter e.g.:
<UserControl
x:Class="SilverlightControlLibrary.SilverlightComboBoxControl"
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"
mc:Ignorable="d"
Height="25"
Width="122"
xmlns:custom="clr-namespace:SilverlightControlLibrary">
<UserControl.Resources>
<custom:StringTrimmer x:Key="StringTrimmer" />
</UserControl.Resources>
<ComboBox Height="23"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="120"
Margin="0,0,0,0"
Name="ModuleIDFilterCB"
ItemsSource="{Binding Path=Screen.LicenseModuleIDs, Mode=TwoWay}"
DisplayMemberPath="LicenseModuleName"
SelectedItem="{Binding Screen.bufferProp, Converter={StaticResource StringTrimmer}, Mode=TwoWay}" />
</UserControl>
For XAML reference you can use any name in x:Key attribute, not only "StringTrimmer".

Binding to ComboBox with converter in Silverlight

I have a numeric value that I wish to be converted to a more user-friendly string format when it's displayed. I already have an IValueConverter called FlightLevelConverter that I'm using to do this for a normal TextBlock UI item where it works fine.
I now wish to apply the converter to a ComboBox of altitude choices, but I can't get it to work.
This is the relevant part of the XAML I'm using for the ComboBox:
<UserControl.Resources>
<status:FlightLevelConverter x:Key="FlightLevelConverter"/>
</UserControl.Resources>
...
<ComboBox x:Name="AltitudeCombo" Grid.Row="0" Grid.Column="3" Width="100">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource FlightLevelConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
It displays the un-converted numeric values, not the nice string values. I get no errors and if I set a breakpoint in the converter it doesn't get hit, showing that the converter is never called.
I've spent all morning trawling the Internet in general and StackOverflow in particular to try to discover what I'm doing wrong, but haven't found out anything useful.
Why is my converter not being called? What am I doing wrong?
How do you add the items to the ComboBox?
You should set the ItemsSource property to a collection of numeric values, e.g.
List<double> values = new List<double>();
values.Add(2.1);
values.Add(3.2);
values.Add(4.3);
values.Add(5.4);
AltitudeCombo.ItemsSource = values;
If you add ComboBoxItems like this
AltitudeCombo.Items.Add(new ComboBoxItem() { Content = 1.4 });
the ItemTemplate and hence the binding with its converter won't be applied.
Here is a short working sample. You can compare code...
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:bys="clr-namespace:WpfApplication1"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<bys:MyList x:Key="lst"/>
<bys:MyConverter x:Key="myConverter"/>
</Grid.Resources>
<ComboBox ItemsSource="{StaticResource lst}" SelectedIndex="0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource myConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox >
</Grid>
</Window>
C#:
public class MyList : List<int> {
public MyList() {
AddRange(new[] { 1, 2, 3, 4, 5, 6 });
}
}
public class MyConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return String.Format("<<{0}>>", value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
ComboBox.ItemTemplate is not applicable to the main part of the ComboBox if ComboBox.IsEditable == true. It works for dropdown list entries only. Try to set ComboBox.IsEditable == false. It might help.

Binding to a WPF ToggleButton's IsChecked state

I would like to use a WPF ToggleButton to expand and collapse some controls in my application. How can I use XAML to accomplish this?
I'm thinking that I could somehow bind the Visibility attribute of some controls to the ToggleButton's IsChecked state, but I do not know how to do this.
Maybe I need to give my ToggleButton a Name, then bind using ElementName? Then I would need a ValueConverter for converting between a boolean value and a Visibility, correct? How could I make a generic ValueConverter for this purpose?
You need to bind the Visibility through a converter:
<Window
x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<StackPanel>
<ToggleButton x:Name="toggleButton" Content="Toggle"/>
<TextBlock
Text="Some text"
Visibility="{Binding IsChecked, ElementName=toggleButton, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
</Window>
In Silverlight there is no BooleanToVisibilityConverter but it is easy to write your own with some added features:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace WpfApplication1 {
public class BooleanToVisibilityConverter : IValueConverter {
public Object Convert(Object value, Type targetType, Object parameter, CultureInfo culture) {
if (targetType == typeof(Visibility)) {
var visible = System.Convert.ToBoolean(value, culture);
if (InvertVisibility)
visible = !visible;
return visible ? Visibility.Visible : Visibility.Collapsed;
}
throw new InvalidOperationException("Converter can only convert to value of type Visibility.");
}
public Object ConvertBack(Object value, Type targetType, Object parameter, CultureInfo culture) {
throw new InvalidOperationException("Converter cannot convert back.");
}
public Boolean InvertVisibility { get; set; }
}
}
Now you can specify a converter that maps true to Collapsed and false to Visible:
<BooleanToVisibilityConverter
x:Key="InverseBooleanToVisibilityConverter" InvertVisibility="True"/>
Use the BooleanToVisibilityConverter:
<BooleanToVisibilityConverter x:Key="bvc" />
<TextBlock Visibility="{Binding IsChecked, ElementName=toggle, Converter={StaticResource bvc}}" />
Is there a reason why you aren't just using the Expander? It's based on the ToggleButton anyway.

Resources