绑定到 ICollectionView 的 ComboBox 显示不正确的 SelectedItem

发布于 2024-12-13 08:30:12 字数 2025 浏览 2 评论 0原文

我在使用 Silverlight 4.0 中的一对组合框时遇到问题。

目的是让两个不同的组合框从同一列表中读取,但如果一个组合框中选择的任何项目不会显示在另一个组合框中(因为底层属性不允许相同)。

例如(这只是示例代码,但同样代表了它的工作原理)

<ComboBox ItemsSource="{Binding BackgroundColors}"
          SelectedItem="{Binding SelectedBackgroundColor, Mode=TwoWay}" />

<ComboBox ItemsSource="{Binding ForegroundColors}"
          SelectedItem="{Binding SelectedForegroundColor, Mode=TwoWay}" />

为了允许这种动态过滤,我在 ViewModel 中有 2 个不同的 ICollectionView,每个组合框 ItemsSource 是必然的。每个ICollectionView都有相同ObservableCollection的源,但在过滤器中设置为过滤掉对方的所选项目。

private ObservableCollection<Color> _masterColorList;         
public ICollectionView BackgroundColors { get; }
public ICollectionView ForegroundColors { get; }

当 UI 中的 SelectedItem 发生更改时,ViewModel 属性也会更新,同时,相反的 ICollectionView 也会通过 .Refresh() 刷新。

例如。

public Color SelectedForegroundColor
{
    get { return _selectedForegroundColor; }
    set
    {
        if (_selectedForegroundColor == value)
            return;

        _selectedForegroundColor = value;

        BackgroundColors.Refresh();

        RaisePropertyChanged(() => SelectedForegroundColor);
    }
}

这允许过滤器重新运行并更改可供选择的内容。

这工作得很好,但有一个问题:

假设我们的主列表中有 3 种颜色:

  • 蓝绿
  • 组合

框 1 (CB1) 已选择蓝色 组合框 2 (CB2) 选择了绿色

因此组合框具有这些列表(选择粗体)

CB1

  • 蓝色
  • 红色

CB2

  • 绿色
  • 红色

如果我然后在 CB1 中选择红色,我希望红色将从 CB2 中删除并用蓝色替换它。这种情况正确发生,但显示的值从绿色变为蓝色

底层绑定值不会更改,并且 ICollectionView.CurrentItem 是正确的,但显示清楚地显示错误的值。

我认为正在发生的事情是,因为绿色在列表中较早,所以它与所显示的内容混淆了。如果您对 ICollectionView 进行排序,也会发生这种情况。

我尝试重新引发更改组合框和更改的属性更改事件通知。选定的项目但这似乎不起作用。

有没有人以前见过这个问题或者有什么想法可以解决它?

I'm having an issue with a pair of combo box's in Silverlight 4.0.

The intention is to have two different comboboxes that read from the same list, but if any item selected in one won't show up in the other (as the underlying properties are not allowed to be the same).

E.g. (this is just example code, but identically represents how it works)

<ComboBox ItemsSource="{Binding BackgroundColors}"
          SelectedItem="{Binding SelectedBackgroundColor, Mode=TwoWay}" />

<ComboBox ItemsSource="{Binding ForegroundColors}"
          SelectedItem="{Binding SelectedForegroundColor, Mode=TwoWay}" />

To allow for this dynamic filtering, I have 2 different ICollectionView's in my ViewModel that each combo box ItemsSource is bound to. Each ICollectionView has a source of the same ObservableCollection<T> but in the filter is set to filter out the other's selected item.

private ObservableCollection<Color> _masterColorList;         
public ICollectionView BackgroundColors { get; }
public ICollectionView ForegroundColors { get; }

When a SelectedItem is changed in the UI, the ViewModel properties are updated, and as part of that, the opposite ICollectionView is refreshed via .Refresh().

Eg.

public Color SelectedForegroundColor
{
    get { return _selectedForegroundColor; }
    set
    {
        if (_selectedForegroundColor == value)
            return;

        _selectedForegroundColor = value;

        BackgroundColors.Refresh();

        RaisePropertyChanged(() => SelectedForegroundColor);
    }
}

This allows the filter to rerun and change what is available to be selected.

This works pretty well, but there is a problem:

Say we have 3 colors in our master list:

  • Blue
  • Green
  • Red

Combo box 1 (CB1) is has selected Blue
Combo box 2 (CB2) has selected Green

Thus the combo boxes have these lists (bold is selected)

CB1

  • Blue
  • Red

CB2

  • Green
  • Red

If I then select Red in CB1, I would expect that Red would be removed from CB2 and Blue replace it. This happens correctly, BUT the displayed value changes from Green to Blue.

The underlying bound value doesn't get changed and the ICollectionView.CurrentItem is correct, but the display is clearly showing the wrong value.

What I think is happening is that because Green is earlier in the list, that it is getting confused with what is being shown. It also occurs if you are sorting the ICollectionView.

I've tried re-raising the property changed event notification for the changing combobox & selected item but this doesn't seem to work.

Has anyone seen this issue before or any ideas how I could fix it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

凉薄对峙 2024-12-20 08:30:12

组合框的绑定至少存在 5 个严重问题。

在这里,我认为您遇到了

http://connect .microsoft.com/VisualStudio/feedback/details/523394/silverlight-forum-combobox-selecteditem-binding

这个。

更新项目后,源绑定将停止工作。

我使用了那里可用的解决方案之一,这段代码为我解决了这个问题:

public class ComboBoxEx : ComboBox
    {
        #region Fields

        private bool _suppressSelectionChangedUpdatesRebind = false;

        #endregion

        #region Properties

        public static readonly DependencyProperty SelectedValueProperProperty =
            DependencyProperty.Register(
                "SelectedValueProper",
                typeof(object),
                typeof(ComboBoxEx),
                new PropertyMetadata((o, dp) => {
                                          var comboBoxEx = o as ComboBoxEx;
                                          if (comboBoxEx == null)
                                              return;

                                          comboBoxEx.SetSelectedValueSuppressingChangeEventProcessing(dp.NewValue);
                                      }));

        [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
        public object SelectedValueProper
        {
            get { return GetValue(SelectedValueProperProperty); }
            set { SetValue(SelectedValueProperProperty, value); }
        }

        #endregion

        #region Constructor and Overrides

        public ComboBoxEx()
        {
            SelectionChanged += ComboBoxEx_SelectionChanged;
        }

        /// <summary>
        /// Updates the current selected item when the <see cref="P:System.Windows.Controls.ItemsControl.Items"/> collection has changed.
        /// </summary>
        /// <param name="e">Contains data about changes in the items collection.</param>
        protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            // Must re-apply value here because the combobox has a bug that 
            // despite the fact that the binding still exists, it doesn't 
            // re-evaluate and subsequently drops the binding on the change event
            SetSelectedValueSuppressingChangeEventProcessing(SelectedValueProper);
        }

        #endregion

        #region Events

        private void ComboBoxEx_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Avoid recursive stack overflow
            if (_suppressSelectionChangedUpdatesRebind)
                return;

            if (e.AddedItems != null && e.AddedItems.Count > 0) {
                //SelectedValueProper = GetMemberValue( e.AddedItems[0] );
                SelectedValueProper = SelectedValue; // This is faster than GetMemberValue
            }
            // Do not apply the value if no items are selected (ie. the else)
            // because that just passes on the null-value bug from the combobox
        }

        #endregion

        #region Helpers

        /// <summary>
        /// Gets the member value based on the Selected Value Path
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        private object GetMemberValue(object item)
        {
            return item.GetType().GetProperty(SelectedValuePath).GetValue(item, null);
        }

        /// <summary>
        /// Sets the selected value suppressing change event processing.
        /// </summary>
        /// <param name="newSelectedValue">The new selected value.</param>
        private void SetSelectedValueSuppressingChangeEventProcessing(object newSelectedValue)
        {
            try {
                _suppressSelectionChangedUpdatesRebind = true;
                SelectedValue = newSelectedValue;
            }
            finally {
                _suppressSelectionChangedUpdatesRebind = false;
            }
        }

        #endregion
    }

它不是我的代码,而是来自与此错误相关的文章的代码。

There are at least 5 serious issues with binding of comboboxes.

Here I think you ran into

http://connect.microsoft.com/VisualStudio/feedback/details/523394/silverlight-forum-combobox-selecteditem-binding

this one.

Once you update itemssource binding stops working.

I used one of solutions available there, this code solved this issue for me:

public class ComboBoxEx : ComboBox
    {
        #region Fields

        private bool _suppressSelectionChangedUpdatesRebind = false;

        #endregion

        #region Properties

        public static readonly DependencyProperty SelectedValueProperProperty =
            DependencyProperty.Register(
                "SelectedValueProper",
                typeof(object),
                typeof(ComboBoxEx),
                new PropertyMetadata((o, dp) => {
                                          var comboBoxEx = o as ComboBoxEx;
                                          if (comboBoxEx == null)
                                              return;

                                          comboBoxEx.SetSelectedValueSuppressingChangeEventProcessing(dp.NewValue);
                                      }));

        [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
        public object SelectedValueProper
        {
            get { return GetValue(SelectedValueProperProperty); }
            set { SetValue(SelectedValueProperProperty, value); }
        }

        #endregion

        #region Constructor and Overrides

        public ComboBoxEx()
        {
            SelectionChanged += ComboBoxEx_SelectionChanged;
        }

        /// <summary>
        /// Updates the current selected item when the <see cref="P:System.Windows.Controls.ItemsControl.Items"/> collection has changed.
        /// </summary>
        /// <param name="e">Contains data about changes in the items collection.</param>
        protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            // Must re-apply value here because the combobox has a bug that 
            // despite the fact that the binding still exists, it doesn't 
            // re-evaluate and subsequently drops the binding on the change event
            SetSelectedValueSuppressingChangeEventProcessing(SelectedValueProper);
        }

        #endregion

        #region Events

        private void ComboBoxEx_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Avoid recursive stack overflow
            if (_suppressSelectionChangedUpdatesRebind)
                return;

            if (e.AddedItems != null && e.AddedItems.Count > 0) {
                //SelectedValueProper = GetMemberValue( e.AddedItems[0] );
                SelectedValueProper = SelectedValue; // This is faster than GetMemberValue
            }
            // Do not apply the value if no items are selected (ie. the else)
            // because that just passes on the null-value bug from the combobox
        }

        #endregion

        #region Helpers

        /// <summary>
        /// Gets the member value based on the Selected Value Path
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        private object GetMemberValue(object item)
        {
            return item.GetType().GetProperty(SelectedValuePath).GetValue(item, null);
        }

        /// <summary>
        /// Sets the selected value suppressing change event processing.
        /// </summary>
        /// <param name="newSelectedValue">The new selected value.</param>
        private void SetSelectedValueSuppressingChangeEventProcessing(object newSelectedValue)
        {
            try {
                _suppressSelectionChangedUpdatesRebind = true;
                SelectedValue = newSelectedValue;
            }
            finally {
                _suppressSelectionChangedUpdatesRebind = false;
            }
        }

        #endregion
    }

Its not mine code, but one from articles related to this bug.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文