Background:
CollectionViewSource
in XAML taking data from an ObservableCollection
on the Window.CollectionViewSource
to a DataGrid
behaves exactly as expected (ItemsSource="{Binding Source={StaticResource ClustersView}}"
)CollectionViewSource
works fine.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);
}
}
}