2010-01-28 121 views
7

好了一直与WPF工作了一段时间,但我需要一些帮助。WPF组合框SelectedItem

我有一个ComboBox象下面这样:

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

每当我从标签1移开,然后回来给它的选择被删除。我认为这样做的原因是控件在超出范围之后会被销毁,但是在SelectedItem变为null的过程中,这实际上并不是用户想要的,这是由于UI生命周期。

所以我想知道什么是最好的路线?我正在用MVVM构建这个应用程序,所以我可以忽略ViewModel中MyListSelection属性上的set调用,但我有遍布整个地方的ComboBoxes,并且不喜欢修改我认为是WPF错误的ViewModel。

我可以继承的WPF组合框,但没有SelectedItemChanging事件时的SelectedItem改变我只能添加一个处理程序。

任何想法?

UPDATE:

好了,打我的头靠在我发现为什么我的问题没能再现的后墙上。如果由于某种原因,列表项类型是一个类,SelectedItem被WPF设置为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。

发生了什么事?

回答

10

我有完全相同的问题,并且到现在为止我想不出是什么问题。我在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> 
0

在OP更改后编辑。 嗨,何塞,我无法重现你提到的错误。所以你关于控制被破坏的假设是错误的。即使它现在使用引用类型,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; 
     } 
    } 
} 
+1

当列表类型是引用类型它不具有相同的行为。查看我更新的帖子 – Jose 2010-01-28 21:09:54

0

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

0

我想你可能会错过这里是一个TwoWay绑定SelectedItem。当绑定包含MyList(绑定ItemsSource)和MyListSelection(Bond到您的案例中的SelectedItem)的ViewModel类时,即使您访问了不同的选项卡,也会始终获得这些信息。所以当你回到这个选项卡时,MyListSelection会再次绑定到ComboBoc.SelectedItem,你会很好。试试这个,让我知道。

+1

默认情况下,SelectedItem绑定是TwoWay。 – 2010-01-29 05:53:30

0

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

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

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

+0

是的,这是我多次使用的选项,但该应用程序有很多组合框和列表视图,每次都这样做非常烦人。 – Jose 2010-01-29 17:07:38

+0

事实上,这可能会很烦人,但是即使在每个房地产上也不得不提高房产也是令人讨厌的。 WPF远非完美。 GL – 2010-01-30 12:29:14

+1

这并不总是可接受的,因为空值有时可能是集合中的有效值。此外,属性实际上是依赖项属性的情况如何?然后,你将不得不看着Coerce和Change通知事件来做类似的事情,这只是一团糟。在我看来,这通常不是一个可接受的解决方案。 – jpierson 2010-05-10 09:52:33

0

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

0

该行为由组合框执行,应该由编译器以比它更好的方式实现...... IE编译器应检查并查看ItemsSource的类型和SelectedItem属性的类型引用值势必将永远返回一个值,相当于

应该警告你可能会考虑重写的equals()和GetHashCode()方法...

浪费了大量的时间这个今天!

相关问题