[runtime] Fix corlib out of date error with disabled COM
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / View / VirtualizedContainerService.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5 namespace System.Activities.Presentation.View
6 {
7     using System.Windows;
8
9     using System.Windows.Media;
10     using System.Windows.Media.Effects;
11     using System.Windows.Documents;
12
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;
25     using System.Runtime;
26     using System.Activities.Presentation.Debug;
27     using System.Diagnostics.CodeAnalysis;
28     using System.Xaml;
29     using System.ComponentModel;
30     using System.Globalization;
31     using System.Linq;
32
33     [Fx.Tag.XamlVisible(false)]
34     public class VirtualizedContainerService
35     {
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;
43
44         [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
45         public static readonly AttachableMemberIdentifier HintSizeName = new AttachableMemberIdentifier(typeof(VirtualizedContainerService), "HintSize");
46
47
48         public VirtualizedContainerService(EditingContext context)
49         {
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);
54
55             this.context.Services.Subscribe<DesignerView>((designerView) =>
56             {
57                 designerView.ScrollViewer.ScrollChanged += (sender, args) =>
58                 {
59                     if (!isWorking)
60                     {
61                         isWorking = true;
62                         PopulateItemsInView();
63                         isWorking = false;
64                     }
65                 };
66             });
67
68         }
69
70
71         ViewStateService ViewStateService
72         {
73             get
74             {
75                 if (this.viewStateService == null)
76                 {
77                     this.viewStateService = this.context.Services.GetService<ViewStateService>();
78                 }
79                 Fx.Assert(this.viewStateService != null, "ViewStateService should not be null");
80                 return this.viewStateService;
81             }
82         }
83
84         ViewService ViewService
85         {
86             get
87             {
88                 if (this.viewService == null)
89                 {
90                     this.viewService = this.context.Services.GetService<ViewService>();
91                 }
92                 Fx.Assert(this.viewService != null, "ViewService should not be null");
93                 return this.viewService;
94             }
95         }
96
97
98         DesignerView DesignerView
99         {
100             get
101             {
102                 if (this.designerView == null)
103                 {
104                     this.designerView = this.context.Services.GetService<DesignerView>();
105                 }
106                 Fx.Assert(this.designerView != null, "Designer view should not be null");
107                 return this.designerView;
108             }
109         }
110
111         public static object GetHintSize(object instance)
112         {
113             object viewState;
114             AttachablePropertyServices.TryGetProperty(instance, HintSizeName, out viewState);
115             return viewState;
116         }
117
118         public static void SetHintSize(object instance, object value)
119         {
120             AttachablePropertyServices.SetProperty(instance, HintSizeName, value);
121         }
122
123
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()
130         {
131             var designers = this.tree.GetNodesInside(GetViewerBounds());
132             bool rePopulationNeeded = false;
133             foreach (VirtualizingContainer container in designers)
134             {
135                 if (!container.IsPopulated)
136                 {
137                     container.Populate();
138                     rePopulationNeeded = true;
139                 }
140             }
141
142             if (rePopulationNeeded)
143             {
144                 Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, (Action)(() =>
145                 {
146                     PopulateItemsInView();
147                 }));
148             }
149         }
150
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)
154         {
155             Cursor oldCursor = Mouse.OverrideCursor;
156             Mouse.OverrideCursor = Cursors.Wait;
157             PopulateAllWithWaitCursor( oldCursor, onAfterPopulateAll);
158         }
159
160         void PopulateAllWithWaitCursor( Cursor oldCursor, Action onAfterPopulateAll)
161         {
162             var designers = this.tree.GetNodesInside(new Rect(0, 0, double.MaxValue, double.MaxValue));
163             bool rePopulationNeeded = false;
164             
165             foreach (VirtualizingContainer container in designers)
166             {
167                 if (!container.IsPopulated)
168                 {
169                     container.Populate();
170                     rePopulationNeeded = true;
171                 }
172             }
173
174             if (rePopulationNeeded)
175             {
176                 Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Loaded, (Action)(() =>
177                 {
178                     PopulateAllWithWaitCursor(oldCursor, onAfterPopulateAll);
179                 }));
180             }
181             else
182             {
183                 Mouse.OverrideCursor = oldCursor;
184                 if (onAfterPopulateAll != null)
185                 {
186                     onAfterPopulateAll();
187                 }
188             }
189         }
190
191
192         Rect GetViewerBounds()
193         {
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);
197             return viewerBounds;
198         }
199
200         bool IsVirtualiztionEnabled
201         {
202             get
203             {
204                 return true;
205             }
206         }
207
208         internal FrameworkElement QueryContainerForItem(ModelItem item)
209         {
210             if (null == item)
211             {
212                 throw FxTrace.Exception.ArgumentNull("item");
213             }
214             FrameworkElement element;
215             this.modelItemToContainer.TryGetValue(item, out element);
216             return element;
217         }
218
219         public UIElement GetContainer(ModelItem modelItem, ICompositeView sourceContainer)
220         {
221             FrameworkElement view = null;
222             if (IsVirtualiztionEnabled)
223             {
224                 view = new VirtualizingContainer(this, modelItem, sourceContainer);
225                 view.Loaded += this.OnViewLoaded;
226                 view.Unloaded += this.OnViewUnloaded;
227             }
228             else
229             {
230                 view = this.GetViewElement(modelItem, sourceContainer);
231             }
232             return view;
233         }
234
235         void OnViewLoaded(object view, RoutedEventArgs e)
236         {
237             var virtualView = view as VirtualizingContainer;
238             var viewElement = view as WorkflowViewElement;
239
240             if (null != virtualView && !this.modelItemToContainer.ContainsKey(virtualView.ModelItem))
241             {
242                 this.modelItemToContainer.Add(virtualView.ModelItem, virtualView);
243             }
244             else if (null != viewElement && !this.modelItemToContainer.ContainsKey(viewElement.ModelItem))
245             {
246                 this.modelItemToContainer.Add(viewElement.ModelItem, viewElement);
247             }
248         }
249
250         void OnViewUnloaded(object view, RoutedEventArgs e)
251         {
252             var virtualView = view as VirtualizingContainer;
253             var viewElement = view as WorkflowViewElement;
254
255             if (null != virtualView && this.modelItemToContainer.ContainsKey(virtualView.ModelItem))
256             {
257                 this.modelItemToContainer.Remove(virtualView.ModelItem);
258             }
259             else if (null != viewElement && this.modelItemToContainer.ContainsKey(viewElement.ModelItem))
260             {
261                 this.modelItemToContainer.Remove(viewElement.ModelItem);
262             }
263         }
264
265
266         public WorkflowViewElement GetViewElement(ModelItem modelItem, ICompositeView sourceContainer)
267         {
268             WorkflowViewElement itemView = (WorkflowViewElement)this.ViewService.GetView(modelItem);
269             if (null != sourceContainer)
270             {
271                 DragDropHelper.SetCompositeView(itemView, (UIElement)sourceContainer);
272             }
273             itemView.Loaded += this.OnViewLoaded;
274             itemView.Unloaded += this.OnViewUnloaded;
275
276             return itemView;
277         }
278
279
280
281
282         internal class VirtualizingContainer : Border
283         {
284             VirtualizedContainerService containerService;
285             ModelItem modelItem;
286             ICompositeView sourceContainer;
287             UIElement designerRoot;
288             bool isPopulated = false;
289             Size defaultContainerSize = new Size(20, 20);
290             Rect oldBounds;
291             VirtualizingContainer parentContainer;
292             List<VirtualizingContainer> children;
293
294             public ModelItem ModelItem
295             {
296                 get
297                 {
298                     return this.modelItem;
299                 }
300             }
301
302             public ICompositeView ICompositeView
303             {
304                 get
305                 {
306                     return this.sourceContainer;
307                 }
308             }
309
310             public IEnumerable<VirtualizingContainer> ChildContainers
311             {
312                 get { return this.children; }
313             }
314
315             [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
316             public VirtualizingContainer(VirtualizedContainerService containerService, ModelItem modelItem, ICompositeView sourceContainer)
317             {
318                 this.containerService = containerService;
319                 this.modelItem = modelItem;
320                 this.sourceContainer = sourceContainer;
321                 this.Focusable = false;
322                 this.BorderThickness = new Thickness(1);
323                 SetupPlaceHolder();
324                 this.children = new List<VirtualizingContainer>();
325                 this.Unloaded += (sender, args) =>
326                 {
327                     this.containerService.tree.Remove(this);
328                     this.oldBounds = new Rect(0, 0, 0, 0);
329                     UnRegisterFromParentContainer();
330                 };
331
332                 this.Loaded += (sender, args) =>
333                 {
334                     RegisterWithParentContainer();
335                 };
336
337             }
338
339             private void SetupPlaceHolder()
340             {
341                 string sizeString = (string)(VirtualizedContainerService.GetHintSize(this.modelItem.GetCurrentValue()));
342                 Size? size = null;
343                 if (!string.IsNullOrEmpty(sizeString))
344                 {
345                     size = Size.Parse(sizeString);
346                 }
347                 if (size == null)
348                 {
349                     size = defaultContainerSize;
350                 }
351                 this.MinWidth = size.Value.Width;
352                 this.MinHeight = size.Value.Height;
353             }
354
355             protected override Size ArrangeOverride(Size arrangeBounds)
356             {
357                 this.Dispatcher.BeginInvoke(DispatcherPriority.Render, (Action)(() =>
358                 {
359                     AddToQuadTree();
360                 }));
361                 return base.ArrangeOverride(arrangeBounds);
362             }
363
364
365             void RegisterWithParentContainer()
366             {
367                 DependencyObject parent = VisualTreeHelper.GetParent(this);
368                 while (null != parent && !(parent is VirtualizingContainer))
369                 {
370                     parent = VisualTreeHelper.GetParent(parent);
371                 }
372                 this.parentContainer = parent as VirtualizingContainer;
373                 if (parentContainer != null)
374                 {
375                     if (!parentContainer.children.Contains(this))
376                     {
377                         parentContainer.children.Add(this);
378                     }
379                 }
380             }
381
382             void UnRegisterFromParentContainer()
383             {
384                 if (parentContainer != null)
385                 {
386                     parentContainer.children.Remove(this);
387                     this.parentContainer = null;
388                 }
389             }
390
391             private void AddToQuadTree()
392             {
393                 try
394                 {
395                     Point currentPoint = GetPosition();
396                     if (this.ActualHeight > 0 && this.ActualWidth > 0)
397                     {
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);
401                         if (isInView)
402                         {
403                             this.Populate();
404                             currentPoint = GetPosition();
405                             bounds = new Rect(currentPoint, new Size(this.ActualWidth, this.ActualHeight));
406                         }
407                         else
408                         {
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)
412                             {
413                                 if (this.BorderBrush != SystemColors.GrayTextBrush)
414                                 {
415                                     this.BorderBrush = SystemColors.GrayTextBrush;
416                                 }
417                             }
418                          
419
420
421                         }
422
423                         if (this.oldBounds != bounds)
424                         {
425
426                             this.containerService.tree.Remove(this);
427                             this.containerService.tree.Insert(this, bounds);
428                             if (this.oldBounds != Rect.Empty)
429                             {
430                                 this.Dispatcher.BeginInvoke(DispatcherPriority.Render, (Action)(() =>
431                                 {
432                                     foreach (VirtualizingContainer childContainer in this.children)
433                                     {
434                                         // if there were designers registered under the old bounds let them re-register
435                                         childContainer.AddToQuadTree();
436                                     }
437                                 }));
438                             }
439
440                             this.oldBounds = bounds;
441                         }
442                         
443
444                         if (this.IsPopulated)
445                         {
446                             VirtualizedContainerService.SetHintSize(this.modelItem.GetCurrentValue(), bounds.Size.ToString(CultureInfo.InvariantCulture));
447                         }
448                     }
449                 }
450                 catch (InvalidOperationException)
451                 {
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);
455                 }
456             }
457
458             UIElement DesignerRoot
459             {
460                 get
461                 {
462                     if (this.designerRoot == null)
463                     {
464                         this.designerRoot = this.containerService.DesignerView.scrollableContent;
465                     }
466                     Fx.Assert(this.designerRoot != null, "Designer's scrollable content should not be null now ");
467                     return this.designerRoot;
468                 }
469             }
470
471             private Point GetPosition()
472             {
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));
476                 return currentPoint;
477             }
478
479
480
481             public bool IsPopulated
482             {
483                 get
484                 {
485                     return this.isPopulated;
486                 }
487             }
488
489             internal void Populate()
490             {
491                 if (!IsPopulated)
492                 {
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;
498                     isPopulated = true;
499                 }
500             }
501
502         }
503
504         internal static UIElement TryGetVirtualizedElement(UIElement element)
505         {
506             if (element is VirtualizedContainerService.VirtualizingContainer)
507             {
508                 if (((VirtualizedContainerService.VirtualizingContainer)element).IsPopulated)
509                 {
510                     return ((VirtualizedContainerService.VirtualizingContainer)element).Child;
511                 }
512             }
513             return element;
514         }
515     }
516 }
517
518
519
520