Problems with DataTemplate, Listbox and Binding to SelectedItem
- Hi
I'm trying to define a DataTemplate for an enum class.
A Listbox containing RadioButton for every enum state appeared to be a common solution and worked so far.
The point were I'm getting into trouble is when I try to bind the SelectedItem of a Listbox (which is one of the enum states) to the object which the datatemplate was applied to.
My XAML:
<DataTemplate DataType="{x:Type custScrewing:ScrewDriverState}">
<DataTemplate.Resources>
<!-- This data provider provides an array of different ScrewDriverState enums -->
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type sys:Enum}"
x:Key="allScrewDriverStates">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="custScrewing:ScrewDriverState"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<!-- A value converter which returns the description found in an enum value's
DescriptionAttribute, or the value's name if the attribute is not present. -->
<custCommon:EnumValueDescriptionConverter x:Key="enumValueDescriptionConverter" />
</DataTemplate.Resources>
<ListBox Name="lbScrewDriverStates"
SelectedItem="{Binding}"
ItemsSource="{Binding Source={StaticResource allScrewDriverStates}}"
BorderThickness="0"
ScrollViewer.VerticalScrollBarVisibility="Auto"
IsSynchronizedWithCurrentItem="True"
>
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding Converter={StaticResource enumValueDescriptionConverter}}"
IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}},Path=IsSelected}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
If I start my application, an System.InvalidOperationException occurs saying "Two-way binding requires Path or XPath.".
The declaration SelectedItem="{Binding}" causes this problem and this is actually pretty strange since usually if Path is not provided then the the binding should be applied to the object (binding source) itself. I tried many things but I can't get it to work.
Can somebody help me out?
Thanks,
Philipp
Answers
Philipp Konradi wrote: If I start my application, an System.InvalidOperationException occurs saying "Two-way binding requires Path or XPath.".
The declaration SelectedItem="{Binding}" causes this problem and this is actually pretty strange since usually if Path is not provided then the the binding should be applied to the object (binding source) itself. I tried many things but I can't get it to work.The problem is as you have described, you need a path, a binding without a path binds to the current datacontext. But in this situation, even if you could have a two-way binding, updating the source could only replace the datacontext - it couldn't actually set the value somewhere else because it doesn't know where it came from.
Usually a datatemplate would provide the ability to edit the properties of an item, not replace the item itself. Unfortunately I think the only way to get around this, is to rearchitect what you are doing.
All Replies
- No ideas?
Or is maybe my explanation a little bit weird? In such a case just let me know and I'll provide any details on it.
I took the main ideas for creating such a data template for my enum class from:
- http://www.infusionblogs.com/blogs/jsmith/archive/2006/08/29/835.aspx
- http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=893790&SiteID=1
Please take a look at this problem. You'll help me a lot.
Philipp
For the case somebody wants to reproduce this problem, here are some sources:
public enum ScrewDriverState
{
[Description("ScrewDriver IDLE")]
IDLE,
[Description("ScrewDriver SCREWING")]
SCREWING,
[Description("ScrewDriver UNSCREWING")]
UNSCREWING
}
[ValueConversion(typeof(Enum), typeof(String))]
public class EnumValueDescriptionConverter : IValueConverter
{
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Enum == false)
throw new ArgumentException("'value' must be an enum.");
// Get the field in the enum type which represents the argument value.
FieldInfo field = value.GetType().GetField(value.ToString());
// Get the DescriptionAttribute on that field, if it exists.
object[] attributes = field.GetCustomAttributes(typeof(DescriptionAttribute), false);
// The enum value is not decorated with the attribute, so just return the value itself.
if (attributes.Length == 0)
return value.ToString();
// Return the description applied to the enum value.
DescriptionAttribute descriptionAttribute = attributes[0] as DescriptionAttribute;
return descriptionAttribute.Description;
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException("ConvertBack not supported.");
}
} Philipp Konradi wrote: If I start my application, an System.InvalidOperationException occurs saying "Two-way binding requires Path or XPath.".
The declaration SelectedItem="{Binding}" causes this problem and this is actually pretty strange since usually if Path is not provided then the the binding should be applied to the object (binding source) itself. I tried many things but I can't get it to work.The problem is as you have described, you need a path, a binding without a path binds to the current datacontext. But in this situation, even if you could have a two-way binding, updating the source could only replace the datacontext - it couldn't actually set the value somewhere else because it doesn't know where it came from.
Usually a datatemplate would provide the ability to edit the properties of an item, not replace the item itself. Unfortunately I think the only way to get around this, is to rearchitect what you are doing.
try this for listbox
<ListBox SelectedValue="{Binding ElementName=win1, Path=State}" ItemsSource="{Binding Source={StaticResource odp}}" Name="q1">
<ListBox.Resources>
<Style x:Key="{x:Type ListBoxItem}" TargetType="{x:Type ListBoxItem}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="Transparent">
<RadioButton Content="{TemplateBinding ContentControl.Content}"
IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}},Path=IsSelected}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>
<ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type sys:Enum}" x:Key="odp">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="l:ScrewDriverState"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
- Douglas and lee d, thank you both a lot!
Now I see my misconception.
Previously I saw already that Styles were used with the Enum Listbox. But the concept of DataTemplates appeared to me more powerfull since in case of Styles I fix myself on using a Listbox for visualization, but with DataTemplates I saw the possibility to have full control of data visualization, e.g. use a ComboBox instead of the ListBox. As it turns out now I didn't considered the drawbacks for this more on flexibility and easy to use (I could just write GroupBox.Content = <myData>). But thanks to Douglas, it became now clear to me that inside a data template one does not have a (good) connection to the outer data and following can't bind the templated object to it. So I think I have to return to Styles again :-)
Thanks again,
Philipp

