How to find a DataTemplate control from inside the Click event - c#

I have a GridView with a ColumnHeaderTemplate
The template contains a path with the name arrow:
<DataTemplate x:Key="HeaderTemplate">
<DockPanel>
<Path DockPanel.Dock="Right" Margin="5,0,5,0" x:Name="arrow" StrokeThickness="1" Fill="Gray" Data="M 5,5 L 10,10 L 15,5 L 5,5" SnapsToDevicePixels="True"/>
</DockPanel>
</DataTemplate>
The template is assigned in the view like this:
<GridView ColumnHeaderTemplate="{StaticResource HeaderTemplate}">
The GridView is inside a ListView that manages the events
GridViewColumnHeader.Click="ListView_ColumnHeaderClick"
private void ListView_ColumnHeaderClick(object sender, RoutedEventArgs e)
When the event is triggered I want to be able to find the arrow control.
According to my research I should use the Template.FindName method, but so far I have not been able to make this work.
I cant seem to find the correct objects to use with the function and so I never find the control I am looking for.

No, the FindName method you mean would apply to ControlTemplate, not DataTemplate.
How to: Find ControlTemplate-Generated Elements
For DataTemplate you have to iterate the children maually using VisualTreeHelper.
How to: Find DataTemplate-Generated Elements
I do not know how you have attached the column header event handler, so I assume this:
<ListView ItemsSource="{Binding YourItemsSource}">
<ListView.Resources>
<DataTemplate x:Key="HeaderTemplate">
<DockPanel>
<Path DockPanel.Dock="Right" Margin="5,0,5,0" x:Name="arrow" StrokeThickness="1" Fill="Gray" Data="M 5,5 L 10,10 L 15,5 L 5,5" SnapsToDevicePixels="True"/>
</DockPanel>
</DataTemplate>
<Style x:Key="HeaderContainerStyle" TargetType="{x:Type GridViewColumnHeader}" BasedOn="{StaticResource {x:Type GridViewColumnHeader}}">
<EventSetter Event="Click" Handler="ListView_ColumnHeaderClick"/>
</Style>
</ListView.Resources>
<ListView.View>
<GridView ColumnHeaderTemplate="{StaticResource HeaderTemplate}"
ColumnHeaderContainerStyle="{StaticResource HeaderContainerStyle}">
<!-- ...your column definitions. -->
</GridView>
</ListView.View>
</ListView>
You have to create a custom method to recursively traverse the visual tree of the the grid view column header that checks the type and the name of the child elements to get the right one.
public T GetChild<T>(DependencyObject dependencyObject, string name) where T : FrameworkElement
{
if (dependencyObject == null)
return null;
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObject); i++)
{
var child = VisualTreeHelper.GetChild(dependencyObject, i);
if (child is T frameworkElement && frameworkElement.Name.Equals(name))
return frameworkElement;
var nextChild = GetChild<T>(child, name);
if (nextChild != null)
return nextChild;
}
return null;
}
Then in the event handler, you can pass the sender, which is the column header.
private void ListView_ColumnHeaderClick(object sender, RoutedEventArgs e)
{
var gridViewColumnHeader = (GridViewColumnHeader)sender;
var arrow = GetChild<Path>(gridViewColumnHeader, "arrow");
// ... do something with arrow.
return;
}
Although this solution works and is a legitimate and officially documented way to solve your issue, you should usually not have to traverse the visual tree this way. In most cases it is not necessary as a lot of issues can be solved more elegantly and easier using data binding.

Related

UWP equivalent function to FindAncestor in uwp

I have a list of orders and when the order status is Cancelled, I want to blink the text. So far, my code works. However, sometimes it will throws exception:
WinRT information: Cannot resolve TargetName lblOrderStatus
For some reason lblOrderStatus can be found. So, I want to use "FindAncestor", but FindAncestor doesn't exists in UWP. Is there any equivalent function to FindAncestor in uwp?
Here is my code:
<ItemsControl x:Name="Orders" Grid.Row="1" Background="Transparent">
...
...
...
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
...
...
...
<Viewbox Grid.Column="3" StretchDirection="DownOnly" HorizontalAlignment="Right">
<TextBlock x:Name="lblOrderStatus" Text="{Binding Path=OrderItemStatus, Mode=OneWay}" FontSize="18">
<TextBlock.Resources>
<Storyboard x:Name="sbBlinking">
<DoubleAnimation Storyboard.TargetProperty="(FrameworkElement.Opacity)"
Storyboard.TargetName="lblOrderStatus"
From="1" To="0" AutoReverse="True" Duration="0:0:0.5" RepeatBehavior="Forever" />
</Storyboard>
</TextBlock.Resources>
<interactive:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding OrderItemStatus, Converter={StaticResource EnumToStringConverter}}" ComparisonCondition="Equal" Value="Cancelled">
<media:ControlStoryboardAction Storyboard="{StaticResource sbBlinking}" />
</core:DataTriggerBehavior>
</interactive:Interaction.Behaviors>
</TextBlock>
</Viewbox>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Considering all the solutions I've seen, I feel that using ElementName binding is the simplest workaround to UWP not having a RelativeSource AncestorType binding option.
Assuming you've got a Page with its DataContext set to a viewmodel with a command MyCommand, and you want each item in your list to execute it when its button is clicked:
<Page Name="thisPage">
...
<ListView ...>
<ListView.ItemTemplate>
<DataTemplate>
<Button Command="{Binding ElementName=thisPage, Path=DataContext.MyCommand}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Page>
My initial problem with this solution is that you can't extract the DataTemplate out as a resource to use it on multiple screens (or even dialog boxes); thisPage might not exist in each of those places, or it might not be appropriate to name the root element "thisPage".
But if you use a convention where you include a token UI element in every screen that uses that DataTemplate, and refer to it by a consistent name, it will work. By default this element's DataContext will be your ViewModel (assuming your root element does too)
<Rectangle Name="VmDcHelper" Visibility="Collapsed"/>
...then in your standalone resources XAML file you can write your DataTemplate like this:
<DataTemplate x:Key="MyDataTemplate">
<Button Command="{Binding ElementName=VmDcHelper, Path=DataContext.MyCommand}" />
</DataTemplate>
Then, on every page/screen/dialog that you use that template resource, just drop in a copy of that Rectangle (or whatever) and everything will bind correctly at run-time
This is clearly a hack solution, but after thinking about it some more, it doesn't feel like any more of a hack than using WPF's AncestorType in the first place (having to ensure that your ancestor type is always consistent in all the places you use your DataTemplate).
I'm converting an app from WPF to UWP and found this thread. It seems there are no good solutions on the web, so here is my attempt to 'solve' this problem via workaround.
NOTE: The following is UNTESTED in UWP (but works in WPF) as I'm part way through a large non-compiling port, but theoretically it should work...
1 Create a RelativeSourceBinding Attached Property
This class has two properties: AncestorType and Ancestor. When the AncestorType changes, we subscribe to FrameworkElement.Loaded (to handle parent changes) and find the visual parent of type and assign to the Ancestor attached property.
public class RelativeSourceBinding
{
public static readonly DependencyProperty AncestorTypeProperty = DependencyProperty.RegisterAttached("AncestorType", typeof(Type), typeof(RelativeSourceBinding), new PropertyMetadata(default(Type), OnAncestorTypeChanged));
public static void SetAncestorType(DependencyObject element, Type value)
{
element.SetValue(AncestorTypeProperty, value);
}
public static Type GetAncestorType(DependencyObject element)
{
return (Type)element.GetValue(AncestorTypeProperty);
}
private static void OnAncestorTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((FrameworkElement)d).Loaded -= OnFrameworkElementLoaded;
if (e.NewValue != null)
{
((FrameworkElement)d).Loaded += OnFrameworkElementLoaded;
OnFrameworkElementLoaded((FrameworkElement) d, null);
}
}
private static void OnFrameworkElementLoaded(object sender, RoutedEventArgs e)
{
var ancestorType = GetAncestorType((FrameworkElement) sender);
if (ancestorType != null)
{
var findAncestor = ((FrameworkElement) sender).FindVisualParent(ancestorType);
RelativeSourceBinding.SetAncestor(((FrameworkElement)sender), findAncestor);
}
else
{
RelativeSourceBinding.SetAncestor(((FrameworkElement)sender), null);
}
}
public static readonly DependencyProperty AncestorProperty = DependencyProperty.RegisterAttached("Ancestor", typeof(UIElement), typeof(RelativeSourceBinding), new PropertyMetadata(default(FrameworkElement)));
public static void SetAncestor(DependencyObject element, UIElement value)
{
element.SetValue(AncestorProperty, value);
}
public static UIElement GetAncestor(DependencyObject element)
{
return (UIElement)element.GetValue(AncestorProperty);
}
}
Where FindVisualParent is an extension method defined as
public static UIElement FindVisualParent(this UIElement element, Type type)
{
UIElement parent = element;
while (parent != null)
{
if (type.IsAssignableFrom(parent.GetType()))
{
return parent;
}
parent = VisualTreeHelper.GetParent(parent) as UIElement;
}
return null;
}
2 Apply the RelativeSourceBinding property in XAML
some BEFORE xaml in WPF would look like this
<Style x:Key="SomeStyle" TargetType="local:AClass">
<Style.Setters>
<Setter Property="SomeProperty" Value="{Binding Foo, RelativeSource={RelativeSource AncestorType=local:AnotherClass}}" />
</Style.Setters>
</Style>
and AFTER xaml
<Style x:Key="SomeStyle" TargetType="local:AClass">
<Style.Setters>
<Setter Property="apc:RelativeSourceBinding.AncestorType" Value="local:AnotherClass"/>
<Setter Property="Foreground" Value="{Binding Path=(apc:RelativeSourceBinding.Ancestor).Foo, RelativeSource={RelativeSource Self}}" />
</Style.Setters>
</Style>
It's a bit messy but in the case where you only have one RelativeSource FindAncestor type to find, it should work.
In XAML
You can try using RelativeSource, it provides a means to specify the source of a binding in terms of a relative relationship in the run-time object graph.
For example using TemplatedParent:
Height="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=Parent.ActualHeight}
or
<Binding RelativeSource="{RelativeSource TemplatedParent}" ></Binding>
In code you try using the VisualTreeHelper.GetParent method.
https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.visualtreehelper.getparent
something like the following, here is an example of a utility function
internal static void FindChildren<T>(List<T> results, DependencyObject startNode)
where T : DependencyObject
{
int count = VisualTreeHelper.GetChildrenCount(startNode);
for (int i = 0; i < count; i++)
{
DependencyObject current = VisualTreeHelper.GetChild(startNode, i);
if ((current.GetType()).Equals(typeof(T)) || (current.GetType().GetTypeInfo().IsSubclassOf(typeof(T))))
{
T asType = (T)current;
results.Add(asType);
}
FindChildren<T>(results, current);
}
}
The following example shows code that checks for an element's parent
((StackPanel)LinePane.Parent).ActualWidth;
Also, here is a good blog post showing this class in action. http://blog.jerrynixon.com/2012/09/how-to-access-named-control-inside-xaml.html

Finding Canvas within the visual tree

I have a FlipView controll which in its data template got a scrollviewer, which then got a canvas with the controls. My problem is that I need to access the canvas inside the eventhandler for the FlipView.SelectionChanged event.
The Xaml for the FlipView looks like this.
<FlipView Grid.Row="1"
d:DataContext="{d:DesignInstance model:PageContent}"
SelectionChanged="FlipView_SelectionChanged"
ItemsSource="{Binding TiffPages}"
x:Name="flBillImage">
<FlipView.ItemTemplate>
<DataTemplate>
<ScrollViewer x:Name="scrollBill"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
ZoomMode="Enabled"
DataContextChanged="scrollBill_DataContextChanged">
<Canvas x:Name="cvBill"
DataContextChanged="cvBill_DataContextChanged"
Loaded="cvBill_Loaded"
HorizontalAlignment="Left"
VerticalAlignment="Top"
FlowDirection="LeftToRight" >
<Image x:Name="imgBill"
Loaded="imgBill_Loaded"
DataContextChanged="imgBill_DataContextChanged"
Canvas.ZIndex="0"
Source="{Binding BillImage}"
Visibility="{Binding IsFrameExtracted, Converter={StaticResource BooleanToVisibilityConverter}}" />
</Canvas>
</ScrollViewer>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
And the C# code for parsing the visual tree looks like this:
public static List<Control> AllChildren(DependencyObject parent)
{
var _List = new List<Control>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var _Child = VisualTreeHelper.GetChild(parent, i);
if (_Child is Control)
{
_List.Add(_Child as Control);
}
_List.AddRange(AllChildren(_Child));
}
return _List;
}
Which is used like:
var ctrls = AllChildren(flBillImage);
Checking the returned list I can find the ScrollViewer but I can't find the Canvas. I have also tried to supply the scrollviewer returned as argument to the AllChildren function but I still can't seem to find the Canvas control.
Am I doing this all wrong?
I faced a similar type of problem quite some time ago. This solution was used to access the child elements of a tree in the code-behind. Much straight-forward.
Hope this helps you.

How can I select a specific point in a LineSeries

I'm currently trying to select a point in a lineSeries using toolkit in a wpf application. I'm just trying to select it using a mouse event but I'm on it for hours and haven't had any success so far.
Here is an extract of my xaml
<Grid Name="amplitude_envelope" Grid.ColumnSpan="9" Grid.Column="2" Grid.Row="6" Margin="0,0,95,0">
<chartingToolkit:Chart Title="Amplitude Envelope" Name="chart1" AllowDrop="True" >
<chartingToolkit:Chart.Series>
<chartingToolkit:LineSeries
Name="my_line"
MouseDown="StartDrag"
MouseLeave="StopDrag"
MouseMove="DragObject"
IsSelectionEnabled="True"
Title="Envelope"
DependentValuePath="Power" IndependentValuePath="Speed" >
<chartingToolkit:LineSeries.DependentRangeAxis>
<chartingToolkit:LinearAxis
Orientation="Y"
Title="Amplitude (%)"
Minimum="0"
Maximum="1.2"
Interval="0.2"
ShowGridLines="True"/>
</chartingToolkit:LineSeries.DependentRangeAxis>
</chartingToolkit:LineSeries>
</chartingToolkit:Chart.Series>
</chartingToolkit:Chart>
</Grid>
and my DragObject method looks like:
private void DragObject(object sender, MouseEventArgs e)
{
if (my_line.IsMouseCaptured)
{
LineSeries line = (LineSeries)sender;
var dp = line.SelectedItem;
}
}
I just don't find what I should do in order to call my StartDrag event when cliking directly on a point (So far I can do It when clicking on the line or the chart... but that's not what I'm looking for)
Do you have some idea for me?
You can define your events in LineSeries.DataPointStyle:
<chartingToolkit:LineSeries.DataPointStyle>
<Style>
<EventSetter Event="Control.MouseDown" Handler="StartDrag"/>
</Style>
</chartingToolkit:LineSeries.DataPointStyle>
Also, remember to set IsSelectionEnabled to False.
Reference: Adding events on WPF LineSeries DataPoint

How to put few custom controls dynamicaly inside a datatemplete on Mouse behavior

I have a Datatemplete for List-box Item in which I have a Grid with two columns using WPF. In the first column I want to put few customized controls(Buttons) dynamically using C# in code behind. I don't know how to start and from where should I start, can anybody please help me with some great inputs and examples. Any answer will be greatly appreciate.
Thanks in advance.
XAML code:
<ListBox x:Name="ListBoxItem"
Grid.Row="1"
SelectionMode="Extended"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
FocusVisualStyle="{x:Null}"
KeyboardNavigation.IsTabStop="False"
Background="DarkGray"
ItemsSource="{Binding}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel x:Name="ListContent"
IsItemsHost="True"
Width="500"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel LastChildFill="True"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<StackPanel DockPanel.Dock="Left"
Width="30"
Height="{Binding Height}">
<--Here I want to put few customize buttons in code behind-->
</StackPanel>
<Image x:Name="MainPage"
Stretch="UniformToFill"
Source="{Binding ImagePath}"
Height="{Binding Height}"
Width="{Binding Width}"/>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
You specified wanting to use code behind, so it would look something like this:
XAML:
<StackPanel Initialized="StackPanel_Initialized" .. />
Code behind:
using MyNamespace;
private void StackPanel_Initialized(object sender, EventArgs e)
{
MyControl newItem = new MyControl();
// Set any other properties
StackPanel parent = sender as StackPanel;
parent.Children.Add(newItem);
}
If you are looking for adding Controls inside a the First column of your grid then put a Panel inside the first column and in code behind add controls as child to that Panel. So as you mentioned in above that you are using DataTemplete then I would like to say that you can access that Panel something like:
Put the below codes inside the event where you wnt to add the controls.
ListBoxItem item = (ListBoxItem)(this.lst.ItemContainerGenerator.ContainerFromIndex(i));
ContentPresenter presenter = FindVisualChild<ContentPresenter>(item);
DataTemplate template = presenter.ContentTemplate;
StackPanel stack = (StackPanel)template.FindName("FirstColumn Panel Name", presenter);
and then call the below method:
private childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
return (childItem)child;
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}

Is it possible to bind a Canvas's Children property in XAML?

I'm a little surprised that it is not possible to set up a binding for Canvas.Children through XAML. I've had to resort to a code-behind approach that looks something like this:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
DesignerViewModel dvm = this.DataContext as DesignerViewModel;
dvm.Document.Items.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Items_CollectionChanged);
foreach (UIElement element in dvm.Document.Items)
designerCanvas.Children.Add(element);
}
private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
ObservableCollection<UIElement> collection = sender as ObservableCollection<UIElement>;
foreach (UIElement element in collection)
if (!designerCanvas.Children.Contains(element))
designerCanvas.Children.Add(element);
List<UIElement> removeList = new List<UIElement>();
foreach (UIElement element in designerCanvas.Children)
if (!collection.Contains(element))
removeList.Add(element);
foreach (UIElement element in removeList)
designerCanvas.Children.Remove(element);
}
I'd much rather just set up a binding in XAML like this:
<Canvas x:Name="designerCanvas"
Children="{Binding Document.Items}"
Width="{Binding Document.Width}"
Height="{Binding Document.Height}">
</Canvas>
Is there a way to accomplish this without resorting to a code-behind approach? I've done some googling on the subject, but haven't come up with much for this specific problem.
I don't like my current approach because it mucks up my nice Model-View-ViewModel by making the View aware of it's ViewModel.
<ItemsControl ItemsSource="{Binding Path=Circles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="White" Width="500" Height="500" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Fill="{Binding Path=Color, Converter={StaticResource colorBrushConverter}}" Width="25" Height="25" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
<Setter Property="Canvas.Left" Value="{Binding Path=X}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
Others have given extensible replies on how to do what you actually want to do already. I'll just explain why you couldn't bind Children directly.
The problem is very simple - data binding target cannot be a read-only property, and Panel.Children is read-only. There is no special handling for collections there. In contrast, ItemsControl.ItemsSource is a read/write property, even though it is of collection type - a rare occurence for a .NET class, but required so as to support the binding scenario.
ItemsControl is designed for creating dynamic collections of UI controls from other collections, even non-UI data collections.
You can template an ItemsControl to draw on a Canvas. The ideal way would involve setting the backing panel to a Canvas and then setting the Canvas.Left and Canvas.Top properties on the immediate children. I could not get this to work because ItemsControl wraps its children with containers and it is hard to set the Canvas properties on these containers.
Instead, I use a Grid as a bin for all of the items and draw them each on their own Canvas. There is some overhead with this approach.
<ItemsControl x:Name="Collection" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:MyPoint}">
<Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Ellipse Width="10" Height="10" Fill="Black" Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}"/>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here's the code behind that I used to set up the source collection:
List<MyPoint> points = new List<MyPoint>();
points.Add(new MyPoint(2, 100));
points.Add(new MyPoint(50, 20));
points.Add(new MyPoint(200, 200));
points.Add(new MyPoint(300, 370));
Collection.ItemsSource = points;
MyPoint is a custom class that behaves just like the System version. I created it to demonstrate that you can use your own custom classes.
One final detail: You can bind the ItemsSource property to any collection you want. For example:
<ItemsControls ItemsSource="{Binding Document.Items}"><!--etc, etc...-->
For further details about ItemsControl and how it works, check out these documents: MSDN Library Reference; Data Templating; Dr WPF's series on ItemsControl.
internal static class CanvasAssistant
{
#region Dependency Properties
public static readonly DependencyProperty BoundChildrenProperty =
DependencyProperty.RegisterAttached("BoundChildren", typeof (object), typeof (CanvasAssistant),
new FrameworkPropertyMetadata(null, onBoundChildrenChanged));
#endregion
public static void SetBoundChildren(DependencyObject dependencyObject, string value)
{
dependencyObject.SetValue(BoundChildrenProperty, value);
}
private static void onBoundChildrenChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
if (dependencyObject == null)
{
return;
}
var canvas = dependencyObject as Canvas;
if (canvas == null) return;
var objects = (ObservableCollection<UIElement>) e.NewValue;
if (objects == null)
{
canvas.Children.Clear();
return;
}
//TODO: Create Method for that.
objects.CollectionChanged += (sender, args) =>
{
if (args.Action == NotifyCollectionChangedAction.Add)
foreach (object item in args.NewItems)
{
canvas.Children.Add((UIElement) item);
}
if (args.Action == NotifyCollectionChangedAction.Remove)
foreach (object item in args.OldItems)
{
canvas.Children.Remove((UIElement) item);
}
};
foreach (UIElement item in objects)
{
canvas.Children.Add(item);
}
}
}
And using:
<Canvas x:Name="PART_SomeCanvas"
Controls:CanvasAssistant.BoundChildren="{TemplateBinding SomeItems}"/>
I don't believe its possible to use binding with the Children property. I actually tried to do that today and it errored on me like it did you.
The Canvas is a very rudimentary container. It really isn't designed for this kind of work. You should look into one of the many ItemsControls. You can bind your ViewModel's ObservableCollection of data models to their ItemsSource property and use DataTemplates to handle how each of the items is rendered in the control.
If you can't find an ItemsControl that renders your items in a satisfactory way, you might have to create a custom control that does what you need.

Categories

Resources