WPF MVVM ComboBox SelectedItem或SelectedValue无法正常工作。

44

更新

经过一番调查,问题似乎是在Item源加载完成之前就发生了SelectedValue / SelectedItem。如果我在断点中等待几秒钟,它就像预期的那样工作。不知道怎么解决这个问题。

更新结束

我有一个使用WPF和MVVM的应用程序,并使用ComboBox。以下是ViewModel示例。我遇到的问题是,当我们离开页面并返回时,ComboBox未选择当前选定的值。

ViewModel

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有两个属性(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无法选择值。
我不确定这是否只是ComboBox和MVVM模式工作方式的问题。我们正在进行的文本框绑定是正确的。
19个回答

47

在我这里,设置IsSynchronizedWithCurrentItem="True"起作用了!


1
谢谢Wim。我已经为这个问题苦苦挣扎了40分钟。 - sander
2
一直在苦苦挣扎,但这对我非常有效。谢谢 :) - Tal Even-Tov
1
这个小问题花了我太长时间,这个方法对我有用!非常感谢! - AsPas
1
简单而完美地解决了问题。找了大约4个小时才找到这个方法。谢谢! - Amadeus
1
5个小时的研究让我找到了这个。请享用一个虚拟饼干。 - Thomas Uchiha
显示剩余4条评论

27

您是否尝试在您的视图模型中实现INotifyPropertyChanged,并在设置SelectedItem时引发PropertyChanged事件?

如果这本身无法解决问题,则当导航回页面时,您将能够手动引发PropertyChanged事件,这应足以使WPF重新绘制自身并显示正确的选定项目。


我们的实际实现继承自一个实现INotifyPropertyChanged的ViewBase类。我正在引发PropertyChange事件,但组合框仍然为空白。 - cjibo
如果我设置断点并稍等一会儿,它甚至更好地工作。我认为我的selecteditem是在combobox的绑定完成之前设置的。唉。 - cjibo
1
我忘记在导航回页面时引发PropertyChanged事件。 - cjibo

22

你需要在设置SelectedItem属性之前设置ItemsSource属性。我几天前看到博客提到了这个问题。


非常感谢您,我一直在为我的XAML或视图模型出了什么问题而苦恼,原来是这个问题。 - ShadowLiberal
博士,好答案! - dibs487
在VS 15.8.1上无法工作,@rp7799的答案完美解决了问题。 - Juan Pablo Gomez

11
在这种情况下,选定的绑定项无法工作,因为对象的哈希 ID 不同。
一个可能的解决方案是:
根据选定的项 ID,在项源集合上恢复对象,并将选定的项属性设置为该对象。
例如:
<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"); 
                }
            }
        }            
    }

希望这能帮到你。 我花了很多时间搜寻答案,但找不到。


11

我曾经遇到过类似的问题,后来通过确保正确实现了IEquatable接口解决了。当绑定发生时,它会尝试查看对象是否匹配,因此请确保正确实现了相等性检查。


我现在也尝试过了。我在MyObject上实现了IEquatable接口,匹配了.id字段并返回true。但这也没有效果。如果我添加一个绑定到该属性的文本框,在返回页面时它会显示正确的信息。 - cjibo

4
当离开当前页面时,与ComboBox的ItemsSource属性相关联的CollectionView会被清除。由于ComboBox的IsSyncronizedWithCurrent属性默认为true,因此SelectedItem和SelectedValue属性将被重置。 这似乎是绑定中的内部数据类型问题。正如其他人建议的那样,如果您使用SelectedValue并将其绑定到ViewModel上的int属性,则它将起作用。 一个快捷方式是覆盖MyObject上的Equals运算符,以便比较两个MyObjects时实际比较的是Id属性。
另一个提示:如果您重构了ViewModel并使用SelectedValue,请仅在SelectedValuePath=Id(其中Id为int)时使用它。如果使用字符串键,请将其绑定到ComboBox的Text属性而不是SelectedValue属性。

2
我曾遇到过非常类似的问题。这个问题的原因在于ItemsSource绑定到了RelativeSource,而SelectedItem/SelectedValue直接绑定到了DataContext。虽然我已经解决了它,但是这个问题的原因并不清楚。为了帮助遇到同样问题的人,在这里给出一篇描述:http://stackoverflow.com/questions/33739513/wpf-mvvm-combobox-selectedvalue-is-cleared-when-navigating-away - Bruno V

3

我遇到了一个问题,就是ComboBox显示颜色列表(List<Brush>)时,选择颜色后在关闭下拉框时并未显示该颜色(尽管属性已被更改!)

解决方法是重写ComboBox中所选对象类型(Brush)的Equals(object obj)方法。但是由于Brush是sealed,所以这并不简单。因此我编写了一个包含Brush且实现了Equals方法的类EqualityBrush

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

使用新的EqualityBrush类的列表取代普通的Brush类解决了问题!

我的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 中的“Brush”属性现在必须是 EqualityBrush 类型!

3

我之前也注意到过这种行为。我发现SelectedIndex属性不会导致相同的错误。如果您可以重构ViewModel以公开所选项目的索引,并绑定到该索引,那么您应该可以顺利进行。


2

我对这个问题有一个非常简单的答案。首先,在视图中添加以下代码:IsSynchronizedWithCurrentItem="True"。

接下来,每当您在ViewModel中为该属性分配新对象时,SelectedObject应保存到该属性而不是私有成员。

ViewModel属性应如下所示:

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

这应该修复了问题。

2

我曾经遇到过同样的问题。问题在于,选定的项目不知道应该从集合中使用哪个对象。因此,您需要告诉选定的项目使用集合中的项目。

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

我希望这能有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接