ComboBox for enumeration System.IO.Ports.Parity in WPF - c#

I want (in C#) to populate the list of admissible values for a combobox with the admissible values of the enumeration System.IO.Ports.Parity. To this end I created a collection:
public class theParitySource : ObservableCollection<Parity>
{
public theParitySource()
{
Array parities = System.Enum.GetValues( typeof( Parity ) );
foreach (Parity p in parities) this.Add(p);
}
}
(btw: is there oneliner for this initialization?) and made this the datacontext of the combobox:
...
xmlns:local="clr-namespace:myNamespace"
...
<ComboBox ...>
<ComboBox.DataContext>
<local:theParitySource />
</ComboBox.DataContext>
</ComboBox>
The combobox, however, remains empty (it is shown as empty, but seems to have the correct length), even though I can see in the Debugger how theParitySource gets populated. This approach does work in another combobox (even in the same class) where I do this for a baudrate. That I initialize with integer values, so I guess it is somehow related to the fact that I'm using an enum here, but I'm clueluess what might be the reason. Any pointers? Do I need to write a converter?
(Of course I can work around this by creating a list of strings from the enum, but this would be kind of unpleasant...)
Edit: actually I'd prefer to do all of this in XAML. Is there a simple way to do that?

You can do this all in the Xaml using ObjectDataProvider
In your Window.Resources (or whatever resources you are using) setup a ObjectDataProvider.
To setup the ObjectDataProvider for Enums you set the ObjectType to {x:Type sys:Enum} and the MethodName to GetValues to fill the ComboBox with the actual Enums or you can use GetNames to fill the ComboBox with a string representaion of the Enum
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:io="clr-namespace:System.IO.Ports;assembly=System"
<Window.Resources>
<ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type sys:Enum}" x:Key="ParityValues">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="io:Parity" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
Then bind to your ComboBox
<ComboBox ItemsSource="{Binding Source={StaticResource ParityValues}}" />
Result:

You need to bind to a proper path, you might be setting its data context, but you're not telling it with property to display.
You need to tell it what part of the bound context you are wanting to display and which you are wanting to be 'selected'.
WPF Combobox DisplayMemberPath

Related

WPF Combobox Itemssource is Visibility Enum

So i have a simple question; I want to display the System.Windows.Visibility Enum in a ComboBox. However i don't seem to find the path to it. Searched like a crazy man but couldn't find anyone who know the path to this enum.
I'm aware that this can be done in code (and already have it working) but i prefer to do it in XAML.
Could anyone help me out?
Voila:
<ComboBox SelectedIndex="0">
<Visibility>Visible</Visibility>
<Visibility>Collapsed</Visibility>
<Visibility>Hidden</Visibility>
</ComboBox>
No additional namespaces needed.
#Maciek's solution is the simplest and quickest to apply, however more general, pure XAML way of listing enum values is this:
<FrameworkElement.Resources>
<ObjectDataProvider x:Key="ItemsSource"
ObjectType="{x:Type sys:Enum}"
MethodName="GetValues"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="Visibility" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</FrameworkElement.Resources>
(...)
<ComboBox ItemsSource="{Binding Source={StaticResource ItemsSource}}" />
You can then simply replace Visibility with any other enum type to get a list of its values.
If you want to bind enum values to ItemsSource, you need a property in ViewModel with enum values. Than you have to bind ItemsSource to this property. You get values of enum like this:
Enum.GetValues(typeof(EffectStyle)).Cast<EffectStyle>();
If you can't find a path to your property, that means you have wrong DataContext. Set it to your ViewModel class or to your window, if your are not using MVVM. In the simplest scenario, you could set DataContext to your window in Window constructor.
this.DataContext = this;
Take a look at this or this link, as a big picture of DataContext and Binding in WPF.
If you are interested, also read about MVVM in WPF. You could use MVVM Light
If you want a fully xaml solution for your enum binding to ComboBox, you could write sth like this or use ObjectDataProvider like here.
I think Maciek's solution is good enough in your case.
If you want to do it using code, let's say your combobx has an x:name = my combo you can simply do:
mycombo.ItemsSource = Enum.GetValues(typeof(Visibility)).Cast<Visibility>();

How can I find an item with specific value, in a Collection bound to a Canvas

I've been Google'ing a bit around for this, but without getting any real answer. Probably because my question might be a bit cryptic. Here goes:
Lets say i have an ObservableCollection<SomeModel> containing a bunch of models. I then add the corresponding Views to a Canvas. Specifying this in the resources of the Window, and then bind the Canvas' ItemsSource to the ObservableCollection<SomeModel>. This works fine.
SomeModel is bound to SomeView, this is a UserControl.
Now, when this View gets focus, or when I MouseDown on it, I would like to have it marked as "Selected". Somehow, i would like to have a property in the codebehind to the Window holding my Canvas, where i can always get the selected item.
I've been thinking of having a BindingList instead of the ObservableCollection, and when an IsSelected property on the model changes, then a method will extract the selected item from the list. But this seems to be a bit of a performance killer, as i will be notified on all changes to the items.
How can I accomplish this?
There are multiple ways you could solve this. But probably the simplest it to work with a ListBox and bind to it. ListBox as it has bindable ItemsSource and a SelectedItem property that has the item that is currently selected in it. It also calls the SelectionChanged event when the selection changes if you want to do something in the code behind .cs file.
I would recommend keeping the ObservableCollection in your View Model to keep true to MVVM.
If the style or placement of ListBox does not suit your override the template to something that suits your needs better.
You could look at a behaviour if the above doesn't work for you but best to keep it simple.
UPDATE
This is how you would build the view. Note the ItemsPanel attribute is bound to a defined ItemsPanelTemplate in the UserControl.Resources section which specifies a Canvas to put the items on.
<UserControl
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"
xmlns:local="clr-namespace:SilverlightApplication1"
x:Class="SilverlightApplication1.View1"
d:DesignWidth="640" d:DesignHeight="480">
<UserControl.Resources>
<local:View1Model x:Key="View1ModelDataSource" />
<ItemsPanelTemplate x:Key="ItemsPanelTemplate1">
<Canvas />
</ItemsPanelTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource View1ModelDataSource}}">
<ListBox Margin="80,85,183,54" ItemsPanel="{StaticResource ItemsPanelTemplate1}" ItemsSource="{Binding DataModelCollection}"/>
</Grid>
On the View Model
public class View1Model
{
private ObservableCollection<SomeModel> _DataModelCollection;
public ObservableCollection<SomeModel> DataModelCollection
{
get { return this._DataModelCollection; }
set { this._DataModelCollection = value; }
}
}
It should be noted though that Canvas itself does not have any logic to let the user move controls around on it at runtime.

Bind textblock to dictionary value for key in XAML?

I have a model with an enum property (in this case, related to Export Control Regulations). When displaying the value to the user, I want to show a corresponding string. Sometimes this is in ComboBox (where the user can select a value), and sometimes it is in a TextBlock (where it is read-only).
Example: for ExportRegulationType.EAR, I want to display "EAR", while for ExportRegulationType.DoNotExport, I want to display "Do Not Export". Note that I don't have any language localization needs, but I recognize the issue...
Currently, in my ViewModel, I have a property that returns a string based on the current enum value, and also another property that returns a Dictionary<ExportRegulationType, string>. For the ComboBoxes, I can bind ItemsSource to the dictionary property, and for the TextBlocks, I can bind to the string property. This works, but is kind of clumsy.
Two questions:
1) It seems to me that I should be able to declare the dictionary (with keys and values) as a static resource in XAML (probably in App.xaml), and use that for the ItemsSource for the ComboBox version. However, I can't figure out how to declare and reference such a thing. How can I do that?
2) Assuming the above is in place, I would think I could also set up a binding with the textblock, so based on the enum property, it will look up the string in the dictionary.
I have seen the following questions relating to a static or dynamic enum value. The first isn't adequate, and the second isn't answered...
These should be a XAML-only, and will enable me to remove the methods from my ViewModel (having only the one exposed ExportRegulationType enumerated property. Are these possible?
Edit: Additional information:
In the application, I will have many different sets of views, models, and ViewModels. However, as export control regulations are a common and consistent requirement, I am using composition to keep it DRY. i.e., Models A and B both have an ExportControl model. ViewModels A1, A2, B1 and B2 will have an ExportControlViewModel. The views will have controls bound to the ExportControlViewModel of their ViewModel. The views will have either a ComboBox or a TextBlock, but not both (Depending on if the user can change the value).
I don't know if this will work for your case, but here is a possible solution. In your view model, expose a ExportRegulationType property and then create a value converter to display your desired string.
First create your value converter:
class ExportRegulationTypeToStringConverter: IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
ExportRegulationType regType = (ExportRegulationType)value;
switch(regType)
{
case ExportRegulationType.EAR:
return "EAR";
case ExportRegulationType.DoNotExport:
return "Do Not Export";
//handle other cases
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Binding.DoNothing;
}
#endregion
}
Then add a reference to your converter in your xaml. local is the namespace in which your class is located.
<local:ExportRegulationTypeToStringConverter x:Key="exportRegConverter" />
Finally, set the value of your text box to use the converter. pathToEnum is the property exposed on your ViewModel of type ExportRegulationType.
<TextBlock Text="{Binding pathToEnum, Converter={StaticResource exportRegConverter}}" />
Use ObjectDataProvider to fill the ComboBox with the values of the enum.
<Window.Resources>
<ObjectDataProvider x:Key="dataFromEnum"
MethodName="GetValues" ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:ExportRegulationType"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
Now we create the ComboBox and use a container style with our value converter to display the desired strings for our enum.
<ComboBox ItemsSource="{Binding Source={StaticResource dataFromEnum}}">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Content" Value="{Binding Converter={StaticResource exportRegConverter}}" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
Instead of the Dictionary you have another option.
See the following question: WPF Binding a ListBox to an enum, displaying the Description Attribute
You could add a Description Attribute to your enums like this
public enum ExportRegulationType
{
[Description("EAR")]
EAR,
[Description("Do Not Export")]
DoNotExport
}
And when you want to display it, you can just use EnumDescriptionConverter Converter found in the question I linked
I solved this with a blend of what #Dylan and #Meleak wrote. I'm putting this as an answer to show what the final solution was:
First, I implemented an IValueConverter, (based on #Meleak's answer):
class EnumDescriptionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Enum regulation = (Enum)value;
return GetEnumDescription(regulation);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return String.Empty;
}
/// <summary>
/// Returns text intended for display based on the Description Attribute of the enumeration value.
/// If no Description Attribute is applied, the value is converted to a string and returned.
/// </summary>
/// <param name="enumObj">The enumeration value to be converted.</param>
/// <returns>Text of the Description Attribute or the Enumeration itself converted to string.</returns>
private string GetEnumDescription(Enum enumObj)
{
// Get the DescriptionAttribute of the enum value.
FieldInfo fieldInfo = enumObj.GetType().GetField(enumObj.ToString());
object[] attributeArray = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributeArray.Length == 0)
{
// If no Description Attribute was found, default to enum value conversion.
return enumObj.ToString();
}
else
{
// Get the text of the Description Attribute
DescriptionAttribute attrib = attributeArray[0] as DescriptionAttribute;
return attrib.Description;
}
}
}
I tagged my enum (note that several values are not tagged as the desired text is the same as the value itself):
public enum ExportRegulationType
{
[Description("Not Determined")]
NotDetermined, // Export authority not determined
EAR, // Controlled by EAR Regulations
ITAR, // Controlled by ITAR Regulations
[Description("Do Not Export")]
DoNotExport, // Export not allowed
Unrestricted // Export not controlled
}
In my App.xaml, I declared the ObjectDataProvider to get the list of enum values and the EnumDisplayConverter (Here since they will be used by several different views):
<Application.Resources>
[Other stuff...]
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type sys:Enum}"
x:Key="ExportRegulationValues">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="models:ExportRegulationType"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<local:EnumDescriptionConverter x:Key="ExportDisplayConverter"/>
</Application.Resources>
For a TextBlock:
<TextBlock Text="{Binding Export.Regulation, Converter={StaticResource ExportDisplayConverter}}"/>
For a Combo Box:
<ComboBox ItemsSource="{Binding Source={StaticResource ExportRegulationValues}}"
SelectedValue="{Binding Document.Export.Regulation}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource ExportDisplayConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This works perfectly!
Use an ObjectDataProvider
then bind the ComboBox's Items to it, and set "DisplayMemberPath" to "Value".
What this should do is to show the values of your dictionary, but in code-behind the SelectedValue is a KeyValuePair<>.
For your textblock, use a Binding using ElementName=yourcombobox and Path=SelectedItem:
<TextBlock Text="{Binding SelectedItem, ElementName=yourcombobox}" />
Let me know how it goes =)
Here is a blog post of mine with an approach using attached behaviors.
It is based on the principle that different enumeration values don't need to be limited to switching strings. Instead, you can declare whatever pieces of UI you want to represent each value (strings, images, different controls and layouts, etc.) and use an attached behavior to control their visibility.
Your situation, then, can be framed as having two different text blocks, each bound to the same property of type ExportRegulationType. Since they are bound to the same property, their visibilities are mutually exclusive:
<Grid>
<TextBlock
Text="EAR"
local:EnumVisibility.Value="{Binding ExportRegulationType}"
local:EnumVisibility.TargetValue="EAR"
/>
<TextBlock
Text="Do Not Export"
local:EnumVisibility.Value="{Binding ExportRegulationType}"
local:EnumVisibility.TargetValue="DoNotExport"
FontWeight="Bold"
/>
</Grid>
I included the FontWeight="Bold" to show that you can make different decisions for each enumeration value. This also supports XAML localization because the text is set like any other text block.
See the post for a complete walkthrough of the solution, code samples, and a zip file containing the framework and an example application.
Edit in response to additional information:
Here is another post in the same series which describes how to select enumeration values with Selector controls.
A ComboBox bound to the ExportRegulationType property would look this this:
<ComboBox local:EnumSelector.SelectedValue="{Binding ExportRegulationType, Mode=TwoWay}">
<ComboBoxItem Content="EAR" local:EnumSelector.ItemValue="EAR" />
<ComboBoxItem Content="Do Not Export" local:EnumSelector.ItemValue="DoNotExport" />
</ComboBox>
We associate each item with an enumeration value, then use a TwoWay binding to EnumSelector.SelectedValue so it will write back to the view model's property whenever it changes.
This provides the same flexibility as with the text blocks: you can make whatever decisions you want about how to set the text and what is contained by each item.

WPF context menu bound to List<> dependency property

Im trying to make the contents of a List thats a dependency property show up in a WPF context menu.
I have a class with the following dependency property, a list of Foo's (data holding class):
public List<Foo> FooList
{
get { return (List<Foo>)GetValue(FooListProperty); }
set { SetValue(FooListProperty, value); }
}
public static DependencyProperty FooListProperty =
DependencyProperty.Register("FooList", typeof(List<Foo>),
typeof(FooButton));
In XAML I set up the following static resource, I assume its needed since the context menu isnt part of the visual tree:
<UserControl.Resources>
<ResourceDictionary>
<CollectionViewSource
x:Key="FooListSource"
Source="{Binding FooList}"/>
<!-- ... -->
</ResourceDictionary>
</UserControl.Resources>
Also part of the ResourceDictionary above is a CompositeCollection which is needed to make the items show up in the actual context menu. If the UserControl CanStop property is true, we also show a separator and a stop command. These bindings does also fail, although the MenuItems themselves show up. So If I can figure out why these fail, the List might be easier.
<CompositeCollection x:Key="FooListItems">
<CollectionContainer
Collection="{Binding Source={StaticResource FooListSource}}"/>
<Separator
Visibility="{Binding CanStop,
Converter={StaticResource VisibleIfTrue}}" />
<MenuItem
Command="{x:Static Buttons:FooButton.Stop}"
Header="Stop"
Visibility="{Binding CanStop,
Converter={StaticResource VisibleIfTrue}}"/>
</CompositeCollection>
And finally the context menu itself, also in the ResourceDictionary:
<ContextMenu
x:Key="FooButtonMenu"
ItemsSource="{Binding Source={StaticResource FooListItems}}"
ItemTemplate="{StaticResource FooListTemplate}"
<ContextMenu.CommandBindings>
<CommandBinding
Command="{x:Static Buttons:FooButton.Stop}"
Executed="Stop_Executed" />
</ContextMenu.CommandBindings>
</ContextMenu>
I feel Im posting way to much code but Im not sure I can make this piece any simpler. Only the separator and the hardcoded menuitem shows up. So something must be messed up with the bindings. Bindings are usually not that hard but now when I want to bind something thats not really part of the same tree I feel a bit lost.
Any suggestions are welcome. :)
As you suspected, your problem does seem to be caused by the use of List<Foo> instead of ObservableCollection<Foo>. Since List<Foo> doesn't notify on property changes, the only way to get WPF to recognize you've added or removed an item is to temporarily set the FooList property to something else and then set it back.
There is no need to switch to a CLR property. Just change List<Foo> to ObservableCollection<Foo>.
The reason the bindings in your CompositeCollection aren't working is that CompositeCollection is not a DependencyObject, so it can't inherit a DataContext.
I don't see why you've made FooList a dependency property. You're not making it the target of a binding, which is the most common reason to create a dependency property. You haven't implemented a callback, so it can't do change notification (the second most common reason to create a dependency property). You're not using it for value inheritance. So why, then?
It seems to me that what you really want is for FooList to be a normal CLR property of type ObservableCollection<Foo> (or any class that implements INotifyCollectionChanged). That will do all the change notification that you need - at least, that you need for the code you've posted so far.

Get databinding object in control

I want to create an enumcombobox where the popup will show the enumvalues of the controls' binding object. Somehow I cannot get the binding object property at runtime. Databindings wil get me to the binding object. But the property and its type is invisable for me, or I just didn't find it yet... Can anyone help me with this?
You have to use a DataObjectProvider. In your resources, put something like :
<ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type sys:Enum}" x:Key="odpEnum">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="yourEnumNameHere"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
In your combo, put :
<ComboBox ItemsSource="{Binding Source={StaticResource odpEnum}}"/>
This should fill your combo with your enum.

Categories

Resources