jazza
Topic Author
Posts: 5
Joined: 26 Sep 2019, 08:32

Referencing vectors as a bindable resource

27 Jan 2020, 08:11

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.
<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>
Following is a snippet from the XAML view that will reference this resource inside a control template.
<!-- 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}"/>
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?
 
jazza
Topic Author
Posts: 5
Joined: 26 Sep 2019, 08:32

Re: Referencing vectors as a bindable resource

28 Jan 2020, 03:58

I've managed to find a solution that allows me to clone the vector resource as follows.
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;
}
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.
 
User avatar
sfernandez
Site Admin
Posts: 1911
Joined: 22 Dec 2011, 19:20

Re: Referencing vectors as a bindable resource

29 Jan 2020, 10:20

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:
<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>
<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>
Hope this helps.
 
jazza
Topic Author
Posts: 5
Joined: 26 Sep 2019, 08:32

Re: Referencing vectors as a bindable resource

31 Jan 2020, 05:53

That sounds like a much better solution! Thanks!
 
User avatar
sfernandez
Site Admin
Posts: 1911
Joined: 22 Dec 2011, 19:20

Re: Referencing vectors as a bindable resource

31 Jan 2020, 13:01

Setting this as solved then.

Who is online

Users browsing this forum: blafollette and 3 guests