【问题标题】:WPF MVVM ComboBox SelectedItem or SelectedValue not workingWPF MVVM ComboBox SelectedItem 或 SelectedValue 不起作用
【发布时间】:2010-10-14 09:56:41
【问题描述】:

更新

经过一番调查。问题似乎是 SelectedValue/SelectedItem 在项目源完成加载之前发生。如果我坐在断点处等待几秒钟,它会按预期工作。不知道我将如何解决这个问题。

结束更新

我有一个在 WPF 中使用 MVVM 和 ComboBox 的应用程序。下面是 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 有两个属性(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 模式工作方式的问题。我们正在做的文本框绑定工作正常。

【问题讨论】:

    标签: wpf mvvm combobox


    【解决方案1】:

    设置 IsSynchronizedWithCurrentItem="True" 对我有用!

    【讨论】:

    • 谢谢维姆。已经为此苦苦挣扎了 40 分钟。
    • 一直在努力解决这个问题。这对我来说非常有效。谢谢:)
    • 在这个小问题上花了太长时间,这对我有用!非常感谢!
    • 简单而完美。花了大约4个小时才找到这个。谢谢!
    【解决方案2】:

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

    如果这本身不能解决问题,那么您将能够在导航回页面时自己手动引发PropertyChanged 事件,这足以让 WPF 重绘自身并显示正确的选择项目。

    【讨论】:

    • 我们的实际实现继承自实现 INotifyPropertyChanged 的​​ ViewBase 类。我正在提高 PropertyChange,但组合框仍为空白。
    • 如果我放入断点并稍等片刻,效果会更好。我认为我的 selecteditem 在组合框的绑定完成之前就已经设置好了。呃。
    • 我忘记在导航回页面时引发 PorpertyChanged 事件。
    【解决方案3】:

    您需要将 ItemsSource 属性放在 SelectedItem 属性之前。几天前我看到一个博客提到了这个问题。

    【讨论】:

    • 非常感谢,我一直在努力找出我的 XAML 或视图模型做错了什么,而一直都是这样。
    • 波什,很好的答案!
    • 不适用于 VS 15.8.1,@rp7799 的回答完美。
    【解决方案4】:

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

    【讨论】:

    • 我现在也试过了。我在 MyObject 上实现了 IEquatable 并匹配 .id 字段并返回 true。那也没有效果。如果我添加一个绑定到属性的文本框,它会在返回页面时显示正确的信息。
    【解决方案5】:

    在这种情况下,selecteditem绑定不起作用,因为对象的hash id不同。

    一种可能的解决方案是:

    根据选中项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"); 
                    }
                }
            }            
        }
    

    希望对你有帮助。 我花了很多时间寻找答案,但我找不到。

    【讨论】:

      【解决方案6】:

      离开当前页面时,与ComboBoxItemsSource 属性关联的CollectionView 将被清除。因为ComboBoxIsSyncronizedWithCurrent属性默认为真,SelectedItemSelectedValue属性被重置。
      这似乎是绑定中的内部数据类型问题。正如上面其他人建议的那样,如果您使用SelectedValue 而不是通过绑定到视图模型上的 int 属性,它将起作用。 一个捷径是覆盖 MyObject 上的 Equals 运算符,以便在比较两个 MyObject 时,比较实际的 Id 属性。

      另一个提示:如果您确实重组了您的视图模型并使用SelectedValue,请仅在SelectedValuePath=Id 时使用它,其中Idint。如果使用字符串键,则绑定到ComboBoxText 属性而不是SelectedValue

      【讨论】:

      • 我遇到了一个非常相似的问题。问题的原因是 ItemsSource 绑定到 RelativeSource,而 SelectedItem/SelectedValue 直接绑定到 DataContext。我能够解决它,但问题的原因尚不清楚。此处为遇到相同问题的任何人进行了描述:stackoverflow.com/questions/33739513/…
      【解决方案7】:

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

      【讨论】:

        【解决方案8】:

        我遇到了同样的问题。事情是。所选项目不知道它应该使用集合中的哪个对象。所以你必须对选中的项目说使用集合中的项目。

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

        我希望这会有所帮助。

        【讨论】:

          【解决方案9】:

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

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

          viewmodel 属性应该如下所示

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

          这应该可以解决问题。

          【讨论】:

            【解决方案10】:

            我通过在 UserControl_Loaded 事件中添加调度程序解决了这个问题

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

            【讨论】:

              【解决方案11】:

              IsSyncronizedWithCurrent=False 将使它工作。

              【讨论】:

                【解决方案12】:

                我与这个问题斗争了一段时间。在我的情况下,我使用复杂类型(列表)作为项目源,并使用 KeyType 作为选定值。在加载事件中,KeyType 被设置为 null。这导致一切都崩溃了。键更改时,不会更新任何子元素。事实证明,当我添加检查以确保 KeyType 的建议值不为空时,一切都按预期工作。

                    #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
                

                【讨论】:

                  【解决方案13】:

                  SelectedValuePathSelectedValue 的类型必须完全相同。

                  例如,如果SelectedValuePath 的类型是Int16,而绑定到SelectedValue 的属性类型是int,它将不起作用。

                  我花了好几个小时才找到它,这就是为什么我在问了这么久之后才在这里回答的原因。也许像我这样有同样问题的可怜人可以看到它。

                  【讨论】:

                    【解决方案14】:

                    我遇到了显示颜色列表 ( List ) 的 ComboBox 的问题。
                    可以选择颜色,但在选择关闭时不显示(尽管属性已更改!)

                    修复是覆盖 ComboBox (Brush) 中选择的类型的 Equals(object obj) 方法,这并不简单,因为 Brush 是密封的。 所以我写了一个类 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;
                        }
                    }
                    

                    使用我的新 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 类型!

                    【讨论】:

                      【解决方案15】:

                      这可能是您将 DataContext 应用于页面的方式。在 WPF 中,每次导航到页面时,所有内容都会重新初始化,调用构造函数,加载方法,等等。因此,如果您在 View 中设置 DataContext ,您无疑会吹走用户选择的 SelectedItem。为了避免这种情况,请使用页面的 KeepAlive 属性。

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

                      这将导致在导航回您已经访问过的页面时仅触发 Loaded 事件。因此,您需要确保在 Initialize(外部或构造函数内)而不是 Load 上设置 DataContext。

                      但是,这仅适用于该页面的实例。如果您导航到该页面的新实例,它将再次调用它的构造函数。

                      【讨论】:

                      • 因为我使用的是 MVVM 模式,所以 XAML 页面没有隐藏代码。没有加载事件。一切都是通过数据绑定发生的。就此而言,模板是由 DataTemplateSelector 基于视图模型中的属性选择的。
                      【解决方案16】:

                      ComboBox.SelectionBoxItem.ToString()

                      【讨论】:

                        【解决方案17】:

                        使用 Loaded 事件:

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

                        它对我有用。

                        【讨论】:

                          【解决方案18】:

                          您还可以将 SelectedIndex 绑定到 ViewModel 中的属性并以这种方式操作 SelectedItem:

                                  public int SelectedIndex
                                  {
                                      get { return _selectedIndex; }
                                      set
                                      {
                                          _selectedIndex = value;
                                          OnPropertyChanged();
                                      }    
                                  }
                          

                          在您的 XAML 中:

                          <ComboBox SelectedIndex="{Binding SelectedIndex,Mode=TwoWay}" ... >
                          

                          【讨论】:

                            猜你喜欢
                            • 2012-06-23
                            • 2012-06-19
                            • 2017-02-20
                            • 2014-03-18
                            • 1970-01-01
                            • 2021-09-14
                            • 2011-02-21
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多