1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
5 namespace System.Activities.Presentation.View
9 using System.Windows.Media;
10 using System.Windows.Media.Effects;
11 using System.Windows.Documents;
13 using System.Activities.Presentation;
14 using System.Activities.Presentation.Model;
15 using System.Activities.Presentation.Documents;
16 using System.Activities.Presentation.Services;
17 using System.Activities.Presentation.View;
18 using System.Collections.ObjectModel;
19 using System.Collections;
20 using System.Collections.Generic;
21 using System.Windows.Controls;
22 using System.Windows.Threading;
23 using System.Windows.Shapes;
24 using System.Windows.Input;
26 using System.Activities.Presentation.Debug;
27 using System.Diagnostics.CodeAnalysis;
29 using System.ComponentModel;
30 using System.Globalization;
33 [Fx.Tag.XamlVisible(false)]
34 public class VirtualizedContainerService
36 EditingContext context;
37 QuadTree<VirtualizingContainer> tree;
38 bool isWorking = false;
39 DesignerView designerView;
40 ViewStateService viewStateService;
41 ViewService viewService;
42 IDictionary<ModelItem, FrameworkElement> modelItemToContainer;
44 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
45 public static readonly AttachableMemberIdentifier HintSizeName = new AttachableMemberIdentifier(typeof(VirtualizedContainerService), "HintSize");
48 public VirtualizedContainerService(EditingContext context)
50 this.context = context;
51 this.tree = new QuadTree<VirtualizingContainer>();
52 this.modelItemToContainer = new Dictionary<ModelItem, FrameworkElement>();
53 this.tree.Bounds = new Rect(0, 0, int.MaxValue, int.MaxValue);
55 this.context.Services.Subscribe<DesignerView>((designerView) =>
57 designerView.ScrollViewer.ScrollChanged += (sender, args) =>
62 PopulateItemsInView();
71 ViewStateService ViewStateService
75 if (this.viewStateService == null)
77 this.viewStateService = this.context.Services.GetService<ViewStateService>();
79 Fx.Assert(this.viewStateService != null, "ViewStateService should not be null");
80 return this.viewStateService;
84 ViewService ViewService
88 if (this.viewService == null)
90 this.viewService = this.context.Services.GetService<ViewService>();
92 Fx.Assert(this.viewService != null, "ViewService should not be null");
93 return this.viewService;
98 DesignerView DesignerView
102 if (this.designerView == null)
104 this.designerView = this.context.Services.GetService<DesignerView>();
106 Fx.Assert(this.designerView != null, "Designer view should not be null");
107 return this.designerView;
111 public static object GetHintSize(object instance)
114 AttachablePropertyServices.TryGetProperty(instance, HintSizeName, out viewState);
118 public static void SetHintSize(object instance, object value)
120 AttachablePropertyServices.SetProperty(instance, HintSizeName, value);
124 // This method populates all items in the current scroll region.
125 // we first get the virtualizing containers inthe current scroll region
126 // ask them to populate the content, and then wait for a layout pass
127 // so that the first round of population can cause more populations
128 // we do this till all items in the current view are completely populated.
129 private void PopulateItemsInView()
131 var designers = this.tree.GetNodesInside(GetViewerBounds());
132 bool rePopulationNeeded = false;
133 foreach (VirtualizingContainer container in designers)
135 if (!container.IsPopulated)
137 container.Populate();
138 rePopulationNeeded = true;
142 if (rePopulationNeeded)
144 Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, (Action)(() =>
146 PopulateItemsInView();
151 // This method populates all items in the entire designer canvas
152 // this uses the same technique Populateitemsinview uses to bring items into view.
153 internal void BeginPopulateAll(Action onAfterPopulateAll)
155 Cursor oldCursor = Mouse.OverrideCursor;
156 Mouse.OverrideCursor = Cursors.Wait;
157 PopulateAllWithWaitCursor( oldCursor, onAfterPopulateAll);
160 void PopulateAllWithWaitCursor( Cursor oldCursor, Action onAfterPopulateAll)
162 var designers = this.tree.GetNodesInside(new Rect(0, 0, double.MaxValue, double.MaxValue));
163 bool rePopulationNeeded = false;
165 foreach (VirtualizingContainer container in designers)
167 if (!container.IsPopulated)
169 container.Populate();
170 rePopulationNeeded = true;
174 if (rePopulationNeeded)
176 Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Loaded, (Action)(() =>
178 PopulateAllWithWaitCursor(oldCursor, onAfterPopulateAll);
183 Mouse.OverrideCursor = oldCursor;
184 if (onAfterPopulateAll != null)
186 onAfterPopulateAll();
192 Rect GetViewerBounds()
194 ScrollViewer parentView = this.DesignerView.ScrollViewer;
195 Rect viewerBounds = new Rect(parentView.HorizontalOffset, parentView.VerticalOffset, parentView.ViewportWidth, parentView.ViewportHeight);
196 viewerBounds.Scale(1 / this.designerView.ZoomFactor, 1 / this.designerView.ZoomFactor);
200 bool IsVirtualiztionEnabled
208 internal FrameworkElement QueryContainerForItem(ModelItem item)
212 throw FxTrace.Exception.ArgumentNull("item");
214 FrameworkElement element;
215 this.modelItemToContainer.TryGetValue(item, out element);
219 public UIElement GetContainer(ModelItem modelItem, ICompositeView sourceContainer)
221 FrameworkElement view = null;
222 if (IsVirtualiztionEnabled)
224 view = new VirtualizingContainer(this, modelItem, sourceContainer);
225 view.Loaded += this.OnViewLoaded;
226 view.Unloaded += this.OnViewUnloaded;
230 view = this.GetViewElement(modelItem, sourceContainer);
235 void OnViewLoaded(object view, RoutedEventArgs e)
237 var virtualView = view as VirtualizingContainer;
238 var viewElement = view as WorkflowViewElement;
240 if (null != virtualView && !this.modelItemToContainer.ContainsKey(virtualView.ModelItem))
242 this.modelItemToContainer.Add(virtualView.ModelItem, virtualView);
244 else if (null != viewElement && !this.modelItemToContainer.ContainsKey(viewElement.ModelItem))
246 this.modelItemToContainer.Add(viewElement.ModelItem, viewElement);
250 void OnViewUnloaded(object view, RoutedEventArgs e)
252 var virtualView = view as VirtualizingContainer;
253 var viewElement = view as WorkflowViewElement;
255 if (null != virtualView && this.modelItemToContainer.ContainsKey(virtualView.ModelItem))
257 this.modelItemToContainer.Remove(virtualView.ModelItem);
259 else if (null != viewElement && this.modelItemToContainer.ContainsKey(viewElement.ModelItem))
261 this.modelItemToContainer.Remove(viewElement.ModelItem);
266 public WorkflowViewElement GetViewElement(ModelItem modelItem, ICompositeView sourceContainer)
268 WorkflowViewElement itemView = (WorkflowViewElement)this.ViewService.GetView(modelItem);
269 if (null != sourceContainer)
271 DragDropHelper.SetCompositeView(itemView, (UIElement)sourceContainer);
273 itemView.Loaded += this.OnViewLoaded;
274 itemView.Unloaded += this.OnViewUnloaded;
282 internal class VirtualizingContainer : Border
284 VirtualizedContainerService containerService;
286 ICompositeView sourceContainer;
287 UIElement designerRoot;
288 bool isPopulated = false;
289 Size defaultContainerSize = new Size(20, 20);
291 VirtualizingContainer parentContainer;
292 List<VirtualizingContainer> children;
294 public ModelItem ModelItem
298 return this.modelItem;
302 public ICompositeView ICompositeView
306 return this.sourceContainer;
310 public IEnumerable<VirtualizingContainer> ChildContainers
312 get { return this.children; }
315 [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
316 public VirtualizingContainer(VirtualizedContainerService containerService, ModelItem modelItem, ICompositeView sourceContainer)
318 this.containerService = containerService;
319 this.modelItem = modelItem;
320 this.sourceContainer = sourceContainer;
321 this.Focusable = false;
322 this.BorderThickness = new Thickness(1);
324 this.children = new List<VirtualizingContainer>();
325 this.Unloaded += (sender, args) =>
327 this.containerService.tree.Remove(this);
328 this.oldBounds = new Rect(0, 0, 0, 0);
329 UnRegisterFromParentContainer();
332 this.Loaded += (sender, args) =>
334 RegisterWithParentContainer();
339 private void SetupPlaceHolder()
341 string sizeString = (string)(VirtualizedContainerService.GetHintSize(this.modelItem.GetCurrentValue()));
343 if (!string.IsNullOrEmpty(sizeString))
345 size = Size.Parse(sizeString);
349 size = defaultContainerSize;
351 this.MinWidth = size.Value.Width;
352 this.MinHeight = size.Value.Height;
355 protected override Size ArrangeOverride(Size arrangeBounds)
357 this.Dispatcher.BeginInvoke(DispatcherPriority.Render, (Action)(() =>
361 return base.ArrangeOverride(arrangeBounds);
365 void RegisterWithParentContainer()
367 DependencyObject parent = VisualTreeHelper.GetParent(this);
368 while (null != parent && !(parent is VirtualizingContainer))
370 parent = VisualTreeHelper.GetParent(parent);
372 this.parentContainer = parent as VirtualizingContainer;
373 if (parentContainer != null)
375 if (!parentContainer.children.Contains(this))
377 parentContainer.children.Add(this);
382 void UnRegisterFromParentContainer()
384 if (parentContainer != null)
386 parentContainer.children.Remove(this);
387 this.parentContainer = null;
391 private void AddToQuadTree()
395 Point currentPoint = GetPosition();
396 if (this.ActualHeight > 0 && this.ActualWidth > 0)
398 Rect bounds = new Rect(currentPoint, new Size(this.ActualWidth, this.ActualHeight));
399 Rect viewerBounds = this.containerService.GetViewerBounds();
400 bool isInView = viewerBounds.IntersectsWith(bounds) || viewerBounds.Contains(bounds) || bounds.Contains(viewerBounds);
404 currentPoint = GetPosition();
405 bounds = new Rect(currentPoint, new Size(this.ActualWidth, this.ActualHeight));
409 // a previous Arrange could have led to adding this to the quadtree already.
410 // so remove previos instances from quadtree.
411 if (!this.isPopulated)
413 if (this.BorderBrush != SystemColors.GrayTextBrush)
415 this.BorderBrush = SystemColors.GrayTextBrush;
423 if (this.oldBounds != bounds)
426 this.containerService.tree.Remove(this);
427 this.containerService.tree.Insert(this, bounds);
428 if (this.oldBounds != Rect.Empty)
430 this.Dispatcher.BeginInvoke(DispatcherPriority.Render, (Action)(() =>
432 foreach (VirtualizingContainer childContainer in this.children)
434 // if there were designers registered under the old bounds let them re-register
435 childContainer.AddToQuadTree();
440 this.oldBounds = bounds;
444 if (this.IsPopulated)
446 VirtualizedContainerService.SetHintSize(this.modelItem.GetCurrentValue(), bounds.Size.ToString(CultureInfo.InvariantCulture));
450 catch (InvalidOperationException)
452 // This can happen if an arrange happened within the child of the container, when not in the visual tree
453 // for the current breadcrumb root. The GetTransform will throw invalidoperation in this case.
454 this.containerService.tree.Remove(this);
458 UIElement DesignerRoot
462 if (this.designerRoot == null)
464 this.designerRoot = this.containerService.DesignerView.scrollableContent;
466 Fx.Assert(this.designerRoot != null, "Designer's scrollable content should not be null now ");
467 return this.designerRoot;
471 private Point GetPosition()
473 GeneralTransform generalTransform1 = this.TransformToAncestor((Visual)this.DesignerRoot);
474 // Get current position by transforming origin using the current transform.
475 Point currentPoint = generalTransform1.Transform(new Point(0, 0));
481 public bool IsPopulated
485 return this.isPopulated;
489 internal void Populate()
493 this.BorderBrush = Brushes.Transparent;
494 this.BorderThickness = new Thickness(0);
495 this.Child = this.containerService.GetViewElement(this.ModelItem, this.ICompositeView);
496 this.MinHeight = defaultContainerSize.Height;
497 this.MinWidth = defaultContainerSize.Width;
504 internal static UIElement TryGetVirtualizedElement(UIElement element)
506 if (element is VirtualizedContainerService.VirtualizingContainer)
508 if (((VirtualizedContainerService.VirtualizingContainer)element).IsPopulated)
510 return ((VirtualizedContainerService.VirtualizingContainer)element).Child;