У мене є ComboBox, який, схоже, не оновлює SelectedItem / SelectedValue.
ComboBox ItemsSource пов'язаний із властивістю класу ViewModel, у якому перелічено купу записів телефонної книги RAS як CollectionView. Тоді я зв'язав (в окремий час) і те, SelectedItem
або SelectedValue
інше властивість ViewModel. Я додав MessageBox до команди збереження для налагодження значень, встановлених прив'язкою даних, але 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 ініціалізується в конструкторі з бізнес-об'єкта. ComboBox XAML виглядає приблизно так:
<ComboBox ItemsSource="{Binding Path=PhonebookEntries}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding Path=PhonebookEntry}" />
Мене цікавить лише фактичне значення рядка, відображене в ComboBox, а не будь-які інші властивості об'єкта, оскільки це значення, яке мені потрібно передати RAS, коли я хочу встановити VPN-з'єднання, отже, DisplayMemberPath
і SelectedValuePath
вони є властивістю Name ConnectionViewModel. ComboBox DataTemplate
застосовується до ItemsControl
вікна, який DataContext був встановлений для екземпляра ViewModel.
ComboBox відображає список елементів правильно, і я можу вибрати його в інтерфейсі без проблем. Однак, коли я показую вікно повідомлення з команди, властивість PhonebookEntry все ще має в ньому початкове значення, а не вибране значення з ComboBox. Інші екземпляри TextBox оновлюються штрафом і відображаються в MessageBox.
Що мені не вистачає при зв’язуванні даних ComboBox? Я багато шукав і, здається, не знаходжу нічого, що я роблю неправильно.
Таку поведінку я бачу, однак вона не працює чомусь у моєму конкретному контексті.
У мене є MainWindowViewModel, який має CollectionView
ConnectionViewModels. У коді за файлом MainWindowView.xaml я встановив DataContext на MainWindowViewModel. MainWindowView.xaml ItemsControl
пов'язаний з колекцією ConnectionViewModels. У мене є DataTemplate, який містить ComboBox, а також деякі інші TextBoxes. TextBoxes прив'язані безпосередньо до властивостей 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 позаду:
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}" />
Усі TextBoxes прив’язуються правильно, і дані переміщуються між ними та ViewModel без проблем. Тільки ComboBox не працює.
Ви правильні у своїх припущеннях щодо класу 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>
За кодом :
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; }
}
}
}
Якщо запустити цей приклад, ви отримаєте поведінку, про яку я говорю. Під час редагування TextBox оновлює свій обов'язковий штраф, але ComboBox цього не робить. Дуже заплутано бачити, як насправді єдине, що я зробив, це запровадити батьківський ViewModel.
Наразі я працюю під враженням, що елемент, прив'язаний до дочірніх даних DataContext, має цю дитину як свій DataContext. Я не можу знайти жодної документації, яка так чи інакше очищає це питання.
Тобто,
Вікно -> DataContext =
MainWindowViewModel ..Items -> Зв'язане до DataContext.PhonebookEntries
.... Елемент -> DataContext = Телефонна книга (Включено неявно)
Я не знаю, чи це пояснює моє припущення краще (?).
Щоб підтвердити моє припущення, змініть прив'язку TextBox до
<TextBox Text="{Binding Mode=OneWay}" Width="50" />
І це покаже корінь прив'язки TextBox (який я порівнюю з DataContext) - це екземпляр ConnectionViewModel.
<
T>
та в Getter властивості за допомогою _list.AsReadOnly (), аналогічно тому, як ви згадали. Це працює так, як я б сподівався, що оригінальний метод матиме. Крім того, мені зрозуміло, що в той час, як прив'язка ItemSource працює нормально, я могла просто використати властивість Поточний у ViewModel для доступу до вибраного елемента в ComboBox. Однак це не так природно, як зв'язування властивості ComboBoxes SelectedValue / SelectedItem.