【问题标题】:ComboBox SelectedItem does not change after clearing the observable collection清除可观察集合后,ComboBox SelectedItem 不会改变
【发布时间】:2011-01-05 13:48:51
【问题描述】:

我遇到了与ObservableCollection 绑定的ComboBox 的问题,我想知道是否有人能指出我缺少的东西。

我有一个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>

【问题讨论】:

  • 有什么理由不只在 aaaa() 中设置 TablesCombobox.SelectedIndex 而不是 TheIndex?不确定为什么绑定不起作用。
  • 我只是将您的复制代码复制/粘贴到 VS2010 Express Beta 2 中 - 它可以顺利运行...

标签: wpf combobox observablecollection selecteditem selectedindex


【解决方案1】:

问题完全是由aaaa() 方法中的Tables.Clear() 行引起的。由于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

【讨论】:

    【解决方案2】:

    我知道这是一个老问题,但我自己也遇到过这个问题,所以根据@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])));
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多