[c#] 將WPF組合框綁定到自定義列表



Answers

將數據綁定到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";

ComboData看起來像:

public class ComboData
{ 
  public int Id { get; set; } 
  public string Value { get; set; } 
}
Question

我有一個ComboBox似乎沒有更新SelectedItem / SelectedValue。

ComboBox ItemsSource綁定到ViewModel類的屬性,該類將一大堆RAS電話簿條目列為CollectionView。 然後我將SelectedItemSelectedValue綁定到ViewModel的另一個屬性(在不同的時間)。 我在save命令中添加了一個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中顯示的實際字符串值感興趣,而不是對象的任何其他屬性,因為這是我想要建立VPN連接時需要傳遞給RAS的值,因此DisplayMemberPathSelectedValuePath都是Name ConnectionViewModel的屬性。 ComboBox位於DataTemplate中,該DataTemplate應用於窗口上的ItemsControl ,該窗口的DataContext已設置為ViewModel實例。

ComboBox顯示正確的項目列表,我可以在UI中選擇一個沒有問題的項目。 但是,當我從命令顯示消息框時,PhonebookEntry屬性仍然具有初始值,而不是ComboBox中的選定值。 其他TextBox實例正在更新並顯示在MessageBox中。

數據綁定ComboBox時我錯過了什麼? 我做了很多搜索,似乎無法找到任何我做錯的事情。

這是我看到的行為,但是由於某種原因,在我的特定環境下它不起作用。

我有一個MainWindowViewModel,它具有ConnectionViewModels的CollectionView 。 在MainWindowView.xaml代碼隱藏文件中,我將DataContext設置為MainWindowViewModel。 MainWindowView.xaml具有綁定到ConnectionViewModels集合的ItemsControl 。 我有一個DataTemplate包含組合框以及其他一些文本框。 TextBoxes使用Text="{Binding Path=ConnectionName}"直接綁定到ConnectionViewModel的屬性。

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}" />

文本框全部綁定正確,並且數據在它們和ViewModel之間移動並沒有麻煩。 只有ComboBox沒有工作。

你在關於PhonebookEntry類的假設中是正確的。

我所做的假設是我的DataTemplate使用的DataContext是通過綁定層次自動設置的,所以我不必為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
.... Item - > DataContext = PhonebookEntry(隱式關聯)

我不知道這是否能更好地解釋我的假設(?)。

為了確認我的假設,將TextBox的綁定更改為

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

這將顯示TextBox綁定根(我與DataContext比較)是ConnectionViewModel實例。




我有一個類似的問題,SelectedItem從未得到更新。

我的問題是選定的項目與列表中包含的項目不同。 所以我只需重寫我的MyCustomObject中的Equals()方法並比較這兩個實例的ID以告訴ComboBox它是同一個對象。

public override bool Equals(object obj)
{
    return this.Id == (obj as MyCustomObject).Id;
}



Related