WPF MVVM ComboBox SelectedItem或SelectedValue不起作用

时间:2009-03-19 20:16:50

标签: wpf mvvm combobox

更新

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

结束更新

我有一个使用MVF的应用程序,使用带有ComboBox的MVVM。下面是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模式工作方式的问题。我们正在进行的文本框绑定正常工作。

18 个答案:

答案 0 :(得分:28)

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

答案 1 :(得分:27)

您是否尝试在viewmodel中实施INotifyPropertyChanged,然后在设置PropertyChanged时引发SelectedItem事件?

如果这本身不能解决问题,那么当您导航回页面时,您将能够自己手动提升PropertyChanged事件,这应该足以让WPF重绘自己并显示更正所选项目。

答案 2 :(得分:21)

您需要将ItemsSource属性放在SelectedItem属性之前。几天前我偶然提到了这个问题。

答案 3 :(得分:11)

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

答案 4 :(得分:10)

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

一种可能的解决方案是:

根据所选的项目ID,恢复itemsource集合上的对象,并将选定的item属性设置为。

示例:

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

我希望它可以帮到你。 我花了很多时间寻找答案,但我找不到。

答案 5 :(得分:4)

离开当前页面时,会清除与CollectionView的{​​{1}}属性关联的ItemsSource。由于默认情况下ComboBox ComboBox属性为true,因此会重置IsSyncronizedWithCurrentSelectedItem属性。
这似乎是绑定中的内部数据类型问题。正如上面其他人所建议的,如果你使用SelectedValue而不是通过绑定到viewmodel上的int属性,它将起作用。 您的快捷方式是覆盖MyObject上的SelectedValue运算符,以便在比较两个MyObject时,比较实际的Equals属性。

另一个提示:如果您重新构建了视图模型并使用Id,请仅在SelectedValue SelectedValuePath=Id Id时使用它。如果使用字符串键,则绑定到int的{​​{1}}属性,而不是Text

答案 6 :(得分:3)

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

答案 7 :(得分:2)

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

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

我希望这会有所帮助。

答案 8 :(得分:2)

我对这个问题有一个非常简单的答案。首先将以下代码添加到View IsSynchronizedWithCurrentItem =“True”。

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

viewmodel Proptery应如下所示

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

这应解决问题。

答案 9 :(得分:1)

我在这个问题上打了一段时间。在我的情况下,我使用复杂类型(List)作为项目源,并使用KeyType作为选定的值。在load事件中,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

答案 10 :(得分:1)

我通过在UserControl_Loaded事件

中添加调度程序解决了这个问题
 Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
 {
     combobox.SelectedIndex = 0;
 }));

答案 11 :(得分:1)

我在使用显示颜色列表的ComboBox时出现此问题(List&lt; Brush&gt;) 选择颜色是可能的,但是当选择关闭时它没有显示(尽管属性已经改变了!)

该修复程序覆盖了ComboBox(画笔)中所选类型的Equals(object obj)方法,这并不简单,因为Brush是密封的。 所以我写了一个包含Brush的类 EqualityBrush 并实现了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类的List而不是普通的Brush类修复了这个问题!

My Combobox 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 中的“刷子”属性现在必须是Type EqualityBrush!

答案 12 :(得分:1)

SelectedValuePathSelectedValue的类型必须完全相同。

例如,如果SelectedValuePath的类型为Int16,并且绑定到SelectedValue的属性类型为int,则无效。

我花了好几个小时才发现这一点,这就是为什么我在这么多时间之后回答问题的问题。也许像我这样有同样问题的另一个可怜的家伙可以看到它。

答案 13 :(得分:0)

使用已加载的事件:

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

它对我有用。

答案 14 :(得分:0)

IsSyncronizedWithCurrent = False将使其正常工作。

答案 15 :(得分:0)

ComboBox.SelectionBoxItem.ToString()

答案 16 :(得分:0)

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

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

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

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

答案 17 :(得分:0)

您还可以将SelectedIndex绑定到ViewModel中的属性,并通过以下方式操作SelectedItem:

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

在您的XAML中:

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