web-dev-qa-db-fra.com

Liaison WPF à Listbox selectedItem

Quelqu'un peut-il aider avec ce qui suit - j'ai joué avec cela, mais je ne peux pas le faire fonctionner pour le reste de ma vie.

J'ai un modèle de vue qui contient les propriétés suivantes;

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

Dans mon XAML, j'ai;

<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>

Maintenant, le ItemsSource fonctionne bien et je reçois une liste d'objets Rule avec leurs noms affichés dans lbRules. 

Le problème que je rencontre a pour effet de lier la propriété SelectedRule à SelectedItem de lbRules. J'ai essayé de lier la propriété text d'un textblock à SelectedRule mais elle est toujours nulle.

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

L'erreur que je vois dans la fenêtre de sortie est la suivante: Erreur de chemin BindingExpression: propriété 'SelectedRule' introuvable.

Quelqu'un peut-il m'aider avec cette liaison - Je ne vois pas pourquoi il ne devrait pas trouver la propriété SelectedRule.

J'ai ensuite essayé de changer la propriété text du textblock comme ci-dessous, ce qui fonctionne. Le problème est que je veux utiliser SelectedRule dans mon ViewModel.

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

Merci beaucoup pour votre aide.

29
Oli Baylis

Tout d'abord, vous devez implémenter l'interface INotifyPropertyChanged dans votre modèle de vue et déclencher l'événement PropertyChanged dans l'outil de définition de la propriété Rule. Sinon, aucun contrôle lié à la propriété SelectedRule ne "saura" qu'il aura été modifié.

Ensuite, votre XAML

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

est parfaitement valable si cette TextBlock est en dehors de la ListBox 's ItemTemplate et a la même DataContext que la ListBox.

24
arconaut

Dans la DataTemplate, vous travaillez dans le contexte d'une Rule, c'est pourquoi vous ne pouvez pas vous lier à SelectedRule.Name - cette propriété n'existe pas sur une Rule. Pour vous lier au contexte de données d'origine (votre ViewModel), vous pouvez écrire:

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

UPDATE: en ce qui concerne la liaison de la propriété SelectedItem, elle a l'air parfaitement valide, j'ai essayé la même chose sur ma machine et tout fonctionne bien. Voici mon application de test complète:

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 derrière:

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 a raison,

Dans DataTemplate, votre DataContext est définie sur Rule et est actuellement traitée par celle-ci.

Pour accéder aux parents DataContext, vous pouvez également utiliser une RelativeSource dans votre lien:

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

Plus d'infos sur RelativeSource peuvent être trouvées ici:

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

3
Arcturus

Pour moi, j'utilise généralement DataContext ensemble afin de lier une propriété à deux profondeurs telle que cette question.

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

Ou bien, je préfère utiliser ElementName parce que les liaisons ne sont réalisées qu'avec des contrôles d'affichage.

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

0
Youngjae