mshwf
mshwf

Reputation: 7469

Bind TextBlock's Text to the SeletedItem text of a ListBox?

I have a ListBox that I provide with DisplayMemberPath in run time, and have a TextBlock that I want its Text property to be bound to the selected item text of the ListBox, hardcoding the value works:

<TextBlock Text="{Binding ElementName=lstBx, Path=SelectedItem.Title}"/>

But how I do it without knowing which property is used for displaying?

Edit

The ListBox and the TextBlock are parts of the ControlTemplate of a custom control that has a custom DisplayMemberPath property that I bind to the listBox's DisplayMemberPath

    <ListBox Name="lstBx" ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ItemsSource}" 
DisplayMemberPath="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DisplayMemberPath}" 
SelectedValuePath="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SelectedValuePath}"/>

Upvotes: 0

Views: 104

Answers (2)

Il Vic
Il Vic

Reputation: 5666

In my opinion you have 2 options:

  1. create your own extension of Listbox. which has a kind of SelectedDisplay property (it should work like SelectedValue property, but using DisplayMemberPath instead of SelectedValuePath)
  2. use a MultiBinding and some lamba expression dynimic building.

I will show you the second solution. First of all the multibinding:

<TextBlock>
    <TextBlock.Text>
        <MultiBinding Mode="OneWay" Converter="{StaticResource PropertyMultiValueConverter}">
            <Binding ElementName="lstBx" Path="SelectedItem"/>
            <Binding ElementName="lstBx" Path="DisplayMemberPath" />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

Now the code for the PropertyMultiValueConverter class (of course it can be improved and optimized for avoiding a continuos lambda expression generation):

public class PropertyMultiValueConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        object selectedItem = values[0];
        string displayMemberPath = values[1] as string;

        if (selectedItem != null)
        {
            ParameterExpression param =
                System.Linq.Expressions.Expression.Parameter(selectedItem.GetType(), "x");
            MemberExpression body =
                System.Linq.Expressions.Expression.Property(param, displayMemberPath);

            LambdaExpression lambda =
                System.Linq.Expressions.Expression.Lambda(body, param);

            Delegate expression = lambda.Compile();
            return expression.DynamicInvoke(selectedItem);
        }
        else
        {
            return null;
        }
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

Lambda expressions are faster than using reflection, so I prefer this way. As you can see the generated lambda expression retrieves the value of the property and returns it.

Upvotes: 1

rahulaga-msft
rahulaga-msft

Reputation: 4164

I think i get your question. Seems you meant SelectedItem.{CanBeAnyProperty} right?

One solution could be you bind your textblock like below :

<TextBlock Text="{Binding ElementName=lstBx, Path=SelectedItem}"/>

And override SelectedItem ToString() method to return appropriate desired property value.

For example: say your Item bound to list is Person.

So you can override ToString() method of person class like :

class Person{

Public string ToString(){
return Name; /*this again you need to work out runtime* OR/
return Age.ToString();}
}

Upvotes: 0

Related Questions