.NET Framework Developer Center > .NET Development Forums > Windows Presentation Foundation (WPF) > Problems with DataTemplate, Listbox and Binding to SelectedItem
Ask a questionAsk a question
 

AnswerProblems with DataTemplate, Listbox and Binding to SelectedItem

  • Wednesday, November 29, 2006 12:52 AMPhilipp Konradi Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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

  • Tuesday, December 12, 2006 9:39 AMDouglas Stockwell Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

     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

  • Tuesday, December 12, 2006 9:19 AMPhilipp Konradi Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.");
    }
    }


  • Tuesday, December 12, 2006 9:39 AMDouglas Stockwell Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

     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.

  • Tuesday, December 12, 2006 12:08 PMlee dModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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>

     

  • Tuesday, December 12, 2006 2:19 PMPhilipp Konradi Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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