Mark A. Donohoe
Mark A. Donohoe

Reputation: 30368

How can you bind an attached property to DisplayMemberPath of a ComboBox?

I can't seem to find the correct syntax to allow an attached property to be used as the DisplayMemberPath of a ComboBox.

The property is SelectorSwitchedControl.NameForSelector
It's in the namespace 'LocalTest' which is mapped to the XAML prefix 'local'.

Here's the code...

<UserControl x:Class="Playground.SelectorSwitchedControlTest.SelectorSwitchedControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:glc="clr-namespace:Playground.CommonControls"
    xmlns:local="clr-namespace:Playground.SelectorSwitchedControlTest"
    Background="Transparent">

    <Border x:Name="MainBorder"
        BorderBrush="Gray" BorderThickness="1">

        <DockPanel>

            <glc:FixedToolBar DockPanel.Dock="Top">

                <ComboBox x:Name="MainSelector"
                    ItemsSource="{Binding Children, ElementName=MainPanel}"
                    DisplayMemberPath="(local:SelectorSwitchedControl.NameForSelector)" />

            </glc:FixedToolBar>

            <local:SelectorSwitchedControlPanel x:Name="MainPanel" />

        </DockPanel>

    </Border>

</UserControl>

...which for some reason gives me the exception 'Prefix 'local' does not map to a namespace.' which I'm not sure why it's saying that as if I remove the 'DisplayMemberPath' line, the '' tag renders just like it's supposed to proving the namespace is mapped.

I've also tried all of the following...

I know it's just one of those days where my mind isn't working and I'm missing something simple, but it's driving me crazy! So what's the proper syntax?

Upvotes: 2

Views: 680

Answers (3)

Aleksey
Aleksey

Reputation: 1347

DisplayMemberPath - path to the display string property for each item. Set it to "NameForSelector", not to "{Binding NameForSelector}".

 <DockPanel>
    <ComboBox x:Name="MainSelector" ItemsSource="{Binding Children}" DisplayMemberPath="NameForSelector" />
 </DockPanel>

 public class SelectorSwitchedControl
    {
        public string Name { get; set; }
        public string NameForSelector{ get; set; }
    }

Upvotes: 2

Anton Tykhyy
Anton Tykhyy

Reputation: 20066

I think it's just not possible to use an attached property in DisplayMemberPath with regular controls. The reason is that the property path you are using refers to an XML namespace declared in your XAML. Normally when you use the attached property syntax there is a parser context available when the XAML/BAML reader is creating the objects and this context supplies the namespace information. However DisplayMemberPath is just a string and does not capture this context, so this context is not available to supply the namespace information at the point where your property path is actually used to create a binding. From my reading of code in PresentationFramework.dll, you might be able to supply the context through the target object (the one to which your property is attached) by having it implement IServiceProvider and return a suitable IXamlTypeResolver (relevant code starts from PropertyPath.GetTypeFromName).

As a cheaper alternative, consider a template or template selector instead of DisplayMemberPath. If you want to use the default lookup mechanisms, try something along the lines of

<ItemTemplate>
  <DataTemplate>
    <ContextPresenter 
      Content="{Binding (local:SelectorSwitchedControl.NameForSelector)}"/>
  </DataTemplate>
</ItemTemplate>

Upvotes: 1

Neal
Neal

Reputation: 111

The correct value is

DisplayMemberPath="(local:SelectorSwitchedControl.NameForSelector)"

If that is not working then I would use Snoop (http://snoopwpf.codeplex.com/) to make sure that the value is getting set correctly.

Here is the simplest working example

Xaml:

<Window x:Class="WPFTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WPFTest"
    Title="MainWindow" Height="350" Width="525" Loaded="MainWindow_Loaded">
<Grid>
    <ComboBox Name="cb" DisplayMemberPath="(local:MainWindow.TestValue)" />
</Grid>

Code:

public static string GetTestValue(DependencyObject element)
    {
        return (string)element.GetValue(TestValueProperty);
    }

    public static void SetTestValue(DependencyObject element, string value)
    {
        element.SetValue(TestValueProperty, value);
    }

    public static readonly DependencyProperty TestValueProperty = DependencyProperty.RegisterAttached("TestValue", typeof(string), typeof(MainWindow), new FrameworkPropertyMetadata(null));

    private void MainWindow_Loaded(object sender, System.Windows.RoutedEventArgs e)
    {
        TextBlock tb = default(TextBlock);


        for (int i = 10; i <= 15; i++)
        {
            tb = new TextBlock();
            tb.Text = "Text for " + i;
            tb.SetValue(TestValueProperty, "Property For " + i);
            this.cb.Items.Add(tb);
        }
    }

Upvotes: 0

Related Questions