2

我遇到了与ComboBox有关的问题,该问题绑定到ObservableCollection,我想知道是否有人能指出我缺少的内容。ComboBox SelectedItem在清除可观察集合后不会更改

我有一个ComboBox这是绑定到一个简单的ObservableCollection<string>。另外,我还将SelectedIndex绑定到某个属性的OneWay

在我的应用程序中,我得到了一个要清除收集并重新填充不同数据并将SelectedIndex设置为新值的点。由于某种原因,SelectedIndex绑定不起作用。

我安装的问题一点点摄制:

public partial class Window1 : Window, INotifyPropertyChanged 
{ 
    private int j; 
    public event PropertyChangedEventHandler PropertyChanged; 

    public Window1() 
    { 
     InitializeComponent(); 
     DataContext = this; 
     Tables = new ObservableCollection<string>(); 
    } 

    public ObservableCollection<string> Tables { get; set; } 

    private int _TheIndex; 
    public int TheIndex 
    { 
     get { return _TheIndex; } 
     set 
     { 
      _TheIndex = value; 
      if (PropertyChanged != null) 
      { 
       PropertyChanged.Invoke(this, new PropertyChangedEventArgs("TheIndex")); 
      } 
     } 
    } 

    private void aaaa(object sender, RoutedEventArgs e) 
    { 
     j = (j + 1)%10; 
     Tables.Clear(); 
     for(int i = 0; i < 10 ; i++) 
     { 
      Tables.Add(i.ToString()); 
     } 
     TheIndex = j; 
    } 
} 

的XAML是:

<Window x:Class="WpfApplication1.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"> 
    <Grid> 
     <StackPanel> 
      <ComboBox x:Name="TablesCombobox" 
         ItemsSource="{Binding Tables}" 
         SelectedIndex="{Binding TheIndex, Mode=OneWay}"/> 
      <Button Content="asdasd" Click="aaaa"/> 
     </StackPanel> 
    </Grid> 
</Window> 
+0

为什么你不只是在aaaa()中设置TablesCombobox.SelectedIndex而不是TheIndex?不知道为什么绑定不起作用。 – 2010-01-15 21:03:41

+0

我只需将您的repro代码复制/粘贴到VS2010 Express Beta 2中即可使用 - 并且工作顺利... – 2010-01-15 21:57:50

回答

4

的问题完全由Tables.Clear()线在aaaa()方法造成的。由于Tables是一个可观察的集合,因此清除集合的所有内容将导致WPF用新的空列表更新显示。然后它尝试使用SelectedIndex选择当前有效的项目,该项目不存在(因为列表现在为空)。其结果是,绑定引擎留下与不能被应用的值,并且决定去激活和分离的结合逻辑:

System.Windows.Data Warning: Got PropertyChanged event from Window1 for TheIndex 
System.Windows.Data Warning: GetValue at level 0 from Window1 using DependencyProperty(TheIndex): '1' 
System.Windows.Data Warning: TransferValue - got raw value '1' 
System.Windows.Data Warning: TransferValue - using final value '1' 
System.Windows.Data Warning: Deactivate 
System.Windows.Data Warning: Replace item at level 0 with {NullDataItem} 
System.Windows.Data Warning: Detach 

通过它到达时间“TheIndex = j的;”行,绑定不再活动,并且看不到对TheIndex的更改,这意味着不再选择所需的索引。

有几个解决方案来解决这个问题:

  1. 每次不要吹走整个集合。不清除集合,数据绑定逻辑总是有一个索引来选择,这意味着它永远不会分离。
  2. 使用TwoWay绑定。这是可行的,因为现在ComboBox参与绑定;您清除Tables,绑定尝试设置但找不到索引,因此组合框重置为-1的特殊“无索引”位置,然后写回到TheIndex(双向部分),这是有效的值,所以绑定逻辑不会分离。
  3. 在清除收集之前,请不要选择索引(-1)。如果在清除Tables时未选择索引(-1),则ComboBox不会尝试应用SelectedItem,这意味着它不会“查看”清空并重新填充的集合,因此不会分离。

    private void aaaa(object sender, RoutedEventArgs e) 
    { 
        TheIndex = -1; 
        j = (j + 1)%10; 
        Tables.Clear(); 
        for (int i = 0; i < 10; i++) 
        { 
         Tables.Add(i.ToString()); 
        } 
        TheIndex = j; 
    } 
    

出于性能,建筑和清楚的原因,我会强烈建议选择1,虽然我知道您的实际情况可能会更复杂,需要沿着3线的东西。


旁注:

定位背后是这样的结合问题的原因使用像一个上面贴的结合痕迹时,是相当容易的。打开它们的单一的结合通过声明System.Diagnostics命名空间和添加PresentationTraceSources.TraceLevel=High到绑定在作祟:调试的

<Window xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase" /> 
... 
<TextBlock Text="{Binding Path=x, diag:PresentationTraceSources.TraceLevel=High}" /> 

更多的方式WPF绑定here

0

我知道这是一个老问题,但我自己也经历过这个问题,所以写了一个基于@Nicholas Armstrong答案选项1的帮手方法,并且认为我会分享它,希望有人会发现它有用:

public void refreshDropdownOptions(ObservableCollection<object> OldOptions, ObservableCollection<object> NewOptions) 
{ 
    MainWindow application = Application.Current.MainWindow as MainWindow; 

    int highestCount = 0; 

    if(OldOptions.Count() > NewOptions.Count()) 
    { 
     highestCount = OldOptions.Count(); 
    } 
    else 
    { 
     highestCount = NewOptions.Count(); 
    } 

    for (int i = 0; i < highestCount; i++) 
    { 
     if(i < OldOptions.Count() && i < NewOptions.Count()) 
     {// If we have not exceeded the count of either list, copy the new value over the old 
      application.Dispatcher.Invoke((Action)(() => OldOptions[i] = NewOptions[i]));     
     } 
     else if (i < OldOptions.Count() && i >= NewOptions.Count()) 
     {// If we have no more new options remove the old option 
      application.Dispatcher.Invoke((Action)(() => OldOptions.RemoveAt(i))); 
      highestCount = OldOptions.Count(); 
      i--; 
     } 
     else if (i >= OldOptions.Count() && i < NewOptions.Count()) 
     {// if we have no more old options to replace, add the new option to the end of the collection 
      application.Dispatcher.Invoke((Action)(() => OldOptions.Add(NewOptions[i]))); 
     } 
    } 
}