Ligar uma lista WPF a uma lista personalizada

Tenho uma caixa que não parece actualizar o valor SelectedItem/Selectededvalue.

o ItemsSource da ComboBox Está ligado a uma propriedade numa classe de modelos de visualização que lista um monte de itens da agenda RAS como uma vista de recolha. Então eu liguei (em momentos separados) tanto o SelectedItem ou SelectedValue a outra propriedade do modelo de visão. Eu adicionei uma MessageBox no comando save para depurar os valores definidos pela base de dados, mas a SelectedItem/SelectedValue a ligação não está a ser estabelecida.

a A classe ViewModel parece-se com isto:

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");
        }
    }
}

a colecção _fonebookentries está a ser inicializada no construtor a partir de um objecto comercial. O XAML de ComboBox parece-se com isto:

<ComboBox ItemsSource="{Binding Path=PhonebookEntries}"
    DisplayMemberPath="Name"
    SelectedValuePath="Name"
    SelectedValue="{Binding Path=PhonebookEntry}" />

só estou interessado no valor actual do texto mostrado na lista, não em quaisquer outras propriedades do objecto, dado que este é o valor que preciso de passar para RAS quando quero fazer a ligação VPN, por isso DisplayMemberPath e SelectedValuePath são ambos propriedade do nome do ConnectionViewModel. A ComboBox está em um DataTemplate aplicado a um ItemsControl em uma janela que o DataContext foi definido para uma instância de modelo de visualização.

o ComboBox mostra a lista de itens correctamente, e posso seleccionar um na interface sem problemas. No entanto, quando mostrar a caixa de mensagens do comando, a propriedade do 'PhonebookEntry' ainda tem o valor inicial nela, não o valor seleccionado da caixa de comandos. Outras instâncias do campo de texto estão atualizando bem e exibindo no Caixa de mensagens.

O que me está a escapar com a base de dados do ComboBox? Fiz muita pesquisa e não consigo encontrar nada que esteja a fazer de errado.


Este é o comportamento que estou a ver, no entanto, não está a funcionar por alguma razão no meu contexto particular.

Eu tenho um modelo de visão principal que tem um CollectionView de modelos de visão Conectiva. Na janela principal.código de ficheiro xaml por trás, configuro o ficheiro DataContext para o MainWindowViewModel. A Janela Principal.o xaml tem um ItemsControl ligado à colecção de modelos Connectionview. Eu tenho um DataTemplate que contém a ComboBox, bem como algumas outras caixas de texto. As caixas de texto estão ligadas diretamente às propriedades do ConnectionViewModel usando 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; }
}

o código XAML atrás:

public partial class Window1
{
    public Window1()
    {
        InitializeComponent();
        DataContext = new MainWindowViewModel();
    }
}

Então 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}" />

as caixas de texto Todas se ligam corretamente, e os dados se movem entre elas e o modelo de visualização sem problemas. É só o ComboBox que não está a funcionar.

suposição sobre a aula de Fonookentry.

a suposição que estou a fazer é que o DataContext usado pela minha DataTemplate é automaticamente definido através da hierarquia de encadernação, para que não tenha de o definir explicitamente para cada item da ItemsControl. Isso parece-me um pouco estúpido.


Aqui está uma implementação de teste que demonstra o problema, com base no exemplo acima.

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>

a Código atrás:

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 der esse exemplo, terá o comportamento de que estou a falar. A caixa de texto actualiza a sua multa de encadernação quando a edita, mas a caixa de comandos não. Muito confuso, visto que a única coisa que fiz foi apresentar um modelo de visão dos pais.

Estou actualmente a trabalhar com a impressão de que um item ligado à criança de um DataContext tem essa criança como seu DataContext. Não consigo encontrar nenhuma documentação que esclareça isto de uma forma ou de outra. restante.

ou seja,

Janela - > DataContext = MainWindowViewModel
..Items - > Bound to DataContext.Fonebookentries
....Item - > DataContext = Fonebookentry (implicitamente associado)

Não sei se isso explica melhor a minha suposição.).


para confirmar a minha suposição, mude a ligação da caixa de texto para

<TextBox Text="{Binding Mode=OneWay}" Width="50" />

e isto irá mostrar a raiz de ligação da caixa de texto (que estou a comparar com o DataContext) é a Instância ConnectionViewModel.

Author: Peter Mortensen, 2009-02-18

4 answers

Você configurou o 'DisplayMemberPath' e o 'SelectedValuePath' para 'Name', por isso assumo que você tem um 'phonebookentry' de classe com um nome de propriedade pública.

Já configuraste o DataContext para o teu objecto ConnectionViewModel? Copiei-te o código e fiz algumas pequenas modificações, e parece funcionar bem. Posso definir a propriedade dos modelos de visualização PhoneBookEnty e o item seleccionado no combobox muda, e posso alterar o item seleccionado no combobox e os modelos de visualização A propriedade do 'PhoneBookEntry' está configurada correctamente.

Aqui está o meu conteúdo 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>
E aqui está o meu código atrás:
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: geoffs segundo exemplo não parece funcionar, o que me parece um pouco estranho. Se eu mudar a propriedade do PhonebookEntries no modelo Connectionview para ser do tipo ReadOnlyCollection , a ligação em dois sentidos da propriedade SelectedValue no combobox funciona bem.

Talvez haja algum problema com a colectividade? Eu notei um aviso na consola de saída:

Sistema.Janela.Aviso de dados: 50: a utilização directa da vista de recolha não é totalmente suportada. As características básicas funcionam, embora com algumas ineficiências, mas recursos avançados podem encontrar bugs conhecidos. Considere o uso de uma classe derivada para evitar estes problemas.

Edit2 (. net 4.5): o conteúdo da lista DropDownList pode ser baseado no ToString () e não no DisplayMemberPath, enquanto o DisplayMemberPath especifica o membro para o apenas o item seleccionado e apresentado.

 168
Author: Kjetil Watnedal, 2018-04-19 13:25:26

Para ligar os dados à 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";

O ComboData parece:

public class ComboData
{ 
  public int Id { get; set; } 
  public string Value { get; set; } 
}
 65
Author: Roy, 2016-08-10 07:24:35
Tive o que inicialmente parecia ser um problema idêntico, mas acabou por ser devido a um problema de compatibilidade NHibernate/WPF. O problema foi causado pela forma como WPF verifica a igualdade de objetos. Consegui pôr as minhas coisas a funcionar usando a propriedade object ID no valor Selected e SelectedValuePath properties.
<ComboBox Name="CategoryList"
          DisplayMemberPath="CategoryName"
          SelectedItem="{Binding Path=CategoryParent}"
          SelectedValue="{Binding Path=CategoryParent.ID}"
          SelectedValuePath="ID">

Veja o post do blogue de Chester, the WPF ComboBox-SelectedItem, SelectedValue, and SelectedValuePath with NHibernate, para informacao.

 22
Author: CyberMonk, 2018-04-19 13:32:20
Tive um problema semelhante em que o SelectedItem nunca foi actualizado.

O meu problema era que o item seleccionado não era a mesma instância que o item contido na lista. Então eu simplesmente tive que anular o método Equals() em meu MyCustomObject e comparar os IDs dessas duas instâncias para dizer à ComboBox que é o mesmo objeto.

public override bool Equals(object obj)
{
    return this.Id == (obj as MyCustomObject).Id;
}
 1
Author: phifi, 2018-04-19 13:34:40