简体   繁体   中英

How to make a DependencyProperty on a Custom Control accept a CollectionViewSource Binding?

Background:

  • I can create a CollectionViewSource in XAML taking data from an ObservableCollection on the Window.
  • Binding the CollectionViewSource to a DataGrid behaves exactly as expected ( ItemsSource="{Binding Source={StaticResource ClustersView}}" )
  • Filtering of the CollectionViewSource works fine.
  • My CustomControl is working fine (at least, all the other Bindings and functionality)

Problem:

I'm not able to (or don't know how to) create a DependencyProperty on my CustomControl that will Bind to the CollectionViewSource and update/trigger when the View of the ColelctionViewSource changes (ie such as when Filtering changes).

What type should the DependencyProperty be so that I can bind to the CollectionViewSource ? Object type reveals the bound CollectionViewSource will be passed as ListCollectionView to my CustomControl DependencyProperty - but ListCollectionView doesn't provide an event (that is visible, ie. not Internal ) for CollectionChanged .

Deriving ItemsControl as the BaseClass for my CustomControl is not an option. A side comment - I shouldn't need to derive from ItemsControl to achieve this function: How would I create a CustomControl with 2 bindings that accept 2 different CollectionViewSources ( ItemsControl would not be able to do this)?

Code:

CollectionViewSource "ClustersView"

DataGrid : ItemsSource is bound to "ClustersView" (This works fine)

NavigatorControl : This is my CustomControl

Code:

<CollectionViewSource x:Key="ClustersView" 
                          Source="{Binding EventClusters, ElementName=me}" 
                          Filter="ClustersViewSource_Filter"></CollectionViewSource>

<DataGrid ItemsSource="{Binding Source={StaticResource ClustersView}}">

<LightMapperControls:NavigatorControl Clusters="{Binding Source={StaticResource ClustersView}}">
            </LightMapperControls:NavigatorControl>

 //The DP on NavigatorControl that I bind the CollectionViewSource to
public object Clusters
            {
                get { return (object)GetValue(ClustersProperty); }
                set { SetValue(ClustersProperty, value); }
            }
    // Using a DependencyProperty as the backing store for Clusters.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ClustersProperty =
        DependencyProperty.Register("Clusters", typeof(object), typeof(NavigatorControl), new PropertyMetadata(null, OnClustersChanged));

private static void OnClustersChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ListCollectionView lcv = (ListCollectionView)e.NewValue;     //e.NewValue is of type ListCollectionView

            //lcv.Items     //no such property
            //lcv.CollectionChanged     //no such event
        }

The solution is to let WPF do all the work! I made the Clusters DependencyObject of type object , then added the FrameworkPropertyMetadataOption.AffectsRender to trigger OnRender to redraw the canvas whenever Clusters changes (ie. Filtering occurs on the CollectionViewSource). (The actual method for redrawing the canvas is DrawClusters called from OnRender ). DrawClusters contains the cast of Clusters ( object ) to ICollectionView which I enumerate over in the for..each loop to access each individual Cluster .

Thank you @PeterDuniho for your questions that forced me to readdress what I thought I knew!

Relevant code below

    public object Clusters
    {
        get { return (object)GetValue(ClustersProperty); }
        set { SetValue(ClustersProperty, value); }
    }

    // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ClustersProperty =
        DependencyProperty.Register("Clusters", typeof(object), typeof(NavigatorControl), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));

private void DrawClusters(ref DrawingContext dc, ref double SquareWidth, ref double SquareHeight)
    {
        if (Clusters == null)
            return;
        if (ClustersVisibility == Visibility.Visible)
        {
            ICollectionView cv = (ICollectionView)Clusters;

            foreach (ClusterBase cluster in cv)
            {
                PathGeometry geometry = new PathGeometry();
                foreach (ClusterSquare square in cluster.Squares)
                {
                    geometry = Geometry.Combine(geometry, new RectangleGeometry(square.ToRect(SquareWidth, SquareHeight)), GeometryCombineMode.Union, null);
                }
                dc.DrawGeometry(clustersbrush, clusterspen, geometry);
            }
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM