【问题标题】:WPF Custom DependencyProperty Binding - SetValue set correct value but binded property set receive nullWPF 自定义 DependencyProperty 绑定 - SetValue 设置正确的值,但绑定的属性集接收 null
【发布时间】:2019-06-01 23:42:01
【问题描述】:

我的控件中的自定义 DependencyProperty 存在问题。让我解释一下:

我可以控制可检查项目的列表。我需要属性绑定到 IEnumerable SelectedItems。 SelectedItemsProperty 填充的逻辑在可控范围内,所以不只是简单的绑定。这是我控制的代码:

    public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register("SelectedItems", typeof(IEnumerable<object>), typeof(ButtonColumnFilter));

    public IEnumerable<object> SelectedItems
    {
      get { return (IEnumerable<object>)GetValue(SelectedItemsProperty); }
      set { Debug.WriteLine("SelectedItems has been set in ButtonColumnFilter - Count:{0}", value?.Count());
            SetValue(SelectedItemsProperty, value);
          }
     }

在这里,我收到带有正确数量的选定项目的调试消息,因此我的控制内部逻辑运行良好。 XAML 中的属性绑定在这里:

    <MESControls:ButtonColumnFilter CanSearch="True" CanSort="False" DisplayMemberPath="DisplayName"
ItemsSource="{Binding Path=FilterAdapter.Drivers, Mode=OneWay, IsAsync=True}"
OnApplyFilter="Drivers_ApplyFilter"
SelectedItems="{Binding Path=SelectedFilterDrivers, Mode=TwoWay}"/>

并且属性是在我的代码中定义的:

public IEnumerable<Driver> SelectedFilterDrivers
        {
            get => _SelectedFilterDrivers;
            set
            {

                Debug.WriteLine("SelectedFilterDrivers has been set in PlannerFilterAdapter - Count: {0}", value?.Count());
                if (_SelectedFilterDrivers != value)
                {

                    _SelectedFilterDrivers = value;
                    NotifyPropertyChanged("SelectedFilterDrivers");
                }
            }
        }

!!!但是在这里我得到调试消息'value == null'!!!

绑定运行良好,并且我想可以及时正确调用属性集。奇怪的是,控件内部的 SetValue 具有正确的值,但在代码外部,属性设置值为 null。

有什么问题吗? 谢谢。

更新:

这是更改控件内部的 SelectedItems 的代码:

private void CheckableItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {

            if (e.PropertyName == "IsChecked" && !_IsChecking)
            {
                _IsChecking = true;

                CheckableItem item = sender as CheckableItem;

                if(item.IsChecked == true && InnerItemsSource.Where(ci=>ci.Item.ToString() != SELECTALL).All(ci=>ci.IsChecked == true))
                {
                    InnerItemsSource.Single(ci => ci.Item.ToString() == SELECTALL).IsChecked = true;
                    SelectAllIsSelected = true;
                }
                else if (InnerItemsSource.Where(ci => ci.Item.ToString() != SELECTALL).All(ci=>ci.IsChecked == true) || InnerItemsSource.Where(ci => ci.Item.ToString() != SELECTALL).All(ci => ci.IsChecked == false))
                {
                    InnerItemsSource.Single(ci => ci.Item.ToString() == SELECTALL).IsChecked = false;
                    SelectAllIsSelected = false;
                } 
                else 
                {
                    InnerItemsSource.Single(ci => ci.Item.ToString() == SELECTALL).IsChecked = null;
                    SelectAllIsSelected = false;
                }

                SelectedItems = CheckedItems;

                NotifyPropertyChanged("IsFilterUsed");
                NotifyPropertyChanged("FilterIcon");


            }

            _IsChecking = false;
        }

public IEnumerable<object> CheckedItems
        {
            get
            {
                return InnerItemsSource?.Where(ci => ci.IsChecked == true && ci.Item.ToString() != SELECTALL).Select(ci => ci.Item);
            }

        }

但正如我所写,控件内设置的属性接收到正确的值,我猜是正确的类型。 CheckedItems 的返回值为 IEnumerable'object>'。

感谢您的帮助。

调用栈情况

这是集合的可见计数,在调用堆栈中仅退一步

调用栈的下一步——断点——值为null 事实是,这些步骤之间是一些外部代码 - 但我没有收到任何错误,也没有警告

更新 - 一些进展

我尝试将 public IEnumerableDriver> SelectedFilterDrivers 更改为

public IEnumerable<object> SelectedFilterDrivers
        {
            get => _SelectedFilterDrivers;
            set
            {

                Debug.WriteLine("SelectedFilterDrivers has been set in PlannerFilterAdapter - Count: {0}", value?.Count());
                if (_SelectedFilterDrivers != value)
                {

                    _SelectedFilterDrivers = (IEnumerable<Driver>)value;
                    NotifyPropertyChanged("SelectedFilterDrivers");
                }
            }
        }

我收到了正确数量的物品。因此,在重新键入集合中的项目期间存在问题。所以我需要以更好的方式继续前进,如何保持 SelectedItems 使用通用 IEnumerableobject>

的能力

重要的是,IEnumerableobject> 与简单绑定配合得很好(在这种情况下 ItemsSource - true 是,它只是 OneWay 绑定)

【问题讨论】:

  • 您的控件中是否有任何代码实际更改了SelectedItems 属性,即分配一个新的集合实例,或者您只是添加/删除项目?在后一种情况下,将属性绑定到 ObservableCollection。
  • 你什么时候把你的属性设置在控制范围内?
  • 已添加请求的代码。我总是添加新的集合实例。
  • 它的赋值是否与绑定源属性兼容,即它是否是 Driver 对象的集合?
  • 您可以在 SelectedFilterDrivers 设置器中放置一个条件断点(条件:value==null),然后查看该调用的来源。

标签: c# wpf binding wpf-controls dependency-properties


【解决方案1】:

没有深入了解您的代码,您是否尝试过使用SetCurrentValue 而不是SetValue

See this SO Answer

【讨论】:

  • 属性绑定Mode=TwoWay时,调用SetValue不会替换Binding,只是设置Binding的source属性。尽管有很多赞成票,但对于 TextBox 的 Text 属性,这个答案是错误的,因为它默认绑定了 TwoWay。也就是说,依赖属性的 CLR 包装器的设置器必须调用 SetValue,仅此而已。
  • 不起作用。结果相同。 SelectedItems 已在 ButtonColumnFilter 中设置 - 计数:4 SelectedFilterDrivers 已在 PlannerFilterAdapter 中设置 - 计数:
【解决方案2】:

没有从IEnumerable&lt;object&gt;IEnumerable&lt;Driver&gt; 的隐式转换。您应该在 Control 中将 SelectedItems 设置为 IEnumerable。

顺便说一句,如果您最终打算将您选择的驱动程序列表实现为一个可观察的集合,并且您想监听控件中的更改,那么在控件端您可以尝试强制转换为 INotifyCollectionChanged,它将为您提供事件。

【讨论】:

  • 我试图改变它,仍然是相同的结果。我只需要通过绑定使用选定的项目自动填充外部(意味着不受控制)属性。外部属性将始终为 IEnumerable。现在,我知道问题出在对象的重新输入上。所以我必须找到一些方法,如何让正确的某种类型的 IEnumerable 不受控制。但是感谢您的帮助。
猜你喜欢
  • 2020-06-16
  • 2013-01-20
  • 2011-01-18
  • 2011-01-17
  • 2011-08-06
  • 2011-12-14
  • 1970-01-01
  • 2013-02-21
  • 1970-01-01
相关资源
最近更新 更多