it-swarm.com.de

WPF-Bindung an Listbox selectedItem

Kann jemand mit dem folgenden helfen - habe damit rumgespielt, aber für das Leben von mir kann es nicht funktionieren.

Ich habe ein Ansichtsmodell, das die folgenden Eigenschaften enthält:

public ObservableCollection<Rule> Rules { get; set; }
public Rule SelectedRule { get; set; }

In meiner XAML habe ich;

<ListBox x:Name="lbRules" ItemsSource="{Binding Path=Rules}" 
         SelectedItem="{Binding Path=SelectedRule, Mode=TwoWay}">
<ListBox.ItemTemplate>
    <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Name:" />
                <TextBox x:Name="ruleName">
                    <TextBox.Text>
                        <Binding Path="Name" UpdateSourceTrigger="PropertyChanged" />
                    </TextBox.Text>
                </TextBox>
            </StackPanel>
    </DataTemplate>
</ListBox.ItemTemplate>

Nun funktioniert die ItemsSource einwandfrei und ich erhalte eine Liste von Regelobjekten, deren Namen in lbRules angezeigt werden. 

Problem, das ich habe, bindet die SelectedRule-Eigenschaft an SelectedItem von lbRules. Ich habe versucht, die Texteigenschaft eines Textblocks an SelectedRule zu binden, aber es ist immer null.

<TextBlock Text="{Binding Path=SelectedRule.Name}" />

Der Fehler, den ich im Ausgabefenster sehe, ist: BindingExpression path error: 'SelectedRule' Eigenschaft wurde nicht gefunden.

Kann mir jemand bei dieser Bindung helfen? Ich kann nicht erkennen, warum die SelectedRule-Eigenschaft nicht gefunden werden sollte.

Ich habe dann versucht, die Texteigenschaft des Textblocks wie folgt zu ändern, was funktioniert. Das Problem ist, dass ich die SelectedRule in meinem ViewModel verwenden möchte.

<TextBlock Text="{Binding ElementName=lbRules, Path=SelectedItem.Name}" />

Vielen Dank für Ihre Hilfe.

29
Oli Baylis

Zunächst müssen Sie die Schnittstelle INotifyPropertyChanged in Ihr Ansichtsmodell implementieren und das Ereignis PropertyChanged im Setter der Eigenschaft Rule auslösen. Ansonsten "weiß" kein Steuerelement, das an die SelectedRule-Eigenschaft bindet, wenn es geändert wurde.

Dann deine XAML

<TextBlock Text="{Binding Path=SelectedRule.Name}" />

ist vollkommen gültig, wenn diese TextBlock außerhalb der ListBox der ItemTemplate ist und dieselbe DataContext wie die ListBox hat.

24
arconaut

Innerhalb von DataTemplate arbeiten Sie im Kontext einer Rule. Deshalb können Sie nicht an SelectedRule.Name binden - es gibt keine solche Eigenschaft für eine Rule. Sie können sich an den ursprünglichen Datenkontext (der Ihr ViewModel ist) binden schreiben:

<TextBlock Text="{Binding ElementName=lbRules, Path=DataContext.SelectedRule.Name}" />

UPDATE: In Bezug auf die SelectedItem-Eigenschaftsbindung sieht es einwandfrei aus. Ich habe es auf meinem Rechner versucht und es funktioniert einwandfrei. Hier ist meine vollständige Test-App:

XAML:

<Window x:Class="TestWpfApplication.ListBoxSelectedItem"
    xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
    Title="ListBoxSelectedItem" Height="300" Width="300"
    xmlns:app="clr-namespace:TestWpfApplication">
    <Window.DataContext>
        <app:ListBoxSelectedItemViewModel/>
    </Window.DataContext>
    <ListBox ItemsSource="{Binding Path=Rules}" SelectedItem="{Binding Path=SelectedRule, Mode=TwoWay}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="Name:" />
                    <TextBox Text="{Binding Name}"/>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Window>

Code hinter:

namespace TestWpfApplication
{
    /// <summary>
    /// Interaction logic for ListBoxSelectedItem.xaml
    /// </summary>
    public partial class ListBoxSelectedItem : Window
    {
        public ListBoxSelectedItem()
        {
            InitializeComponent();
        }
    }


    public class Rule
    {
        public string Name { get; set; }
    }

    public class ListBoxSelectedItemViewModel
    {
        public ListBoxSelectedItemViewModel()
        {
            Rules = new ObservableCollection<Rule>()
            {
                new Rule() { Name = "Rule 1"},
                new Rule() { Name = "Rule 2"},
                new Rule() { Name = "Rule 3"},
            };
        }

        public ObservableCollection<Rule> Rules { get; private set; }

        private Rule selectedRule;
        public Rule SelectedRule
        {
            get { return selectedRule; }
            set
            {
                selectedRule = value;
            }
        }
    }
}
10
Max Galkin

Yocoder hat recht,

Innerhalb der DataTemplate ist Ihre DataContext auf die Rule gesetzt, die derzeit behandelt wird.

Um auf die Eltern DataContext zuzugreifen, können Sie auch die Verwendung eines RelativeSource in Ihrer Bindung in Betracht ziehen:

<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ____Your Parent control here___ }}, Path=DataContext.SelectedRule.Name}" />

Weitere Informationen zu RelativeSource finden Sie hier:

http://msdn.Microsoft.com/de-de/library/system.windows.data.relativesource.aspx

3
Arcturus

Für mich verwende ich normalerweise DataContext, um eine Zwei-Tiefen-Eigenschaft wie diese Frage zu binden.

<TextBlock DataContext="{Binding SelectedRule}" Text="{Binding Name}" />

Oder ich bevorzuge ElementName, da Bindungen nur mit Ansichtssteuerelementen erzielt werden.

<TextBlock DataContext="{Binding ElementName=lbRules, Path=SelectedItem}" Text="{Binding Name}" />

0
Youngjae