Привязки в WPF ComboBox в пользовательский список
у меня есть ComboBox, который, кажется, не обновляет SelectedItem/SelectedValue.
элемент ComboBox ItemsSource привязан к свойству в классе ViewModel, который перечисляет кучу записей телефонной книги RAS как CollectionView. Тогда я связал (в разное время) оба SelectedItem или SelectedValue к другому свойству ViewModel. Я добавил MessageBox в команду save для отладки значений, установленных привязкой данных, но SelectedItem/SelectedValue привязка не быть набор.
класс ViewModel выглядит примерно так:
public ConnectionViewModel
{
private readonly CollectionView _phonebookEntries;
private string _phonebookeEntry;
public CollectionView PhonebookEntries
{
get { return _phonebookEntries; }
}
public string PhonebookEntry
{
get { return _phonebookEntry; }
set
{
if (_phonebookEntry == value) return;
_phonebookEntry = value;
OnPropertyChanged("PhonebookEntry");
}
}
}
коллекция _phonebookEntries инициализируется в конструкторе из бизнес-объекта. XAML ComboBox выглядит примерно так:
<ComboBox ItemsSource="{Binding Path=PhonebookEntries}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding Path=PhonebookEntry}" />
меня интересует только фактическое строковое значение, отображаемое в выпадающем списке, а не любые другие свойства объекта, поскольку это значение мне нужно передать в RAS, когда я хочу сделать VPN-соединение, следовательно DisplayMemberPath и SelectedValuePath оба свойства имени ConnectionViewModel. ComboBox находится в DataTemplate применил к ItemsControl в окне кто DataContext был установлен в экземпляр ViewModel.
ComboBox отображает список элементов правильно, и я могу выбрать один в пользовательском интерфейсе без проблем. Однако, когда я показываю окно сообщения из команды, свойство PhonebookEntry все еще имеет начальное значение в нем, а не выбранное значение из выпадающего списка. Другие экземпляры текстового поля являются обновление штрафа и отображение в окне сообщений.
что мне не хватает с привязкой данных ComboBox? Я много искал и не могу найти ничего, что я делаю неправильно.
это поведение, которое я вижу, однако оно не работает по какой-то причине в моем конкретном контексте.
у меня есть MainWindowViewModel, который имеет CollectionView из ConnectionViewModels. В MainWindowView.код файла xaml, я установил DataContext в значение MainWindowViewModel. В MainWindowView.xaml имеет ItemsControl привязан к коллекции ConnectionViewModels. У меня есть DataTemplate, который содержит ComboBox, а также некоторые другие текстовые поля. Текстовые поля привязаны непосредственно к свойствам ConnectionViewModel с помощью Text="{Binding Path=ConnectionName}".
public class ConnectionViewModel : ViewModelBase
{
public string Name { get; set; }
public string Password { get; set; }
}
public class MainWindowViewModel : ViewModelBase
{
// List<ConnectionViewModel>...
public CollectionView Connections { get; set; }
}
код XAML-behind:
public partial class Window1
{
public Window1()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
тогда XAML:
<DataTemplate x:Key="listTemplate">
<Grid>
<ComboBox ItemsSource="{Binding Path=PhonebookEntries}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding Path=PhonebookEntry}" />
<TextBox Text="{Binding Path=Password}" />
</Grid>
</DataTemplate>
<ItemsControl ItemsSource="{Binding Path=Connections}"
ItemTemplate="{StaticResource listTemplate}" />
текстовые поля все связываются правильно, и данные перемещаются между ними и ViewModel без труда. Это только комбобокс, который не работает.
вы правы в своем предположении относительно класса PhonebookEntry.
предположение, которое я делаю, заключается в том, что DataContext, используемый моим DataTemplate, автоматически устанавливается через иерархию привязки, так что мне не нужно явно устанавливать его для каждого элемента в ItemsControl. Мне это кажется немного глупым.
вот тестовая реализация, которая демонстрирует проблему, на основе приведенного выше примера.
XAML:
<Window x:Class="WpfApplication7.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="itemTemplate">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Path=Name}" Width="50" />
<ComboBox ItemsSource="{Binding Path=PhonebookEntries}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding Path=PhonebookEntry}"
Width="200"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Path=Connections}"
ItemTemplate="{StaticResource itemTemplate}" />
</Grid>
</Window>
The код:
namespace WpfApplication7
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
public class PhoneBookEntry
{
public string Name { get; set; }
public PhoneBookEntry(string name)
{
Name = name;
}
}
public class ConnectionViewModel : INotifyPropertyChanged
{
private string _name;
public ConnectionViewModel(string name)
{
_name = name;
IList<PhoneBookEntry> list = new List<PhoneBookEntry>
{
new PhoneBookEntry("test"),
new PhoneBookEntry("test2")
};
_phonebookEntries = new CollectionView(list);
}
private readonly CollectionView _phonebookEntries;
private string _phonebookEntry;
public CollectionView PhonebookEntries
{
get { return _phonebookEntries; }
}
public string PhonebookEntry
{
get { return _phonebookEntry; }
set
{
if (_phonebookEntry == value) return;
_phonebookEntry = value;
OnPropertyChanged("PhonebookEntry");
}
}
public string Name
{
get { return _name; }
set
{
if (_name == value) return;
_name = value;
OnPropertyChanged("Name");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class MainWindowViewModel
{
private readonly CollectionView _connections;
public MainWindowViewModel()
{
IList<ConnectionViewModel> connections = new List<ConnectionViewModel>
{
new ConnectionViewModel("First"),
new ConnectionViewModel("Second"),
new ConnectionViewModel("Third")
};
_connections = new CollectionView(connections);
}
public CollectionView Connections
{
get { return _connections; }
}
}
}
если вы запустите этот пример, вы получите поведение, о котором я говорю. Текстовое поле обновляет свою привязку штраф при редактировании его, но ComboBox не делает. Очень запутанный видя, как на самом деле единственное, что я сделал, это ввести Родительский ViewModel.
в настоящее время я работаю под впечатлением, что элемент привязан к ребенок с DataContext, что ребенок, как значение DataContext. Я не могу найти никакой документации, которая проясняет это так или иначе.
то есть,
Window - > DataContext = MainWindowViewModel
..Элементы - > привязка к DataContext.PhonebookEntries
....Item - > DataContext = PhonebookEntry (неявно связанный)
я не знаю, если это объясняет мое предположение лучше(?).
, чтобы подтвердить мое предположение, измените привязка текстового поля должна быть
<TextBox Text="{Binding Mode=OneWay}" Width="50" />
и это покажет корень привязки TextBox (который я сравниваю с DataContext) является экземпляром ConnectionViewModel.
4 ответов:
вы устанавливаете DisplayMemberPath и SelectedValuePath в "Name", поэтому я предполагаю, что у вас есть класс PhoneBookEntry с именем общедоступного свойства.
вы установили DataContext для вашего объекта ConnectionViewModel?
Я скопировал твой код и сделал некоторые незначительные изменения, и это, кажется, работает нормально. Я могу установить свойство viewmodels PhoneBookEnty и выбранный элемент в поле со списком изменений, и я могу изменить выбранный элемент в поле со списком и представлении свойство models PhoneBookEntry установлено правильно.
вот мой контент XAML:
<Window x:Class="WpfApplication6.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <StackPanel> <Button Click="Button_Click">asdf</Button> <ComboBox ItemsSource="{Binding Path=PhonebookEntries}" DisplayMemberPath="Name" SelectedValuePath="Name" SelectedValue="{Binding Path=PhonebookEntry}" /> </StackPanel> </Grid> </Window>и вот мой код:
namespace WpfApplication6 { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { public Window1() { InitializeComponent(); ConnectionViewModel vm = new ConnectionViewModel(); DataContext = vm; } private void Button_Click(object sender, RoutedEventArgs e) { ((ConnectionViewModel)DataContext).PhonebookEntry = "test"; } } public class PhoneBookEntry { public string Name { get; set; } public PhoneBookEntry(string name) { Name = name; } public override string ToString() { return Name; } } public class ConnectionViewModel : INotifyPropertyChanged { public ConnectionViewModel() { IList<PhoneBookEntry> list = new List<PhoneBookEntry>(); list.Add(new PhoneBookEntry("test")); list.Add(new PhoneBookEntry("test2")); _phonebookEntries = new CollectionView(list); } private readonly CollectionView _phonebookEntries; private string _phonebookEntry; public CollectionView PhonebookEntries { get { return _phonebookEntries; } } public string PhonebookEntry { get { return _phonebookEntry; } set { if (_phonebookEntry == value) return; _phonebookEntry = value; OnPropertyChanged("PhonebookEntry"); } } private void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; } }Edit: второй пример Джеффа, похоже, не работает, что кажется мне немного странным. Если Я измените свойство PhonebookEntries в ConnectionViewModel на тип ReadOnlyCollection, привязка TwoWay свойства SelectedValue на combobox работает нормально.
может быть проблема с CollectionView? Я заметил предупреждение в консоли вывода:
для привязки данных к ComboBox
List<ComboData> ListData = new List<ComboData>(); ListData.Add(new ComboData { Id = "1", Value = "One" }); ListData.Add(new ComboData { Id = "2", Value = "Two" }); ListData.Add(new ComboData { Id = "3", Value = "Three" }); ListData.Add(new ComboData { Id = "4", Value = "Four" }); ListData.Add(new ComboData { Id = "5", Value = "Five" }); cbotest.ItemsSource = ListData; cbotest.DisplayMemberPath = "Value"; cbotest.SelectedValuePath = "Id"; cbotest.SelectedValue = "2";ComboData выглядит так:
public class ComboData { public int Id { get; set; } public string Value { get; set; } }
У меня было то, что сначала казалось идентичной проблемой, но оказалось, что это связано с проблемой совместимости NHibernate/WPF. Проблема была вызвана тем, как WPF проверяет равенство объектов. Я был в состоянии получить мои вещи, чтобы работать с помощью идентификатора объекта недвижимости в SelectedValue и свойства SelectedValuePath.
<ComboBox Name="CategoryList" DisplayMemberPath="CategoryName" SelectedItem="{Binding Path=CategoryParent}" SelectedValue="{Binding Path=CategoryParent.ID}" SelectedValuePath="ID">смотрите сообщение в блоге от Честера,WPF ComboBox-SelectedItem, SelectedValue и SelectedValuePath с помощью NHibernate, для сведения.
У меня была аналогичная проблема, когда SelectedItem никогда не обновлялся.
моя проблема заключалась в том, что выбранный элемент не был тем же экземпляром, что и элемент, содержащийся в списке. Поэтому мне просто пришлось переопределить метод Equals () в моем MyCustomObject и сравнить идентификаторы этих двух экземпляров, чтобы сообщить ComboBox, что это один и тот же объект.
public override bool Equals(object obj) { return this.Id == (obj as MyCustomObject).Id; }
Comments