web-dev-qa-db-ja.com

WPF MVVM ComboBox SelectedItemまたはSelectedValueが機能しない

更新

少し調査した後。問題のように思われるのは、Itemソースの読み込みが完了する前にSelectedValue/SelectedItemが発生していることです。ブレークポイントに座って数秒待つと、期待どおりに機能します。これをどのように回避するかわかりません。

更新の終了

MVVMとComboBoxを使用してWPFで使用するアプリケーションがあります。以下は、ViewModelの例です。私が抱えている問題は、ページを離れてComboBoxに戻るときに、現在選択されている値が選択されていないことです。

モデルの表示

public class MyViewModel
{
     private MyObject _selectedObject;
     private Collection<Object2> _objects;
     private IModel _model;

     public MyViewModel(IModel model)
    {
         _model = model;
         _objects = _model.GetObjects();
    }

    public Collection<MyObject> Objects
    {
         get
         {
              return _objects;
         }
         private set
         {
              _objects = value;
         }
     }

     public MyObject SelectedObject
     {
          get
          {
              return _selectedObject;
          }
          set
          {
               _selectedObject = value;
          }
      }
 }

この例のために、MyObjectには2つのプロパティ(TextとId)があるとしましょう。 ComboBoxのXAMLは次のようになります。

XAML

<ComboBox Name="MyComboBox" Height="23"  Width="auto" 
    SelectedItem="{Binding Path=SelectedObject,Mode=TwoWay}" 
    ItemsSource="{Binding Objects}"
    DisplayMemberPath="Text"
    SelectedValuePath="Id">

どの方法でページに戻ってオブジェクトを再構築するときにこれを構成しても、ComboBoxは値を選択しません。ただし、オブジェクトはプロパティのgetを介して正しいオブジェクトを返します。

これがComboBoxとMVVMパターンの動作の問題に過ぎないかどうかはわかりません。行っているテキストボックスバインドは正しく機能します。

40
cjibo

ビューモデルにINotifyPropertyChangedを実装して、PropertyChangedが設定されたときにSelectedItemイベントを発生させましたか?

これで問題が解決しない場合は、ページに戻るときに手動でPropertyChangedイベントを発生させることができます。これで、WPFが自分自身を再描画して正しい選択を表示するのに十分なはずです項目。

27
Orion Edwards

設定IsSynchronizedWithCurrentItem="True"私のために働いた!

30
rp7799

SelectedItemプロパティの前にItemsSourceプロパティを配置する必要があります。数日前にこの問題について言及しているブログに出会いました。

21
GuestPerson

この場合、オブジェクトのハッシュIDが異なるため、selecteditemバインドは機能しません。

1つの可能な解決策は次のとおりです。

選択されたアイテムIDに基づいて、itemsourceコレクションのオブジェクトを復元し、選択されたアイテムプロパティをそれに設定します。

例:

<ctrls:ComboBoxControlBase SelectedItem="{Binding Path=SelectedProfile, Mode=TwoWay}" ItemsSource="{Binding Path=Profiles, Mode=OneWay}" IsEditable="False" DisplayMemberPath="Name" />

ItemSourceにバインドされるプロパティは次のとおりです。

public ObservableCollection<Profile> Profiles
{
   get { return this.profiles; }
   private set { profiles = value; RaisePropertyChanged("Profiles"); }
}

SelectedItemにバインドされるプロパティは次のとおりです。

public Profile SelectedProfile 
{
    get { return selectedProfile; }
    set
    {
        if (this.SelectedUser != null)
        {
            this.SelectedUser.Profile = value; 
            RaisePropertyChanged("SelectedProfile");  
        } 
    } 
}

回復コードは次のとおりです。

    [Command("SelectionChanged")]
    public void SelectionChanged(User selectedUser)
    {
        if (selectedUser != null)
        {
            if (selectedUser is User)
            {
                if (selectedUser.Profile != null)
                {
                    this.SelectedUser = selectedUser;
                    this.selectedProfile = this.Profiles.Where(p => p.Id == this.SelectedUser.Profile.Id).FirstOrDefault();
                    MessageBroker.Instance.NotifyColleagues("ShowItemDetails"); 
                }
            }
        }            
    }

それがあなたのお役に立てば幸いです。答えを探すのに多くの時間を費やしましたが、見つけることができませんでした。

10
Breno P. Lucena

現在のページを離れると、CollectionViewItemsSourceプロパティに関連付けられているComboBoxが削除されます。 ComboBoxIsSyncronizedWithCurrentプロパティはデフォルトでtrueであるため、SelectedItemおよびSelectedValueプロパティはリセットされます。
これは、バインディングの内部データ型の問題のようです。上記で他の人が提案したように、ビューモデルのintプロパティにバインドする代わりにSelectedValueを使用すると、動作します。ショートカットは、MyObjectのEquals演算子をオーバーライドして、2つのMyObjectを比較するときに実際のIdプロパティが比較されるようにすることです。

別のヒント:ビューモデルを再構築してSelectedValueを使用する場合は、SelectedValuePath=Idここで、Idintです。文字列キーを使用する場合は、Textの代わりにComboBoxSelectedValueプロパティにバインドします。

4
Dave

以前にもこの動作に気づきました。 SelectedIndexプロパティが同じバグを引き起こさないことに気付きました。 ViewModelを再構築して、選択したアイテムのインデックスを公開し、それにバインドできる場合は、準備ができているはずです。

3
Abe Heidebrecht

同じ問題がありました。事はです。選択したアイテムは、コレクションからどのオブジェクトを使用する必要があるかを知りません。そのため、コレクションのアイテムを使用するには、選択したアイテムに発言する必要があります。

public MyObject SelectedObject
 {
      get
      {
          Objects.find(x => x.id == _selectedObject.id)
          return _selectedObject;
      }
      set
      {
           _selectedObject = value;
      }
 }

これがお役に立てば幸いです。

2
StefanHa

この問題に対する非常に簡単な答えがあります。最初に、次のコードをビューIsSynchronizedWithCurrentItem = "True"に追加します。

次に、ViewModelの新しいオブジェクトをそのプロパティに割り当てるときはいつでも、SelectedObjectはプライベートメンバーではなく、そのプロパティに保存する必要があります。

ビューモデルプロパティは次のようになります。

    public Role SelectedObject 
    {
        get { return object; }
        set
        {
            if (value != null)
            {
                if (!object.Equals(value))
                {
                    object = value;
                    OnPropertyChanged(() => SelectedObject );
                }
            }
        }
    }

これで問題が解決するはずです。

2
Tim

私はしばらくの間この問題と戦っていました。私の場合、アイテムソースとして複合型(リスト)を使用し、選択した値としてKeyTypeを使用していました。ロードイベントで、KeyTypeがnullに設定されていました。これにより、すべてが壊れました。キーが変更されても、サブ要素は更新されません。 KeyTypeの提案された値がnullでないことを確認するチェックを追加すると、すべてが期待どおりに機能することが判明しました。

    #region Property: SelectedKey
    // s.Append(string.Format("SelectedKey : {0} " + Environment.NewLine, SelectedKey.ToString()));

    private KeyType _SelectedKey = new KeyType();
    public KeyType SelectedKey
    {
        get { return _SelectedKey; }
        set
        {
            if(value != null )
                if (!_SelectedKey.Equals(value))
                {
                    _SelectedKey = value;
                    OnPropertyChanged("SelectedKey");
                }
        }
    }
    #endregion SelectedKey
1
Scott McFadden

UserControl_Loadedイベントにディスパッチャを追加することで問題を解決しました

 Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
 {
     combobox.SelectedIndex = 0;
 }));
1
Peter T.

SelectedValuePathSelectedValueの型は完全に同じでなければなりません。

たとえば、SelectedValuePathのタイプがInt16およびSelectedValueにバインドするプロパティのタイプはintであり、機能しません。

私はそれを見つけるのに何時間も費やします、そしてそれは私が質問が尋ねられた多くの時間後にここで答えている理由です。同じ問題を抱えている私のような他の貧しい人がそれを見ることができるかもしれません。

1
Dummy01

色のリストを表示するComboBoxでこの問題が発生しました(List <Brush>)。
色の選択は可能でしたが、選択が閉じられたときに表示されませんでした(ただし、プロパティは変更されました!)

修正は、ComboBox(ブラシ)で選択されたタイプのEquals(object obj)メソッドを上書きしていました。だから私はクラスを書いた EqualityBrush Brushを含み、Equalsを実装します:

public class EqualityBrush
{
    public SolidColorBrush Brush { get; set; }

    public override bool Equals(object o)
    {
        if (o is EqualityBrush)
        {
            SolidColorBrush b = ((EqualityBrush)o).Brush;
            return b.Color.R == this.Brush.Color.R && b.Color.G == this.Brush.Color.G && b.Color.B == this.Brush.Color.B;
        }
        else
            return false;
    }
}

通常のBrushクラスの代わりに新しいEqualityBrushクラスのリストを使用すると、問題が修正されました。

私のコンボボックス XAML 次のようになります。

<ComboBox ItemsSource="{Binding BuerkertBrushes}" SelectedItem="{Binding Brush, Mode=TwoWay}" Width="40">
    <ComboBox.Resources>
        <DataTemplate DataType="{x:Type tree:EqualityBrush}">
            <Rectangle Width="20" Height="12" Fill="{Binding Brush}"/>
        </DataTemplate>
    </ComboBox.Resources>
</ComboBox>

私の「ブラシ」プロパティが ViewModel タイプEqualityBrushでなければなりません!

1
JCH2k

Loadedイベントを使用:

private void cmb_Loaded(object sender, RoutedEventArgs e) {
    if (cmb.Items.Count > 0) cmb.SelectedIndex = 0;          
}

わたしにはできる。

0
Vinicius

IsSyncronizedWithCurrent = Falseが機能します。

0
Rohit

DataContextをページに適用する方法である可能性があります。 WPFでは、ページに移動するたびに、すべてが再初期化され、コンストラクターが呼び出され、メソッドが読み込まれます。したがって、ビュー内でDataContextを設定している場合、ユーザーが選択したSelectedItemを吹き飛ばすことは間違いありません。それを回避するには、ページのKeepAliveプロパティを使用します。

<Page KeepAlive="True" ...>
   ...
</Page>

これにより、既にアクセスしたページに戻るときにLoadedイベントのみが発生します。そのため、Loadではなく、Initialize(外部またはコンストラクター内)でDataContextを設定していることを確認する必要があります。

ただし、これはページのそのインスタンスに対してのみ機能します。そのページの新しいインスタンスに移動すると、コンストラクタが再度呼び出されます。

0
markti

ComboBox.SelectionBoxItem.ToString()

0
Sean Campbell