WPF自定义控件:将CollectionViewSource绑定到DependencyProperty

use*_*639 2 c# data-binding wpf dependency-properties custom-controls

我有一个WPF custom Control,其中包含一个ComboBox.我想通过a ItemsSource将这个ComboBox 绑定到一个Dependency Property自定义控件类CollectionViewSource,但我无法弄清楚如何使CollectionViewSource识别正确DataContext(在这种情况下我的自定义控件属性).

我google了很多,阅读了几乎所有SO自定义控件\ CollectionViewSource\Collection绑定\依赖属性绑定\等.问题并试图像一些类似的问题的解决方案这一个一些 更多的,它仍然没有工作.我可能会错过一些东西,但我不知道是什么.

以下是Custom Control类的相关部分:

public class CustomComboBox : Control
{
   static CustomComboBox()
   {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomComboBox), new FrameworkPropertyMetadata(typeof(CustomComboBox)));
   }

   public CustomComboBox()
   {
      CustomItems = new ObservableCollection<ComboBoxItem>();
      DataContext = this;
   }

   internal ObservableCollection<ComboBoxItem> CustomItems 
   {
      get { return (ObservableCollection<ComboBoxItem>)GetValue(CustomItemsProperty); }
      set { SetValue(CustomItemsProperty, value); }
   }

   // Using a DependencyProperty as the backing store for CustomItems.  This enables animation, styling, binding, etc...
   public static readonly DependencyProperty CustomItemsProperty =
            DependencyProperty.Register("CustomItems", typeof(ObservableCollection<ComboBoxItem>), typeof(CustomComboBox), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

   private string text;
   public string Text
   {
       get { return text; }
       set
       {
           text = value;
           OnPropertyChanged("Text");
       }
   }

  // More properties, events and functions...

}
Run Code Online (Sandbox Code Playgroud)

以及xaml代码的相关部分:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:MyNamespace"
                    xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
                    x:Class="CustomComboBox">

<CollectionViewSource x:Key="GroupedData"
                      Source="{Binding Path=CustomItems, RelativeSource={RelativeSource FindAncestor, AncestorType=local:CustomComboBox}, diag:PresentationTraceSources.TraceLevel=High}">
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="GroupName" />
        </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

<!-- ...Some more Styles and DataTemplates... -->

<Style TargetType="{x:Type local:CustomComboBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:CustomComboBox}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <ComboBox IsEditable="True"
                                  Text="{Binding Text}"
                                  ItemsSource="{Binding Source={StaticResource GroupedData}}">
                                  <!-- ...Some more properties... --> 
                        </ComboBox>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>
Run Code Online (Sandbox Code Playgroud)

我得到这个输出System.Diagnostics:

System.Windows.Data警告:58:路径:'CustomItems'

System.Windows.Data警告:60:BindingExpression(hash = 28932383):默认模式已解析为OneWay

System.Windows.Data警告:61:BindingExpression(hash = 28932383):解析为PropertyChanged的默认更新触发器

System.Windows.Data警告:62:BindingExpression(hash = 28932383):附加到System.Windows.Data.CollectionViewSource.Source(hash = 23914501)

System.Windows.Data警告:66:BindingExpression(hash = 28932383):RelativeSource(FindAncestor)需要树上下文

System.Windows.Data警告:65:BindingExpression(hash = 28932383):解析源延迟

System.Windows.Data警告:67:BindingExpression(hash = 28932383):解析源

System.Windows.Data警告:70:BindingExpression(hash = 28932383):找到数据上下文元素:(确定)

System.Windows.Data警告:67:BindingExpression(hash = 28932383):解析源

System.Windows.Data警告:70:BindingExpression(hash = 28932383):找到数据上下文元素:(确定)

System.Windows.Data警告:67:BindingExpression(hash = 28932383):解析源(最后机会)

System.Windows.Data警告:70:BindingExpression(hash = 28932383):找到数据上下文元素:(确定)

System.Windows.Data错误:4:不能用于与参照结合 '的RelativeSource FindAncestor,AncestorType =' MyNamespace.CustomComboBox找到源 'AncestorLevel = '1''.BindingExpression:路径= CustomItems; 的DataItem = NULL; target元素是'CollectionViewSource'(HashCode = 23914501); target属性是'Source'(类型'Object')

我先试过这个绑定:

<CollectionViewSource x:Key="GroupedData"
                          Source="{Binding CustomItems}">
Run Code Online (Sandbox Code Playgroud)

但后来我得到了错误:

System.Windows.Data错误:2:找不到目标元素的管理FrameworkElement或FrameworkContentElement.BindingExpression:路径= CustomItems; 的DataItem = NULL; target元素是'CollectionViewSource'(HashCode = 37908782); target属性是'Source'(类型'Object')

将ComboBox中的BTW绑定到另一个属性就像Text绑定一样.

Grx*_*x70 5

虽然@KyloRen的答案通常是正确的(并且继承上下文特别有用),但我想提供替代解决方案.关键点在于,CollectionViewSource问题应该被定义为FrameworkElement模板视觉树的一部分资源(在我看来,一个很好的选择是根元素).这是模板:

<ControlTemplate TargetType="{x:Type local:CustomComboBox}">
    <Border (...)>
        <Border.Resources>
            <CollectionViewSource x:Key="GroupedData"
                                  Source="{Binding CustomItems, RelativeSource={RelativeSource TemplatedParent}}">
                (...)
            </CollectionViewSource>
        </Border.Resources>
        <ComboBox IsEditable="True"
                  Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}}"
                  ItemsSource="{Binding Source={StaticResource GroupedData}}">
            (...)
        </ComboBox>
    </Border>
</ControlTemplate>
Run Code Online (Sandbox Code Playgroud)

以下是此解决方案的一些优点:

  • 它完全包含在模板中,因此:
    • 更改模板不会留下任何不必要的资源和/或对新模板产生任何影响
    • 它是完全可重用的"开箱即用",即它不需要任何额外的设置(例如设置属性或添加资源)
  • 它是DataContext独立的,它允许您在任何数据上下文中使用它,而不会破坏控件的功能

请注意,虽然后者是真的,但您应该修改模板中的绑定以供使用RelativeSource={RelativeSource TemplatedParent}(或TemplateBinding在适用的情况下使用).另外,我建议DataContext = this;从构造函数中删除该行,因为它会阻止数据上下文继承.