Referencing vectors as a bindable resource
I am currently trying to implement a view that displays a list of buttons based on bound data from an observable collection. The image for each button is defined in the application resource dictionary as follows.
Following is a snippet from the XAML view that will reference this resource inside a control template.
I have also created a converter that takes an enumerated value for a button from the view model and then searches the application resource dictionary for a matching resource. The pointer to this resource is returned to the control template and the button is then rendered correctly.
There are issues however when I destroy/re-create the view or modify the observable collection. Re-creating the view results in a stack overflow that seemingly originates from an 'AncestorChanged' callback being called infinitely. Clearly I shouldn't be passing a pointer to a static resource to several widgets. I suppose I should be cloning the resource and then passing it to the control template but its not clear to me how to achieve that either.
Is there a better way to reference my vector resources?
Code: Select all
<Canvas x:Key="Btn_A" Width="136" Height="136" x:Shared="false">
<Path Fill="#ffffffff" Data="F1 M 0.000,68.276 C 0.000,61.981 0.812,55.922 2.436,50.102 C 4.059,44.282 6.357,38.848 9.331,33.800 C 12.303,28.754 15.862,24.159 20.010,20.012 C 24.157,15.864 28.766,12.305 33.837,9.331 C 38.908,6.359 44.354,4.061 50.175,2.438 C 55.995,0.814 62.054,0.000 68.349,0.000 C 74.645,0.000 80.702,0.814 86.523,2.438 C 92.343,4.061 97.777,6.359 102.824,9.331 C 107.870,12.305 112.479,15.864 116.651,20.012 C 120.822,24.159 124.382,28.754 127.330,33.800 C 130.278,38.848 132.564,44.282 134.188,50.102 C 135.811,55.922 136.623,61.981 136.623,68.276 C 136.623,74.571 135.811,80.630 134.188,86.450 C 132.564,92.270 130.278,97.717 127.330,102.787 C 124.382,107.859 120.822,112.469 116.651,116.614 C 112.479,120.762 107.870,124.321 102.824,127.293 C 97.777,130.267 92.343,132.564 86.523,134.188 C 80.702,135.812 74.645,136.623 68.349,136.623 C 62.054,136.623 55.995,135.812 50.175,134.188 C 44.354,132.564 38.908,130.267 33.837,127.293 C 28.766,124.321 24.157,120.762 20.010,116.614 C 15.862,112.469 12.303,107.859 9.331,102.787 C 6.357,97.717 4.059,92.270 2.436,86.450 C 0.812,80.630 0.000,74.571 0.000,68.276 Z"/>
<Path Fill="#ff221e1f" Data="F1 M 54.736,77.581 L 81.888,77.581 L 68.312,40.130 L 54.736,77.581 Z M 61.758,22.574 L 74.866,22.574 L 106.231,108.010 L 93.006,108.010 L 86.336,89.752 L 50.289,89.752 L 43.735,108.010 L 30.392,108.010 L 61.758,22.574 Z"/>
</Canvas>
Code: Select all
<!-- Defines the custom layout of a tooltip button -->
<ControlTemplate x:Key="Template_Button_Tooltip" TargetType="Button">
<StackPanel Style="{StaticResource Style_TooltipRoot}">
<Viewbox Name="Viewbox" Style="{StaticResource Style_TooltipIcon}">
<!-- This control appears to be the only way to instantiate a static resource -->
<ContentControl Content="{Binding Button, Converter={StaticResource buttonConverter}}"/>
</Viewbox>
</StackPanel>
</ControlTemplate>
<!-- Assigns a visual representation to each instance of a tooltip in the tooltips collection -->
<DataTemplate x:Key="TooltipsItemTemplate">
<Button Template="{StaticResource Template_Button_Tooltip}"/>
</DataTemplate>
</Grid.Resources>
<ItemsControl ItemsSource="{Binding Tooltips}" Focusable="True" Template="{StaticResource TooltipListTemplate}" ItemTemplate="{StaticResource TooltipsItemTemplate}"/>
There are issues however when I destroy/re-create the view or modify the observable collection. Re-creating the view results in a stack overflow that seemingly originates from an 'AncestorChanged' callback being called infinitely. Clearly I shouldn't be passing a pointer to a static resource to several widgets. I suppose I should be cloning the resource and then passing it to the control template but its not clear to me how to achieve that either.
Is there a better way to reference my vector resources?
Re: Referencing vectors as a bindable resource
I've managed to find a solution that allows me to clone the vector resource as follows.
While this approach solves my issue, it is obviously very expensive. If anyone has any advice on a better approach to using a data bound vector resource in a control template, it would be much appreciated.
Code: Select all
Noesis::Ptr<Noesis::BaseComponent> CloneResource(Noesis::FrameworkElement* resource)
{
// NOTE: This function assumes the following layout for each resource
// <Canvas x:Key="Btn_A" Width="136" Height="136" x:Shared="false">
// <Path Fill="#ffffffff" Data="F1 M 0.000,68.276 C 0.000,61.981 0.812, Z"/>
// <!-- Additional paths -->
// </Canvas>
Noesis::Ptr<Noesis::Canvas> clone = Noesis::MakePtr<Noesis::Canvas>();
clone->SetWidth(resource->GetWidth());
clone->SetHeight(resource->GetHeight());
int childrenCount = Noesis::VisualTreeHelper::GetChildrenCount(resource);
if (childrenCount > 0)
{
for (int i = 0; i < childrenCount; i++)
{
auto child = (Noesis::Path*)Noesis::VisualTreeHelper::GetChild(resource, i);
auto path = Noesis::MakePtr<Noesis::Path>();
path->SetData(child->GetData()->Clone());
path->SetFill(child->GetFill());
clone->GetChildren()->Add(path);
}
}
return clone;
}
-
sfernandez
Site Admin
- Posts: 2997
- Joined:
Re: Referencing vectors as a bindable resource
If you want to share a visual tree among several controls you should use a ControlTemplate/DataTemplate. That will do the clone part automatically when applied to the control.
So you can have defined ControlTemplates in your dictionary and instead of setting the Content property set the Template property:
Hope this helps.
So you can have defined ControlTemplates in your dictionary and instead of setting the Content property set the Template property:
Code: Select all
<ControlTemplate x:Key="Btn_A" TargetType="Control">
<Canvas Width="136" Height="136">
<Path Fill="#ffffffff" Data="F1 M 0.000,68.276 C 0.000,61.981 0.812,55.922 2.436,50.102 C 4.059,44.282 6.357,38.848 9.331,33.800 C 12.303,28.754 15.862,24.159 20.010,20.012 C 24.157,15.864 28.766,12.305 33.837,9.331 C 38.908,6.359 44.354,4.061 50.175,2.438 C 55.995,0.814 62.054,0.000 68.349,0.000 C 74.645,0.000 80.702,0.814 86.523,2.438 C 92.343,4.061 97.777,6.359 102.824,9.331 C 107.870,12.305 112.479,15.864 116.651,20.012 C 120.822,24.159 124.382,28.754 127.330,33.800 C 130.278,38.848 132.564,44.282 134.188,50.102 C 135.811,55.922 136.623,61.981 136.623,68.276 C 136.623,74.571 135.811,80.630 134.188,86.450 C 132.564,92.270 130.278,97.717 127.330,102.787 C 124.382,107.859 120.822,112.469 116.651,116.614 C 112.479,120.762 107.870,124.321 102.824,127.293 C 97.777,130.267 92.343,132.564 86.523,134.188 C 80.702,135.812 74.645,136.623 68.349,136.623 C 62.054,136.623 55.995,135.812 50.175,134.188 C 44.354,132.564 38.908,130.267 33.837,127.293 C 28.766,124.321 24.157,120.762 20.010,116.614 C 15.862,112.469 12.303,107.859 9.331,102.787 C 6.357,97.717 4.059,92.270 2.436,86.450 C 0.812,80.630 0.000,74.571 0.000,68.276 Z"/>
<Path Fill="#ff221e1f" Data="F1 M 54.736,77.581 L 81.888,77.581 L 68.312,40.130 L 54.736,77.581 Z M 61.758,22.574 L 74.866,22.574 L 106.231,108.010 L 93.006,108.010 L 86.336,89.752 L 50.289,89.752 L 43.735,108.010 L 30.392,108.010 L 61.758,22.574 Z"/>
</Canvas>
</ControlTemplate>
Code: Select all
<ControlTemplate x:Key="Template_Button_Tooltip" TargetType="Button">
<StackPanel Style="{StaticResource Style_TooltipRoot}">
<Viewbox Name="Viewbox" Style="{StaticResource Style_TooltipIcon}">
<Control Template="{Binding Button, Converter={StaticResource buttonConverter}}"/>
</Viewbox>
</StackPanel>
</ControlTemplate>
Re: Referencing vectors as a bindable resource
That sounds like a much better solution! Thanks!
-
sfernandez
Site Admin
- Posts: 2997
- Joined:
Re: Referencing vectors as a bindable resource
Setting this as solved then.
Who is online
Users browsing this forum: No registered users and 13 guests