Tengo un ComboBox que no parece actualizar SelectedItem / SelectedValue.
ComboBox ItemsSource está vinculado a una propiedad en una clase ViewModel que enumera un montón de entradas de la agenda telefónica RAS como CollectionView. Luego he vinculado (en momentos separados) tanto la propiedad SelectedItem
como SelectedValue
a otra de ViewModel. He agregado un cuadro de mensaje en el comando guardar para depurar los valores establecidos por el enlace de datos, pero el enlace SelectedItem
/ SelectedValue
no se está configurando.
La clase ViewModel se parece a esto:
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 colección _phonebookEntries se está inicializando en el constructor desde un objeto comercial. El ComboBox XAML se parece a esto:
<ComboBox ItemsSource="{Binding Path=PhonebookEntries}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding Path=PhonebookEntry}" />
Solo estoy interesado en el valor de cadena real que se muestra en el cuadro combinado, no en ninguna otra propiedad del objeto, ya que este es el valor que necesito pasar a RAS cuando quiero hacer la conexión VPN, por lo tanto, DisplayMemberPath
y SelectedValuePath
son propiedad del nombre de ConnectionViewModel. El ComboBox se DataTemplate
aplica a un ItemsControl
en una ventana cuyo DataContext se ha establecido en una instancia de ViewModel.
ComboBox muestra la lista de elementos correctamente, y puedo seleccionar uno en la interfaz de usuario sin ningún problema. Sin embargo, cuando visualizo el cuadro de mensaje del comando, la propiedad PhonebookEntry todavía tiene el valor inicial, no el valor seleccionado de ComboBox. Otras instancias de TextBox se actualizan bien y se muestran en el MessageBox.
¿Qué me falta con el enlace de datos del ComboBox? He buscado mucho y parece que no puedo encontrar nada que esté haciendo mal.
Este es el comportamiento que estoy viendo, sin embargo, no funciona por alguna razón en mi contexto particular.
Tengo un MainWindowViewModel que tiene uno CollectionView
de ConnectionViewModels. En el código subyacente del archivo MainWindowView.xaml, configuré el DataContext en MainWindowViewModel. MainWindowView.xaml tiene un ItemsControl
enlace a la colección de ConnectionViewModels. Tengo un DataTemplate que contiene el ComboBox, así como algunos otros TextBoxes. Los cuadros de texto están vinculados directamente a las propiedades de ConnectionViewModel utilizando 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; }
}
El código subyacente de XAML:
public partial class Window1
{
public Window1()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
Entonces 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}" />
Todos los cuadros de texto se enlazan correctamente y los datos se mueven entre ellos y ViewModel sin problemas. Solo el ComboBox no funciona.
Tiene razón en su suposición con respecto a la clase PhonebookEntry.
La suposición que estoy haciendo es que el DataContext utilizado por mi DataTemplate se establece automáticamente a través de la jerarquía de enlace, por lo que no tengo que establecerlo explícitamente para cada elemento en el ItemsControl
. Eso me parecería un poco tonto.
Aquí hay una implementación de prueba que demuestra el problema, basado en el ejemplo anterior.
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>
El código subyacente :
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; }
}
}
}
Si ejecuta ese ejemplo, obtendrá el comportamiento del que estoy hablando. TextBox actualiza su enlace bien cuando lo edita, pero ComboBox no. Muy confuso, ya que lo único que he hecho es presentar un ViewModel padre.
Actualmente estoy trabajando bajo la impresión de que un elemento vinculado al elemento secundario de un DataContext tiene ese elemento secundario como su DataContext. No puedo encontrar ninguna documentación que aclare esto de una forma u otra.
Es decir,
Ventana -> DataContext = MainWindowViewModel
..Items -> Vinculado a DataContext.PhonebookEntries
.... Elemento -> DataContext = PhonebookEntry (asociado implícitamente)
No sé si eso explica mi suposición mejor (?).
Para confirmar mi suposición, cambie el enlace de TextBox para que sea
<TextBox Text="{Binding Mode=OneWay}" Width="50" />
Y esto mostrará que la raíz de enlace TextBox (que estoy comparando con el DataContext) es la instancia de ConnectionViewModel.
<
T>
y en el captador de propiedades usando _list.AsReadOnly () de forma similar a la que mencionaste. Está funcionando como hubiera esperado que hubiera tenido el método original. Además, me pasó por la mente que, aunque el enlace ItemsSource funcionaba bien, podría haber utilizado la propiedad Current en ViewModel para acceder al elemento seleccionado en ComboBox. Aún así, no se siente tan natural como vincular la propiedad ComboBoxes SelectedValue / SelectedItem.