202

SelectedItem/SelectedValue を更新していないように見える ComboBox があります。

ComboBox ItemsSource は、一連の RAS 電話帳エントリを CollectionView として一覧表示する ViewModel クラスのプロパティにバインドされています。次に、SelectedItemまたはの両方SelectedValueを ViewModel の別のプロパティに (別々の時間に) バインドしました。データバインディングによって設定された値をデバッグするために保存コマンドに 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 に渡す必要がある値であるためDisplayMemberPathですSelectedValuePath。 ConnectionViewModel。ComboBox は、DataContext が ViewModel インスタンスに設定されている Window にDataTemplate適用されます。ItemsControl

ComboBox はアイテムのリストを正しく表示し、UI で問題なくアイテムを選択できます。ただし、コマンドからメッセージ ボックスを表示すると、PhonebookEntry プロパティには ComboBox から選択された値ではなく、初期値が含まれています。他の TextBox インスタンスは正常に更新されており、MessageBox に表示されています。

ComboBox のデータバインドで何が欠けていますか? 私は多くの検索を行ってきましたが、間違っていることは何も見つからないようです。


これは私が見ている動作ですが、私の特定のコンテキストでは何らかの理由で機能していません。

CollectionViewConnectionViewModelsを持つ MainWindowViewModel があります。MainWindowView.xaml ファイルの分離コードで、DataContext を MainWindowViewModel に設定します。MainWindowView.xaml には、ItemsControlConnectionViewModels のコレクションへのバインドがあります。ComboBox と他のいくつかの TextBoxes を保持する DataTemplate があります。TextBoxes は、 を使用して ConnectionViewModel のプロパティに直接バインドされます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; }
}

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

TextBoxes はすべて正しくバインドされ、データは TextBox と 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 としてその子を持っているという印象の下で働いています。これを何らかの方法で解決するドキュメントが見つかりません。

すなわち、

Window -> DataContext =
MainWindowViewModel ..Items -> DataContext.PhonebookEntries にバインド
....Item -> DataContext = PhonebookEntry (暗黙的に関連付け)

それが私の仮定をよりよく説明するかどうかはわかりません(?)。


私の仮定を確認するために、TextBox のバインディングを次のように変更します。

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

これにより、TextBox バインディング ルート (DataContext と比較しています) が ConnectionViewModel インスタンスであることがわかります。

4

4 に答える 4

209

DisplayMemberPath と SelectedValuePath を "Name" に設定したので、パブリック プロパティ Name を持つクラス PhoneBookEntry があると仮定します。

DataContext を ConnectionViewModel オブジェクトに設定しましたか?

私はあなたのコードをコピーし、いくつかのマイナーな変更を加えましたが、問題なく動作しているようです。ビューモデルの PhoneBookEnty プロパティを設定すると、コンボボックスで選択した項目が変更され、コンボボックスで選択した項目を変更でき、ビューモデルの PhoneBookEntry プロパティが正しく設定されます。

ここに私の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>

そして、ここに私のコードビハインドがあります:

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

編集: Geoffs の 2 番目の例はうまくいかないようです。これは私には少し奇妙に思えます。ConnectionViewModel の PhonebookEntries プロパティを ReadOnlyCollection 型に変更すると、コンボボックスの SelectedValue プロパティの TwoWay バインディングが正常に機能します。

CollectionView に問題があるのでしょうか。出力コンソールに次の警告が表示されました。

System.Windows.Data 警告: 50 : CollectionView を直接使用することは完全にはサポートされていません。基本的な機能は機能しますが、非効率的ですが、高度な機能では既知のバグが発生する可能性があります。これらの問題を回避するには、派生クラスの使用を検討してください。

Edit2 (.NET 4.5): DropDownList のコンテンツは、DisplayMemberPath ではなく ToString() に基づくことができますが、DisplayMemberPath は、選択および表示された項目のみのメンバーを指定します。

于 2009-02-18T15:06:16.023 に答える
26

最初は同じ問題のように見えましたが、NHibernate/WPFの互換性の問題が原因であることが判明しました。この問題は、WPFがオブジェクトの同等性をチェックする方法が原因で発生しました。SelectedValueプロパティとSelectedValuePathプロパティのオブジェクトIDプロパティを使用して、自分のものを機能させることができました。

<ComboBox Name="CategoryList"
          DisplayMemberPath="CategoryName"
          SelectedItem="{Binding Path=CategoryParent}"
          SelectedValue="{Binding Path=CategoryParent.ID}"
          SelectedValuePath="ID">

詳細については、Chesterのブログ投稿、WPF ComboBox-SelectedItem、SelectedValue、およびSelectedValuePathwithNHibernateを参照してください。

于 2009-10-05T21:41:42.383 に答える
1

SelectedItem が更新されないという同様の問題がありました。

私の問題は、選択したアイテムがリストに含まれるアイテムと同じインスタンスではないことでした。そのため、MyCustomObject の Equals() メソッドをオーバーライドし、これら 2 つのインスタンスの ID を比較して、同じオブジェクトであることを ComboBox に伝えるだけで済みました。

public override bool Equals(object obj)
{
    return this.Id == (obj as MyCustomObject).Id;
}
于 2016-10-11T15:05:24.127 に答える