Saya memiliki ComboBox yang sepertinya tidak memperbarui SelectedItem / SelectedValue.
ComboBox ItemsSource terikat ke properti pada kelas ViewModel yang mencantumkan sekelompok entri buku telepon RAS sebagai CollectionView. Kemudian saya terikat (pada waktu yang terpisah) baik ke SelectedItem
atau SelectedValue
ke properti lain dari ViewModel. Saya telah menambahkan MessageBox ke dalam perintah save untuk men-debug nilai yang ditetapkan oleh penyatuan data, tetapi SelectedItem
/ SelectedValue
binding tidak diatur.
Kelas ViewModel terlihat seperti ini:
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");
}
}
}
Koleksi _phonebookEntries sedang diinisialisasi dalam konstruktor dari objek bisnis. ComboBox XAML terlihat seperti ini:
<ComboBox ItemsSource="{Binding Path=PhonebookEntries}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding Path=PhonebookEntry}" />
Saya hanya tertarik pada nilai string aktual yang ditampilkan di ComboBox, bukan properti lain dari objek karena ini adalah nilai yang harus saya sampaikan kepada RAS ketika saya ingin membuat koneksi VPN, karenanya DisplayMemberPath
dan SelectedValuePath
keduanya merupakan properti Nama dari ConnectionViewModel. ComboBox DataTemplate
diterapkan pada ItemsControl
pada Window yang DataContext telah diatur ke instance ViewModel.
ComboBox menampilkan daftar item dengan benar, dan saya dapat memilih satu di UI tanpa masalah. Namun ketika saya menampilkan kotak pesan dari perintah, properti PhonebookEntry masih memiliki nilai awal di dalamnya, bukan nilai yang dipilih dari ComboBox. Contoh TextBox lainnya memperbarui baik dan ditampilkan di MessageBox.
Apa yang saya lewatkan dengan menyatukan ComboBox? Saya telah melakukan banyak pencarian dan sepertinya tidak dapat menemukan kesalahan yang saya lakukan.
Ini adalah perilaku yang saya lihat, tetapi itu tidak berhasil karena alasan tertentu dalam konteks khusus saya.
Saya memiliki MainWindowViewModel yang memiliki CollectionView
ConnectionViewModels. Dalam file kode MainWindowView.xaml di belakang, saya mengatur DataContext ke MainWindowViewModel. MainWindowView.xaml memiliki ItemsControl
ikatan dengan koleksi ConnectionViewModels. Saya memiliki DataTemplate yang menampung ComboBox dan juga beberapa TextBox lainnya. TextBox terikat langsung ke properti ConnectionViewModel menggunakan 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; }
}
Kode XAML di belakang:
public partial class Window1
{
public Window1()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
Kemudian 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}" />
Semua TextBox mengikat dengan benar, dan data bergerak di antara mereka dan ViewModel tanpa masalah. Hanya ComboBox yang tidak berfungsi.
Anda benar dalam asumsi Anda mengenai kelas Buku telepon.
Asumsi yang saya buat adalah bahwa DataContext yang digunakan oleh DataTemplate saya secara otomatis diatur melalui hierarki yang mengikat, sehingga saya tidak perlu mengaturnya secara eksplisit untuk setiap item dalam ItemsControl
. Bagi saya itu agak konyol.
Berikut ini adalah implementasi tes yang menunjukkan masalah, berdasarkan contoh di atas.
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 kode-belakang :
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; }
}
}
}
Jika Anda menjalankan contoh itu, Anda akan mendapatkan perilaku yang saya bicarakan. TextBox memperbarui denda pengikatan saat Anda mengeditnya, tetapi ComboBox tidak. Sangat membingungkan melihat sebagai satu-satunya hal yang saya lakukan adalah memperkenalkan ViewModel induk.
Saya saat ini bekerja di bawah kesan bahwa item yang terikat pada anak dari DataContext memiliki anak itu sebagai DataContext-nya. Saya tidak dapat menemukan dokumentasi yang membersihkan ini dengan satu atau lain cara.
Yaitu,
Window -> DataContext = MainWindowViewModel
..Items -> Bound ke DataContext.PhonebookEntries
.... Item -> DataContext = PhonebookEntry (terkait secara implisit)
Saya tidak tahu apakah itu menjelaskan asumsi saya lebih baik (?).
Untuk mengkonfirmasi asumsi saya, ubah pengikatan TextBox menjadi
<TextBox Text="{Binding Mode=OneWay}" Width="50" />
Dan ini akan menunjukkan akar pengikatan TextBox (yang saya bandingkan dengan DataContext) adalah instance ConnectionViewModel.
<
T>
dan di pengambil properti menggunakan _list.AsReadOnly () mirip dengan cara yang Anda sebutkan. Ini bekerja seperti yang saya harapkan metode asli akan melakukannya. Selain itu, terlintas dalam pikiran saya bahwa sementara pengikatan ItemsSource bekerja dengan baik, saya bisa saja menggunakan properti Current di ViewModel untuk mengakses item yang dipilih di ComboBox. Namun, itu tidak terasa alami seperti mengikat properti ComboBoxes SelectedValue / SelectedItem.