【问题标题】:WPF ComboBox SelectedItemWPF ComboBox SelectedItem
【发布时间】:2011-01-10 12:46:43
【问题描述】:

好的,我已经使用 WPF 有一段时间了,但我需要一些帮助。

我有一个ComboBox,如下所示:

<TabControl>
    <TabItem Header="1">
        <ComboBox ItemsSource="{Binding MyList}" SelectedItem="{Binding MyListSelection}"/>
    </TabItem>
    <TabItem Header="2"/>
</TabControl>

每当我从标签 1 移开然后回到它时,选择就会被删除。我认为这样做的原因是控件在超出范围然后重新进入时被破坏。但是在 SelectedItem 变为 null 的过程中,这并不是用户真正想要的,这是由于 UI 造成的事件生命周期。

所以我想知道最好的路线是什么?我正在使用 MVVM 构建这个应用程序,所以我可以忽略我的 ViewModel 中对 MyListSelection 属性的设置调用,但是我到处都有 ComboBoxes,并且不喜欢修改我的 ViewModel 来解决我认为的 WPF 错误。

我可以子类化 WPF ComboBox,但是没有 SelectedItemChanging 事件我只能在 SelectedItem 更改时添加处理程序。

有什么想法吗?

更新:

好的,在将我的头撞到墙上后,我发现了为什么我的问题无法重现。如果由于某种原因列表项类型是一个类,则 WPF 将 SelectedItem 设置为 null,但如果它是一个值类型,则不是。

这是我的测试类(VMBase 只是一个实现 INotifyPropertyChanged 的​​抽象类):

public class TestListViewModel : VMBase
{
    public TestListViewModel()
    {
        TestList = new List<TestViewModel>();
        for (int i = 0; i < 10; i++)
        {
            TestList.Add(new TestViewModel(i.ToString()));
        }
    }

    public List<TestViewModel> TestList { get; set; }

    TestViewModel _SelectedTest;
    public TestViewModel SelectedTest
    {
        get { return _SelectedTest; }
        set
        {
            _SelectedTest = value;
            OnPropertyChanged("SelectedTest");
        }
    }
}

public class TestViewModel : VMBase
{
  public string Name {get;set;}
}

因此,当我将 TestList 更改为键入 int 并在选项卡之间来回切换时,SelectedItem 保持不变。但是当它是 TestViewModel 类型时,当 tabitem 失去焦点时,SelectedTest 被设置为 null。

发生了什么事?

【问题讨论】:

    标签: wpf mvvm combobox selecteditem


    【解决方案1】:

    组合框的这种行为,应该由编译器以比它更好的方式实现... IE 编译器应该检查并查看 ItemsSource 的类型和 SelectedItem 属性的类型引用值绑定到将永远返回可比较的值

    它应该警告您可能会考虑覆盖 Equals() 和 GetHashCode() 方法...

    今天浪费了很多时间!!

    【讨论】:

      【解决方案2】:

      我的列表中的引用类型遇到了完全相同的问题。解决方案是在我的 TestViewModel 中覆盖 Equals(),以便 WPF 能够在对象之间进行值相等检查(而不是引用检查)以确定哪个是 SelectedItem。我的恰好有一个 ID 字段,它确实是 TestViewModel 的识别特征。

      【讨论】:

        【解决方案3】:

        我有完全相同的问题,直到现在我无法弄清楚问题是什么。我在 4 台具有相同操作系统、.Net 版本和硬件规格的不同机器上进行了测试,并且可以在其中两台机器上重现该问题,在其他机器上运行良好。 我发现对我有用的解决方法是在 ItemsSource 之前定义 SelectedItem 绑定。奇怪的是,如果我遵循这种模式,一切都会按预期进行。 也就是说,您只需执行以下操作:

        <Window x:Class="ComboBoxInTabItemSpike.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">
            <StackPanel>
                <TabControl>
                    <TabItem Header="1">
                        <ComboBox SelectedItem="{Binding MySelect}" ItemsSource="{Binding MyList}"/>
                    </TabItem>
                    <TabItem Header="2"/>
                </TabControl>
                <TextBlock Text="{Binding MySelect}"/>
            </StackPanel>
        </Window>
        

        【讨论】:

          【解决方案4】:

          我认为这可以通过简单的空检查来解决。

          public TestViewModel SelectedTest
          {
              get { return _SelectedTest; }
              set
              {
                  if(value != null)
                      _SelectedTest = value;
                  OnPropertyChanged("SelectedTest");
              }
          }
          

          这是因为 ComboBox 在回收时倾向于重置其 SelectedIndex。这个简单的空检查将强制它重新绑定到最后一个有效项。

          【讨论】:

          • 是的,这是我多次使用的选项,但是该应用程序有很多组合框和列表视图,每次都这样做很烦人。
          • 确实这可能很烦人,但是即使在每个属性上都必须再次提高属性也很烦人。 WPF 远非完美。总帐
          • 这并不总是可以接受的,因为空值有时可能是集合中的有效值。还有属性实际上是依赖属性的情况呢?然后,您将不得不查看 Coerce 和 Change 通知事件,以便执行类似的操作,这只是一团糟。在我看来,这确实不是一般可接受的解决方案。
          【解决方案5】:

          在 OP 更改后已编辑。 嗨,何塞,我无法重现您提到的错误。因此,您对 Control 被销毁的假设是错误的。 Combobox 的行为与下面的代码一样,即使它现在使用引用类型。当您更改 TabItems 时,您的其他一些代码必须启动。

          <Window x:Class="ComboBoxInTabItemSpike.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">
              <StackPanel>
                  <TabControl>
                      <TabItem Header="1">
                          <ComboBox ItemsSource="{Binding MyList}"
                                    SelectedItem="{Binding MySelect}"/>
                      </TabItem>
                      <TabItem Header="2"/>
                  </TabControl>
                  <TextBlock Text="{Binding MySelect}"/>
              </StackPanel>
          </Window>
          
          using System.Collections.ObjectModel;
          using System.ComponentModel;
          using System.Windows;
          
          namespace ComboBoxInTabItemSpike
          {
              public partial class Window1 : Window, INotifyPropertyChanged
              {
                  public Window1()
                  {
                      InitializeComponent();
                      MyList=new ObservableCollection<TestObject>(
                          new[]{new TestObject("1"),new TestObject("2"),new TestObject("3") });
                      DataContext = this;
                  }
          
                  public ObservableCollection<TestObject> MyList { get; set; }
          
                  private TestObject mySelect;
                  public TestObject MySelect
                  {
                      get { return mySelect; }
                      set{ mySelect = value;
                      if(PropertyChanged!=null)
                          PropertyChanged(this,new PropertyChangedEventArgs("MySelect"));} 
                  }
          
                  public TestObject MySelectedItem
                  {
                      get { return (TestObject)GetValue(MySelectedItemProperty); }
                      set { SetValue(MySelectedItemProperty, value); }
                  }
          
                  public static readonly DependencyProperty MySelectedItemProperty =
                      DependencyProperty.Register("MySelectedItem",
                                          typeof(TestObject),
                                          typeof(Window1),
                                          new UIPropertyMetadata(null));
          
                  public event PropertyChangedEventHandler PropertyChanged;
              }
          
              public class TestObject
              {
                  public string Name { get; set; }
          
                  public TestObject(string name)
                  {
                      Name = name;
                  }
          
                  public override string ToString()
                  {
                      return Name;
                  }
              }
          }
          

          【讨论】:

          • 当 List 类型是引用类型时,它的行为不同。查看我更新的帖子
          【解决方案6】:

          我认为您在这里可能缺少的是 SelectedItem 上的 TwoWay 绑定。当您绑定包含 MyList(bound ItemsSource) 和 MyListSelection(Bond to SelectedItem in your Case) 的 ViewModel 类时,即使您转到不同的选项卡,也将始终拥有这些信息。因此,当您返回此选项卡时,MyListSelection 将再次绑定回 ComboBoc.SelectedItem,您会很好。试试这个,让我知道。

          【讨论】:

          • SelectedItem 绑定默认是 TwoWay。
          【解决方案7】:

          我建议检查绑定。如果您的应用程序中的任何其他内容正在更改所选项目或项目源,那么您的绑定将中断。您也可以在 Visual Studio 的输出窗口中查看是否有任何错误。

          【讨论】:

            猜你喜欢
            • 2012-06-19
            • 2011-11-01
            • 2017-02-20
            • 2014-03-18
            • 2015-06-28
            • 2010-10-24
            • 2023-03-09
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多