Ho un ComboBox che non sembra aggiornare SelectedItem / SelectedValue.
ComboBox ItemsSource è associato a una proprietà in una classe ViewModel che elenca un gruppo di voci della rubrica RAS come CollectionView. Poi ho legato (in tempi diversi) sia la SelectedItem
o SelectedValue
ad un'altra proprietà del ViewModel. Ho aggiunto un MessageBox nel comando save per eseguire il debug dei valori impostati dal database, ma il SelectedItem
/ SelectedValue
binding non viene impostato.
La classe ViewModel ha un aspetto simile al seguente:
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");
}
}
}
La collezione _phonebookEntries è stata inizializzata nel costruttore da un oggetto business. ComboBox XAML è simile al seguente:
<ComboBox ItemsSource="{Binding Path=PhonebookEntries}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding Path=PhonebookEntry}" />
Sono interessato solo al valore della stringa effettiva visualizzato nel ComboBox, non a qualsiasi altra proprietà dell'oggetto in quanto questo è il valore che devo trasmettere a RAS quando voglio effettuare la connessione VPN, quindi DisplayMemberPath
e SelectedValuePath
sono entrambi proprietà Name di il ConnectionViewModel. ComboBox è DataTemplate
applicato a ItemsControl
su una finestra in cui DataContext è stato impostato su un'istanza ViewModel.
ComboBox visualizza correttamente l'elenco degli elementi e posso selezionarne uno nell'interfaccia utente senza problemi. Tuttavia, quando visualizzo la finestra di messaggio dal comando, la proprietà PhonebookEntry contiene ancora il valore iniziale, non il valore selezionato da ComboBox. Altre istanze di TextBox si stanno aggiornando correttamente e vengono visualizzate in MessageBox.
Cosa mi manca con il databinding del ComboBox? Ho fatto molte ricerche e non riesco a trovare nulla di sbagliato.
Questo è il comportamento che sto vedendo, tuttavia non funziona per qualche motivo nel mio contesto particolare.
Ho un MainWindowViewModel che ha un CollectionView
ConnectionViewModels. Nel file MainWindowView.xaml code-behind, ho impostato DataContext su MainWindowViewModel. MainWindowView.xaml è ItemsControl
associato alla raccolta di ConnectionViewModels. Ho un DataTemplate che contiene ComboBox e alcuni altri TextBox. Le caselle di testo sono associate direttamente alle proprietà di ConnectionViewModel mediante 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; }
}
Il code-behind XAML:
public partial class Window1
{
public Window1()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
Quindi 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}" />
Le caselle di testo si legano tutte correttamente e i dati si spostano tra loro e ViewModel senza problemi. È solo il ComboBox che non funziona.
Sei corretto nel tuo assunto riguardo alla classe PhonebookEntry.
Il presupposto che sto facendo è che il DataContext utilizzato dal mio DataTemplate sia impostato automaticamente attraverso la gerarchia di associazione, in modo da non doverlo impostare esplicitamente per ogni elemento in ItemsControl
. Mi sembrerebbe un po 'sciocco.
Ecco un'implementazione di prova che dimostra il problema, basato sull'esempio sopra.
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>
Il code-behind :
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; }
}
}
}
Se esegui questo esempio, otterrai il comportamento di cui sto parlando. TextBox aggiorna la sua multa vincolante quando la modifichi, ma ComboBox no. Molto confuso visto che davvero l'unica cosa che ho fatto è introdurre un ViewModel genitore.
Attualmente sto lavorando con l'impressione che un elemento associato al figlio di un DataContext abbia quel figlio come DataContext. Non riesco a trovare alcuna documentazione che chiarisca questo in un modo o nell'altro.
Vale a dire,
Window -> DataContext = MainWindowViewModel
..Items -> Bound to DataContext.PhonebookEntries
.... Item -> DataContext = PhonebookEntry (implicitamente associato)
Non so se questo spieghi meglio la mia ipotesi (?).
Per confermare la mia ipotesi, modificare l'associazione di TextBox in modo che sia
<TextBox Text="{Binding Mode=OneWay}" Width="50" />
E questo mostrerà la radice dell'associazione TextBox (che sto confrontando con DataContext) è l'istanza di ConnectionViewModel.
<
T>
e nel getter di proprietà usando _list.AsReadOnly () simile al modo in cui hai menzionato. Funziona come avrei sperato avrebbe avuto il metodo originale. Inoltre, mi è venuto in mente che mentre l'associazione ItemsSource funzionava bene, avrei potuto usare la proprietà Current in ViewModel per accedere all'elemento selezionato in ComboBox. Tuttavia, non sembra naturale quanto vincolante la proprietà ComboBoxes SelectedValue / SelectedItem.