Связывание WPF с Listbox selectedItem
Может ли кто-нибудь помочь со следующим - играть с этим, но не может, чтобы жизнь меня заставила его работать.
У меня есть модель представления, которая содержит следующие свойства:
public ObservableCollection<Rule> Rules { get; set; }
public Rule SelectedRule { get; set; }
В моем XAML у меня есть:
<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>
Теперь ItemsSource отлично работает, и я получаю список объектов Rule с их именами, отображаемыми в lbRules.
У меня возникает проблема привязки свойства SelectedRule к элементу lbRules "SelectedItem". Я попытался привязать свойство textblock text к SelectedRule, но он всегда имеет значение null.
<TextBlock Text="{Binding Path=SelectedRule.Name}" />
Ошибка, которую я вижу в окне вывода:
Ошибка пути BindingExpression: свойство SelectedProject не найдено.
Может ли кто-нибудь помочь мне с этим связыванием - я не понимаю, почему он не должен найти свойство SelectedRule.
Затем я попытался изменить текстовое свойство textblock, как показано ниже, и это работает. Проблема в том, что я хочу использовать SelectedRule в моей ViewModel.
<TextBlock Text="{Binding ElementName=lbRules, Path=SelectedItem.Name}" />
Большое спасибо за вашу помощь.
Ответы
Ответ 1
Во-первых, вам нужно реализовать интерфейс INotifyPropertyChanged
в вашей модели представления и поднять событие PropertyChanged
в настройщике свойства Rule
. В противном случае управление, связанное с свойством SelectedRule
, будет "знать", когда оно было изменено.
Затем ваш XAML
<TextBlock Text="{Binding Path=SelectedRule.Name}" />
отлично, если этот TextBlock
находится вне ListBox
ItemTemplate
и имеет тот же DataContext
, что и ListBox
.
Ответ 2
Внутри DataTemplate
вы работаете в контексте Rule
, поэтому вы не можете привязываться к SelectedRule.Name
- такого свойства на Rule
нет.
Чтобы привязываться к исходному контексту данных (который является вашим ViewModel), вы можете написать:
<TextBlock Text="{Binding ElementName=lbRules, Path=DataContext.SelectedRule.Name}" />
UPDATE: относительно привязки свойств SelectedItem, он выглядит совершенно корректно, я пробовал то же самое на своей машине, и он отлично работает. Вот мое полное тестовое приложение:
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>
Код позади:
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;
}
}
}
}
Ответ 3
Yocoder прав,
Внутри DataTemplate
ваш DataContext
установлен в Rule
, который он обрабатывает в настоящее время.
Чтобы получить доступ к родителям DataContext
, вы можете также рассмотреть возможность использования RelativeSource
в вашей привязке:
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ____Your Parent control here___ }}, Path=DataContext.SelectedRule.Name}" />
Более подробную информацию о RelativeSource
можно найти здесь:
http://msdn.microsoft.com/en-us/library/system.windows.data.relativesource.aspx
Ответ 4
Для меня я обычно использую DataContext
вместе, чтобы связать свойство с двумя глубинами, например, этот вопрос.
<TextBlock DataContext="{Binding SelectedRule}" Text="{Binding Name}" />
Или я предпочитаю использовать ElementName
, потому что он получает привязки только с элементами управления.
<TextBlock DataContext="{Binding ElementName=lbRules, Path=SelectedItem}" Text="{Binding Name}" />
Ответ 5
поскольку вы устанавливаете источник items в свою коллекцию, ваше текстовое поле привязывается к каждому отдельному элементу в этой коллекции. свойство выбранного элемента полезно в этом сценарии, если вы пытались сделать форму мастер-детали, имея 2 списка. вы должны привязать второй источник элементов списка к дочерней коллекции правил. в других словах выбранный элемент предупреждает внешние элементы управления, что ваш источник был изменен, внутренние элементы управления (те, что внутри вашего datatemplate уже знают об изменениях.
и для ответа на ваш вопрос да в большинстве случаев установка источника данных такая же, как установка datacontext элемента управления.