Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
TemplateProviderSelector.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Runtime.CompilerServices;
7 using System.Windows;
8 using System.Windows.Controls;
9 using System.Windows.Media;
10 
11 using SiliconStudio.Core.Extensions;
12 
13 namespace SiliconStudio.Presentation.View
14 {
15  /// <summary>
16  /// An implementation of <see cref="DataTemplateSelector"/> that can select a template from a set of statically registered <see cref="ITemplateProvider"/> objects.
17  /// </summary>
19  {
20 
21  /// <summary>
22  /// The list of all template providers registered for the <see cref="TemplateProviderSelector"/>, indexed by their name.
23  /// </summary>
24  private readonly List<ITemplateProvider> templateProviders = new List<ITemplateProvider>();
25 
26  /// <summary>
27  /// A hashset of template provider names, used only to ensure unicity.
28  /// </summary>
29  private readonly HashSet<string> templateProviderNames = new HashSet<string>();
30 
31  /// <summary>
32  /// A map of all providers that have already been used for each object, indexed by <see cref="Guid"/>.
33  /// </summary>
34  private readonly ConditionalWeakTable<object, List<string>> usedProviders = new ConditionalWeakTable<object, List<string>>();
35 
36  /// <summary>
37  /// A map containing the last container for a given object.
38  /// </summary>
39  private readonly ConditionalWeakTable<object, WeakReference> lastContainers = new ConditionalWeakTable<object, WeakReference>();
40 
41  /// <summary>
42  /// Registers the given template into the static <see cref="TemplateProviderSelector"/>.
43  /// </summary>
44  /// <param name="templateProvider"></param>
45  public void RegisterTemplateProvider(ITemplateProvider templateProvider)
46  {
47  if (templateProvider == null) throw new ArgumentNullException("templateProvider");
48 
49  if (templateProviderNames.Contains(templateProvider.Name))
50  throw new InvalidOperationException("A template provider with the same name has already been registered in this template selector.");
51 
52  InsertTemplateProvider(templateProviders, templateProvider, new List<ITemplateProvider>());
53  templateProviderNames.Add(templateProvider.Name);
54  }
55 
56  /// <summary>
57  /// Unregisters the given template into the static <see cref="TemplateProviderSelector"/>.
58  /// </summary>
59  /// <param name="templateProvider"></param>
60  public void UnregisterTemplateProvider(ITemplateProvider templateProvider)
61  {
62  if (templateProviderNames.Remove(templateProvider.Name))
63  {
64  templateProviders.Remove(templateProvider);
65  }
66  }
67 
68  public override DataTemplate SelectTemplate(object item, DependencyObject container)
69  {
70  if (item == null)
71  return null;
72 
73  var element = container as FrameworkElement;
74  if (element == null)
75  throw new ArgumentException(@"Container must be of type FrameworkElement", "container");
76 
77  var provider = FindTemplateProvider(item, container);
78  if (provider == null)
79  return null;
80 
81  var template = provider.Template;
82  // We set the template we found into the content presenter itself to avoid re-entering the template selector if the property is refreshed.
83  //var contentPresenter = container as ContentPresenter;
84  //if (contentPresenter != null)
85  //{
86  // contentPresenter.ContentTemplate = template;
87  //}
88  return template;
89  }
90 
91  private static void InsertTemplateProvider(List<ITemplateProvider> list, ITemplateProvider templateProvider, List<ITemplateProvider> movedItems)
92  {
93  movedItems.Add(templateProvider);
94  // Find the first index where we can insert
95  int insertIndex = 1 + list.LastIndexOf(x => x.CompareTo(templateProvider) < 0);
96  list.Insert(insertIndex, templateProvider);
97  // Every following providers may have an override rule against the new template provider, we must potentially resort them.
98  for (int i = insertIndex + 1; i < list.Count; ++i)
99  {
100  var followingProvider = list[i];
101  if (followingProvider.CompareTo(templateProvider) < 0)
102  {
103  if (!movedItems.Contains(followingProvider))
104  {
105  list.RemoveAt(i);
106  InsertTemplateProvider(list, followingProvider, movedItems);
107  }
108  }
109  }
110  }
111 
112  private ITemplateProvider FindTemplateProvider(object item, DependencyObject container)
113  {
114  List<string> usedProvidersForItem = usedProviders.GetOrCreateValue(item);
115 
116  bool shouldClear = true;
117  WeakReference lastContainer;
118  // We check if this item has been templated recently.
119  if (lastContainers.TryGetValue(item, out lastContainer) && lastContainer.IsAlive)
120  {
121  // If so, check if the last container used is a parent of the container to use now.
122  DependencyObject parent = VisualTreeHelper.GetParent(container);
123  while (parent != null)
124  {
125  // If so, we are applying template recursively. We want don't want to use the same template
126  // provider that the previous time, so we don't clear the list of providers already used.
127  if (Equals(lastContainer.Target, parent))
128  {
129  shouldClear = false;
130  break;
131  }
132  parent = VisualTreeHelper.GetParent(parent);
133  }
134  }
135  // In any other case, we clear the list of used providers.
136  if (shouldClear)
137  {
138  usedProvidersForItem.Clear();
139  }
140 
141  lastContainers.Remove(item);
142 
143  var availableSelectors = templateProviders.Where(x => x.Match(item)).ToList();
144 
145  ITemplateProvider result = availableSelectors.FirstOrDefault(x => !usedProvidersForItem.Contains(x.Name));
146 
147  if (result != null)
148  {
149  usedProvidersForItem.Add(result.Name);
150  lastContainers.Add(item, new WeakReference(container));
151  }
152  return result;
153  }
154  }
155 }
override DataTemplate SelectTemplate(object item, DependencyObject container)
string Name
Gets an identifier name for this instance of ITemplateProvider.
void UnregisterTemplateProvider(ITemplateProvider templateProvider)
Unregisters the given template into the static TemplateProviderSelector.
An interface for a class that can provide a template for a given object that matches some prerequisit...
void RegisterTemplateProvider(ITemplateProvider templateProvider)
Registers the given template into the static TemplateProviderSelector.
An implementation of DataTemplateSelector that can select a template from a set of statically registe...