Merge pull request #3622 from rolfbjarne/remove-stray-file
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / View / DesignerView.xaml.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5 namespace System.Activities.Presentation.View
6 {
7     using System.Activities.Presentation.View;
8     using System.Activities.Presentation.Model;
9     using System.Activities.Presentation.Services;
10     using System.Activities.Presentation.Xaml;
11     using System.Activities.Presentation.Hosting;
12     using System.Collections;
13     using System.Collections.ObjectModel;
14     using System.ComponentModel;
15     using System.Diagnostics.CodeAnalysis;
16     using System.Globalization;
17     using System.IO;
18     using System.IO.Packaging;
19     using System.Printing;
20     using System.Reflection;
21     using System.Runtime;
22     using System.Windows;
23     using System.Windows.Controls;
24     using System.Windows.Controls.Primitives;
25     using System.Windows.Data;
26     using System.Windows.Documents;
27     using System.Windows.Input;
28     using System.Windows.Interop;
29     using System.Windows.Media;
30     using System.Windows.Media.Imaging;
31     using System.Windows.Threading;
32     using System.Windows.Xps;
33     using System.Windows.Xps.Packaging;
34     using System.Linq;
35     using System.Windows.Shapes;
36     using System.Collections.Generic;
37     using System.Activities.Presentation.Validation;
38     using System.Activities.Presentation.Internal.PropertyEditing;
39     using System.ServiceModel.Activities;
40     using Microsoft.Tools.Common;
41     using System.Activities.Presentation.Sqm;
42
43     // <summary>
44     // Interaction logic for DesignerView.xaml
45     // </summary>
46     [Fx.Tag.XamlVisible(false)]
47     public partial class DesignerView : UserControl
48     {
49         public static readonly DependencyProperty RootDesignerProperty =
50             DependencyProperty.Register("RootDesigner", typeof(UIElement), typeof(DesignerView), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(DesignerView.OnRootDesignerChanged)));
51
52         public static readonly DependencyProperty IsReadOnlyProperty =
53             DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(DesignerView), new UIPropertyMetadata(OnIsReadOnlyChanged));
54
55         static readonly DependencyPropertyKey ActivitySchemaPropertyKey =
56             DependencyProperty.RegisterReadOnly("ActivitySchema", typeof(ModelItem), typeof(DesignerView), new UIPropertyMetadata(OnActivitySchemaChanged));
57
58         public static readonly DependencyProperty ActivitySchemaProperty = ActivitySchemaPropertyKey.DependencyProperty;
59
60         static readonly DependencyPropertyKey FocusedViewElementPropertyKey =
61             DependencyProperty.RegisterReadOnly("FocusedViewElement", typeof(WorkflowViewElement), typeof(DesignerView), new UIPropertyMetadata(null));
62
63         public static readonly DependencyProperty InPanModeProperty =
64             DependencyProperty.Register("InPanMode", typeof(bool), typeof(DesignerView), new UIPropertyMetadata(OnInPanModeChanged));
65
66         public static readonly DependencyProperty FocusedViewElementProperty = FocusedViewElementPropertyKey.DependencyProperty;
67
68         internal static DependencyProperty ShouldExpandAllProperty = DependencyProperty.Register("ShouldExpandAll", typeof(bool), typeof(DesignerView), new PropertyMetadata(false, new PropertyChangedCallback(OnExpandAllCollapseAllChanged)));
69         internal static DependencyProperty ShouldCollapseAllProperty = DependencyProperty.Register("ShouldCollapseAll", typeof(bool), typeof(DesignerView), new PropertyMetadata(false, new PropertyChangedCallback(OnExpandAllCollapseAllChanged)));
70
71         const double scrollDeltaDivider = 100.0;
72
73         GridLength bottomPaneHeight;
74         EditingContext context;
75         DragDropHelper.ViewElementDragShadow viewElementDragShadow;
76         ZoomToTicksConverter zoomToTicksConverter;
77         ShellBarItemVisibility shellBarItemVisibility = ShellBarItemVisibility.Variables | ShellBarItemVisibility.Arguments | ShellBarItemVisibility.Imports;
78         ShellHeaderItemsVisibility shellHeaderItemsVisibility = ShellHeaderItemsVisibility.Breadcrumb | ShellHeaderItemsVisibility.ExpandAll | ShellHeaderItemsVisibility.CollapseAll;
79         Dictionary<ModelItem, ModelItem> selectionMap = new Dictionary<ModelItem, ModelItem>();
80         private bool isInErrorState = false;
81
82         const string breadCrumbRootKey = "BreadCrumbRoot";
83         const string selectionKey = "Selection";
84         const string zoomFactorKey = "ZoomFactor";
85
86
87         internal WorkflowViewElement lastClickedDesigner;
88         IVSSqmService sqmService;
89         ScrollViewerPanner scrollViewerPanner;
90         RubberBandSelector rubberBandSelector;
91
92         private DesignerViewProxy proxy;
93
94         private DesignerView()
95         {
96         }
97
98         internal DesignerView(EditingContext context)
99         {
100             this.proxy = new DesignerViewProxy(this);
101             this.context = context;
102             InitializeComponent();
103             this.InitializeMenuActions();
104             foreach (UIElement element in this.designerExtensionSurface.Children)
105             {
106                 element.IsEnabled = false;
107             }
108
109             this.buttonArguments1.IsChecked = false;
110             UpdateArgumentsButtonVisibility(false);
111
112             this.zoomToTicksConverter = new ZoomToTicksConverter(this, this.zoomSlider, this.zoomPicker);
113             this.zoomSlider.ValueChanged += new RoutedPropertyChangedEventHandler<double>(OnZoomSliderValueChanged);
114             HideBottomPane();
115
116             this.variables1.VariableCollectionChanged += this.OnVariablesCollectionChanged;
117             this.arguments1.ArgumentCollectionChanged += this.OnArgumentsCollectionChanged;
118             Dispatcher.UnhandledException += this.proxy.OnDispatcherUnhandledException;
119             this.ShouldIgnoreDataGridAutoCommit = false;
120             this.sqmService = this.Context.Services.GetService<IVSSqmService>();
121             this.buttonPanMode.Visibility = this.IsPanModeEnabled ? Visibility.Visible : Visibility.Collapsed;
122             this.rubberBandSelector = this.IsRubberBandSelectionEnabled ? new RubberBandSelector(this.context) : null;
123         }
124
125         void OnReadOnlyStateChanged(ReadOnlyState state)
126         {
127             this.IsReadOnly = state.IsReadOnly;
128         }
129
130         void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
131         {
132             if (!e.Handled)
133             {
134                 if (!isInErrorState)
135                 {
136                     isInErrorState = true;
137
138                     //try to prun the visual tree and collapse all the workflow view elements that are too deep
139                     //this is due to the limitation of WPF has a visual tree depth limit.
140                     if (e.Exception is InvalidOperationException)
141                     {
142                         ICollection<WorkflowViewElement> deepElements = VisualTreeUtils.PrunVisualTree<WorkflowViewElement>(this.RootDesigner);
143                         foreach (WorkflowViewElement viewElement in deepElements)
144                         {
145                             viewElement.ForceCollapse();
146                         }
147                     }
148                     Exception ex = e.Exception.InnerException ?? e.Exception;
149                     ErrorReporting.ShowErrorMessage(ex);
150                     isInErrorState = false;
151                 }
152                 e.Handled = true;
153             }
154         }
155
156         public bool IsMultipleSelectionMode
157         {
158             get;
159             private set;
160         }
161
162         void OnDesignerViewLoaded(object sender, RoutedEventArgs e)
163         {
164             ViewStateService viewStateService = this.Context.Services.GetService<ViewStateService>();
165             ModelTreeManager modelTreeManager = this.context.Services.GetService<ModelTreeManager>();
166             //Initialize ShouldExpandAll if it exists in ViewState.
167             object expandAllState = viewStateService.RetrieveViewState(modelTreeManager.Root, DesignerView.ShouldExpandAllProperty.Name);
168             if (expandAllState != null)
169             {
170                 this.ShouldExpandAll = (bool)expandAllState;
171             }
172             if (!this.ShouldExpandAll)
173             {
174                 object collapseAllState = viewStateService.RetrieveViewState(modelTreeManager.Root, DesignerView.ShouldCollapseAllProperty.Name);
175                 if (collapseAllState != null)
176                 {
177                     this.ShouldCollapseAll = (bool)collapseAllState;
178                 }
179             }
180             // SQM: Open Minimap through designer surface
181             this.buttonMinimap.Checked += new RoutedEventHandler(SqmOpenMinimap);
182             this.expandAllButton.Click += new RoutedEventHandler(SqmExpandAll);
183             this.collapseAllButton.Click += new RoutedEventHandler(SqmCollapseAll);
184
185             if (this.IsPanModeEnabled)
186             {
187                 this.scrollViewerPanner = new ScrollViewerPanner(this.ScrollViewer);
188                 this.scrollViewerPanner.Hand = (Cursor)this.Resources["ReadyToPanCursor"];
189                 this.scrollViewerPanner.DraggingHand = (Cursor)this.Resources["PanningCursor"];
190             }
191         }
192
193         void OnDesignerViewUnloaded(object sender, RoutedEventArgs e)
194         {
195             if (this.scrollViewerPanner != null)
196             {
197                 this.scrollViewerPanner.ScrollViewer = null;
198                 this.scrollViewerPanner = null;
199             }
200         }
201
202         void SqmCollapseAll(object sender, RoutedEventArgs e)
203         {
204             if (this.collapseAllButton.IsChecked == true)
205             {
206                 FeatureUsageCounter.ReportUsage(sqmService, WorkflowDesignerFeatureId.CollapseAll);
207             }
208             else
209             {
210                 FeatureUsageCounter.ReportUsage(sqmService, WorkflowDesignerFeatureId.Restore);
211             }
212         }
213
214         void SqmExpandAll(object sender, RoutedEventArgs e)
215         {
216             if (this.expandAllButton.IsChecked == true)
217             {
218                 FeatureUsageCounter.ReportUsage(sqmService, WorkflowDesignerFeatureId.ExpandAll);
219             }
220             else
221             {
222                 FeatureUsageCounter.ReportUsage(sqmService, WorkflowDesignerFeatureId.Restore);
223             }
224         }
225
226         void SqmOpenMinimap(object sender, RoutedEventArgs e)
227         {
228             FeatureUsageCounter.ReportUsage(sqmService, WorkflowDesignerFeatureId.Minimap);
229         }
230
231         static void OnExpandAllCollapseAllChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
232         {
233             ((DesignerView)o).OnExpandAllCollapseAllChanged(e);
234         }
235
236         void OnExpandAllCollapseAllChanged(DependencyPropertyChangedEventArgs e)
237         {
238             ViewStateService viewStateService = this.Context.Services.GetService<ViewStateService>();
239             ModelTreeManager modelTreeManager = this.context.Services.GetService<ModelTreeManager>();
240             {
241                 viewStateService.StoreViewState(modelTreeManager.Root, e.Property.Name, e.NewValue);
242             }
243         }
244
245
246         protected override void OnInitialized(EventArgs e)
247         {
248             this.AddHandler(UIElement.GotKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(this.OnWorkflowElementGotKeyboardFocus), true);
249             this.AddHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(this.OnDesignerSurfaceMouseLeftButtonDown), true);
250             this.scrollViewer.AddHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(this.OnScrollViewerMouseLeftButtonDown), true);
251             base.OnInitialized(e);
252             this.Foreground = new SolidColorBrush(SystemColors.ControlTextColor);
253             this.Loaded += this.OnDesignerViewLoaded;
254             this.Unloaded += this.OnDesignerViewUnloaded;
255
256             this.IsKeyboardFocusWithinChanged += this.OnDesignerKeyboardFocusWithinChanged;
257
258             this.MenuItemStyle = (Style)this.FindResource("menuItemStyle");
259             Fx.Assert(this.MenuItemStyle != null, "menuItemStyle resource not found");
260             this.MenuSeparatorStyle = (Style)this.FindResource("separatorStyle");
261             Fx.Assert(this.MenuSeparatorStyle != null, "separatorStyle resource not found");
262
263             ReadOnlyState state = this.Context.Items.GetValue<ReadOnlyState>();
264             this.IsReadOnly = state.IsReadOnly;
265             this.Context.Items.Subscribe<ReadOnlyState>(OnReadOnlyStateChanged);
266         }
267
268         public ModelItem ActivitySchema
269         {
270             get { return (ModelItem)GetValue(ActivitySchemaProperty); }
271             private set { SetValue(ActivitySchemaPropertyKey, value); }
272         }
273
274         public EditingContext Context
275         {
276             get { return this.context; }
277         }
278
279         public UIElement RootDesigner
280         {
281             get { return (UIElement)GetValue(RootDesignerProperty); }
282             set { SetValue(RootDesignerProperty, value); }
283         }
284
285         public bool ShouldExpandAll
286         {
287             get { return (bool)GetValue(ShouldExpandAllProperty); }
288             set { SetValue(ShouldExpandAllProperty, value); }
289         }
290
291         public bool ShouldCollapseAll
292         {
293             get { return (bool)GetValue(ShouldCollapseAllProperty); }
294             set { SetValue(ShouldCollapseAllProperty, value); }
295         }
296
297         public bool IsReadOnly
298         {
299             get { return (bool)GetValue(IsReadOnlyProperty); }
300             set { SetValue(IsReadOnlyProperty, value); }
301         }
302
303         public WorkflowViewElement FocusedViewElement
304         {
305             get { return (WorkflowViewElement)GetValue(FocusedViewElementProperty); }
306             private set { SetValue(FocusedViewElementPropertyKey, value); }
307         }
308
309         public double ZoomFactor
310         {
311             get
312             {
313                 return this.zoomToTicksConverter.ZoomFactor;
314             }
315         }
316
317         internal ScrollViewer ScrollViewer
318         {
319             get
320             {
321                 return this.scrollViewer;
322             }
323         }
324
325         internal UIElement ScrollableContent
326         {
327             get
328             {
329                 return this.scrollableContent;
330             }
331         }
332
333         internal bool SuppressSelectionOnMouseUp
334         {
335             get
336             {
337                 if (this.rubberBandSelector == null)
338                 {
339                     return false;
340                 }
341                 return this.rubberBandSelector.IsSelected;
342             }
343         }
344
345         internal bool ShouldIgnoreDataGridAutoCommit
346         {
347             get;
348             set;
349         }
350
351         internal bool ShouldStillAllowRubberBandEvenIfMouseLeftButtonDownIsHandled
352         {
353             get;
354             set;
355         }
356
357         internal bool InPanMode
358         {
359             get { return (bool)GetValue(InPanModeProperty); }
360             set { SetValue(InPanModeProperty, value); }
361         }
362
363         private bool IsPanModeEnabled
364         {
365             get
366             {
367                 DesignerConfigurationService configurationService = this.Context.Services.GetService<DesignerConfigurationService>();
368                 if (configurationService != null)
369                 {
370                     return configurationService.PanModeEnabled;
371                 }
372                 return true;
373             }
374         }
375
376         private bool IsRubberBandSelectionEnabled
377         {
378             get
379             {
380                 DesignerConfigurationService configurationService = this.Context.Services.GetService<DesignerConfigurationService>();
381                 if (configurationService != null)
382                 {
383                     return configurationService.RubberBandSelectionEnabled;
384                 }
385                 return true;
386             }
387         }
388
389         public ShellBarItemVisibility WorkflowShellBarItemVisibility
390         {
391             get { return this.shellBarItemVisibility; }
392             set { this.ApplyShellBarItemVisibility(value); }
393         }
394
395         public ShellHeaderItemsVisibility WorkflowShellHeaderItemsVisibility
396         {
397             get { return this.shellHeaderItemsVisibility; }
398             set { this.ApplyShellHeaderItemsVisibility(value); }
399         }
400
401         public void MakeRootDesigner(ModelItem modelItem)
402         {
403             bool checkIfCanBeMadeRoot = true;
404             if (modelItem == modelItem.Root)
405             {
406                 checkIfCanBeMadeRoot = false;
407             }
408             MakeRootDesigner(modelItem, /* setAsSelection = */ true, checkIfCanBeMadeRoot);
409         }
410
411         internal void MakeRootDesigner(ModelItem modelItem, bool setAsSelection)
412         {
413             MakeRootDesigner(modelItem, setAsSelection, true);
414         }
415
416         internal void ForceMakeRootDesigner(ModelItem modelItem)
417         {
418             MakeRootDesigner(modelItem, /* setAsSelection = */ true, false);
419         }
420
421         void SelectAll()
422         {
423             WorkflowViewElement root = this.RootDesigner as WorkflowViewElement;
424             ModelItem rootModelItem = null;
425             if (root != null)
426             {
427                 rootModelItem = root.ModelItem;
428             }
429             if (rootModelItem != null)
430             {
431                 ModelTreeManager modelTreeManager = this.Context.Services.GetService<ModelTreeManager>();
432                 IEnumerable<ModelItem> items = ModelTreeManager.Find(rootModelItem, delegate(ModelItem current)
433                 {
434                     WorkflowViewService viewService = this.Context.Services.GetService<ViewService>() as WorkflowViewService;
435                     return (typeof(WorkflowViewElement).IsAssignableFrom(viewService.GetDesignerType(current.ItemType)));
436                 }, true);
437                 IEnumerable<ModelItem> itemsToSelect = items
438                     // ModelItemKeyValuePair is associated with CaseDesigner. 
439                     // So ModelItemKeyValuePair will be returned even if they are not really Cases.
440                     // Those ModelItemKeyValuePairs need to be excluded.
441                     .Where<ModelItem>(item => !ModelUtilities.IsModelItemKeyValuePair(item.ItemType) || ModelUtilities.IsSwitchCase(item))
442                     .Except<ModelItem>(new ModelItem[] { rootModelItem });
443                 Selection selection = new Selection(itemsToSelect);
444                 this.Context.Items.SetValue(selection);
445             }
446         }
447
448         internal void BeginDragShadowTracking(DragDropHelper.ViewElementDragShadow dragShadow)
449         {
450             // Returns the first adorner layer in the visual tree above a specified Visual.
451             AdornerLayer layer = this.GetAdornerLayerForDragShadow();
452             if (null != layer)
453             {
454                 layer.Add(dragShadow);
455                 this.viewElementDragShadow = dragShadow;
456                 //register for window messages notification
457                 this.Context.Services.GetService<WindowHelperService>().RegisterWindowMessageHandler(new WindowMessage(OnMessage));
458             }
459         }
460
461         internal void EndDragShadowTracking(DragDropHelper.ViewElementDragShadow dragShadow)
462         {
463             AdornerLayer layer = this.GetAdornerLayerForDragShadow();
464             if (null != layer)
465             {
466                 //unregister from window message notification
467                 this.Context.Services.GetService<WindowHelperService>().UnregisterWindowMessageHandler(new WindowMessage(OnMessage));
468                 layer.Remove(dragShadow);
469                 this.viewElementDragShadow = null;
470             }
471         }
472
473         static void UpdateAncestorFlag(ModelItem oldRoot, ModelItem newRoot)
474         {
475             // Walk up the tree and update the flags from the new root. If we hit the old root in the process, we are done.
476             // Otherwise, continue to update the flags from the old root until we hit the new root.
477             if (oldRoot == newRoot)
478             {
479                 return;
480             }
481             bool hitOldRoot = false;
482             if (newRoot != null)
483             {
484                 WorkflowViewElement viewElement = newRoot.View as WorkflowViewElement;
485                 if (viewElement != null)
486                 {
487                     viewElement.IsAncestorOfRootDesigner = false;
488                 }
489                 ModelItem parent = newRoot.Parent;
490                 while (parent != null)
491                 {
492                     WorkflowViewElement view = parent.View as WorkflowViewElement;
493                     if (view != null)
494                     {
495                         view.IsAncestorOfRootDesigner = true;
496                     }
497                     if (parent == oldRoot)
498                     {
499                         hitOldRoot = true;
500                     }
501                     parent = parent.Parent;
502                 }
503             }
504             if (oldRoot != null && !hitOldRoot)
505             {
506                 ModelItem parent = oldRoot.Parent;
507                 while (parent != null && parent != newRoot)
508                 {
509                     WorkflowViewElement view = parent.View as WorkflowViewElement;
510                     if (view != null)
511                     {
512                         view.IsAncestorOfRootDesigner = false;
513                     }
514                     parent = parent.Parent;
515                 }
516             }
517         }
518
519         internal void MakeRootDesigner(ModelItem modelItem, bool setAsSelection, bool checkIfCanBeMadeRoot)
520         {
521             ModelItem currentRootModelItem = (this.RootDesigner != null) ? ((WorkflowViewElement)this.RootDesigner).ModelItem : null;
522             if (modelItem == currentRootModelItem)
523             {
524                 return;
525             }
526             if (typeof(ActivityBuilder).IsAssignableFrom(modelItem.ItemType))
527             {
528                 this.ActivitySchema = modelItem;
529             }
530
531             WorkflowViewService viewService = this.Context.Services.GetService<ViewService>() as WorkflowViewService;
532
533             //try get designer for given model item
534             Type designerType = viewService.GetDesignerType(modelItem.ItemType);
535             //if one doesn't exist - check its parent tree, perhaps there will be one
536             while (null == designerType && null != modelItem.Parent)
537             {
538                 modelItem = modelItem.Parent;
539                 designerType = viewService.GetDesignerType(modelItem.ItemType);
540             }
541
542             if (viewService.ShouldAppearOnBreadCrumb(modelItem, checkIfCanBeMadeRoot))
543             {
544                 UpdateAncestorFlag(currentRootModelItem, modelItem);
545                 Dictionary<ModelItem, ModelItem> newSelectionMap = new Dictionary<ModelItem, ModelItem>();
546                 ModelItem newRootModelItem = modelItem;
547                 ObservableCollection<object> breadCrumbCollection = new ObservableCollection<object>();
548                 object breadCrumbObjectConnector = null;
549                 bool isFirstAdded = false;
550                 while (modelItem != null)
551                 {
552                     bool shouldCheckIfCanBeMadeRoot = true;
553                     if (isFirstAdded)
554                     {
555                         shouldCheckIfCanBeMadeRoot = checkIfCanBeMadeRoot;
556                     }
557                     if (viewService.ShouldAppearOnBreadCrumb(modelItem, shouldCheckIfCanBeMadeRoot))
558                     {
559                         if (isFirstAdded)
560                         {
561                             breadCrumbObjectConnector = new BreadCrumbObjectSeparator();
562                             breadCrumbCollection.Insert(0, breadCrumbObjectConnector);
563                         }
564                         breadCrumbCollection.Insert(0, modelItem);
565                         isFirstAdded = true;
566                         if (selectionMap.ContainsKey(modelItem))
567                         {
568                             newSelectionMap.Add(modelItem, selectionMap[modelItem]);
569                         }
570                     }
571                     modelItem = modelItem.Parent;
572                 }
573
574                 //Remember the selection for the current root.
575                 WorkflowViewElement focusedElement = Keyboard.FocusedElement as WorkflowViewElement;
576                 //This condition will be true when we are breadcrumbing into a child element.
577                 if (focusedElement != null && object.Equals(focusedElement.ModelItem, newRootModelItem))
578                 {
579                     if (currentRootModelItem != null)
580                     {
581                         newSelectionMap[currentRootModelItem] = newRootModelItem;
582                     }
583                 }
584                 this.selectionMap = newSelectionMap;
585                 SetAsRootDesignerView(newRootModelItem, setAsSelection);
586                 breadCrumbListBox.ItemsSource = breadCrumbCollection;
587                 // Move to the top left so that the display name is visible.
588                 this.ScrollViewer.ScrollToTop();
589                 this.ScrollViewer.ScrollToLeftEnd();
590             }
591         }
592
593         void OnMessage(int msgId, IntPtr wParam, IntPtr lParam)
594         {
595             //WM_NCHITTEST message is the only message beeing routed when dragging an activity over elements which do not support
596             //drag & drop; in order to provide smooth dragging expirience i have to get coordinates from this message and update
597             //drag shadow with them
598             //consider this message only when we are in drag mode
599             if (null != this.viewElementDragShadow && Win32Interop.WM_NCHITTEST == msgId)
600             {
601                 AdornerLayer layer = this.viewElementDragShadow.Parent as AdornerLayer;
602                 Fx.Assert(layer != null, "viewElementDragShadow's parent should not be null");
603                 //get current mouse screen coordinates out of LPARAM
604                 uint pos = (uint)lParam;
605                 Point scrPoint = new Point((int)(pos & 0xffff), (int)(pos >> 16));
606                 // Transform a point from screen to AdornerLayer, which is the parent of shadow.                
607                 Point clientPoint = layer.PointFromScreen(scrPoint);
608                 this.viewElementDragShadow.UpdatePosition(clientPoint.X, clientPoint.Y);
609             }
610         }
611
612         void OnWorkflowElementGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
613         {
614             FrameworkElement source = e.NewFocus as FrameworkElement;
615             //walk up visual tree, but not above DesignerView - there won't be any design shapes anyway
616             while (null != source && this != source)
617             {
618                 //select first visual, which is of type WorkflowViewElement
619                 if (typeof(WorkflowViewElement).IsAssignableFrom(source.GetType()))
620                 {
621                     break;
622                 }
623                 source = VisualTreeHelper.GetParent(source) as FrameworkElement;
624             }
625             //try to cast source element as WorkflowViewElement
626             if (this.FocusedViewElement != source)
627             {
628                 this.FocusedViewElement = source as WorkflowViewElement;
629
630                 System.Diagnostics.Debug.WriteLine(
631                     string.Format(CultureInfo.InvariantCulture, "{0} ) DesignerView.OnWorkflowElementGotKeyboardFocus(FocusedViewElement {1}, raisedBy {2})",
632                     DateTime.Now.ToLocalTime(), (null == this.FocusedViewElement ? "<null>" : this.FocusedViewElement.GetType().Name), e.OriginalSource));
633             }
634         }
635
636         void OnDesignerKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
637         {
638             //if current designer lost keyboard focus - commit pending edits 
639             if (!this.IsKeyboardFocusWithin)
640             {
641                 //delegate the call using dispatcher, so all involved components do consume focus event, then i can commit the edit
642                 this.Dispatcher.BeginInvoke(new Action(() =>
643                     {
644                         //check if there is an edit in progress inside datagrid, which might have opened other dialog - 
645                         //in such case, the desigerView would loose keyboard focus and could ---- non-modal dialog (ie. intelisense window for ETB)
646                         if (!this.ShouldIgnoreDataGridAutoCommit)
647                         {
648                             if (null != this.variables1)
649                             {
650                                 DataGridHelper.CommitPendingEdits(this.variables1.variableDataGrid);
651                             }
652                             if (null != this.arguments1)
653                             {
654                                 DataGridHelper.CommitPendingEdits(this.arguments1.argumentsDataGrid);
655                             }
656                         }
657                     }), DispatcherPriority.Input);
658             }
659             else
660             {
661                 ErrorReporting.ActiveDesignerView = this;
662             }
663         }
664
665         //Suppress handling arrow keys in ScrollViewer
666         void OnScrollViewerKeyDown(object sender, KeyEventArgs e)
667         {
668             if (!e.Handled)
669             {
670                 if ((e.Key == Key.Up) || (e.Key == Key.Down) || (e.Key == Key.Left) || (e.Key == Key.Right))
671                 {
672                     e.Handled = true;
673                 }
674
675                 if (e.Key == Key.Escape)
676                 {
677                     if (this.rubberBandSelector != null)
678                     {
679                         this.rubberBandSelector.OnScrollViewerEscapeKeyDown();
680                     }
681                 }
682             }
683         }
684
685         protected override void OnKeyDown(KeyEventArgs e)
686         {
687             //look up for unhandled Enter key events
688             if (!e.Handled && Keyboard.Modifiers == ModifierKeys.None && e.OriginalSource is WorkflowViewElement)
689             {
690                 switch (e.Key)
691                 {
692                     case Key.Enter:
693                         this.navigateToChildFunction((WorkflowViewElement)e.OriginalSource, true);
694                         break;
695
696                     case Key.Back:
697                         this.navigateToParentFunction((WorkflowViewElement)e.OriginalSource, true);
698                         break;
699                 }
700             }
701             base.OnKeyDown(e);
702         }
703
704         protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
705         {
706             if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
707             {
708                 this.zoomSlider.Value += e.Delta / scrollDeltaDivider;
709                 e.Handled = true;
710             }
711             else
712             {
713                 base.OnPreviewMouseWheel(e);
714             }
715         }
716
717         protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
718         {
719             this.ShouldStillAllowRubberBandEvenIfMouseLeftButtonDownIsHandled = false;
720             base.OnPreviewMouseLeftButtonDown(e);
721         }
722
723         protected override void OnPreviewDragOver(DragEventArgs e)
724         {
725             AutoScrollHelper.AutoScroll(e, this.scrollViewer, 10);
726             base.OnPreviewDragOver(e);
727         }
728
729         public void RegisterViewElement(WorkflowViewElement viewElement)
730         {
731             if (this.rubberBandSelector != null)
732             {
733                 this.rubberBandSelector.RegisterViewElement(viewElement);
734             }
735         }
736
737         public void UnregisterViewElement(WorkflowViewElement viewElement)
738         {
739             if (this.rubberBandSelector != null)
740             {
741                 this.rubberBandSelector.UnregisterViewElement(viewElement);
742             }
743         }
744
745         private void OnScrollViewerMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
746         {
747             if (this.rubberBandSelector != null)
748             {
749                 this.rubberBandSelector.OnScrollViewerMouseLeftButtonDown(e);
750             }
751         }
752
753         private void OnScrollViewerMouseMove(object sender, MouseEventArgs e)
754         {
755             if (this.rubberBandSelector != null)
756             {
757                 this.rubberBandSelector.OnScrollViewerMouseMove(e);
758             }
759         }
760
761         private void OnScrollViewerMouseLeave(object sender, MouseEventArgs e)
762         {
763             if (this.rubberBandSelector != null)
764             {
765                 this.rubberBandSelector.OnScrollViewerMouseLeave();
766             }
767         }
768
769         private void OnScrollViewerPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
770         {
771             if (this.rubberBandSelector != null)
772             {
773                 this.rubberBandSelector.OnScrollViewerPreviewMouseLeftButtonUp(e);
774             }
775         }
776
777         private void OnScrollViewerPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
778         {
779             this.ShouldStillAllowRubberBandEvenIfMouseLeftButtonDownIsHandled = true;
780         }
781
782         private void OnRootDesignerPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
783         {
784             this.ShouldStillAllowRubberBandEvenIfMouseLeftButtonDownIsHandled = false;
785         }
786
787         void OnDesignerSurfaceMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
788         {
789             //user clicked on designer surface, somwhere around actual designer - try to select root designer
790             if (e.OriginalSource == this.scrollViewer || e.OriginalSource == this.scrollableContent || e.OriginalSource == this.scrollViewer.Content)
791             {
792                 //get root designer in given breadcrumb scope
793                 var root = this.RootDesigner as WorkflowViewElement;
794                 if (null != root)
795                 {
796                     //if Ctrl is pressed, handle toggling
797                     if (Keyboard.Modifiers == ModifierKeys.Control)
798                     {
799                         Selection.Toggle(this.Context, root.ModelItem);
800                     }
801                     //else, select the root
802                     else
803                     {
804                         Selection.SelectOnly(this.Context, root.ModelItem);
805                     }
806                     //update focused view element - keyboard focus is set to scrollview, but designer infrastructure requires updated
807                     //FocusViewElement to reference root.
808                     this.FocusedViewElement = root;
809                 }
810                 e.Handled = true;
811             }
812         }
813
814         static void OnActivitySchemaChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
815         {
816             DesignerView control = (DesignerView)dependencyObject;
817             control.OnActivitySchemaChanged();
818         }
819
820         static void OnRootDesignerChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
821         {
822             DesignerView control = (DesignerView)dependencyObject;
823             control.OnRootDesignerChanged(e);
824         }
825
826         static void OnIsReadOnlyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
827         {
828             DesignerView designerView = (DesignerView)dependencyObject;
829             designerView.Context.Items.SetValue(new ReadOnlyState() { IsReadOnly = (bool)e.NewValue });
830         }
831
832         static void OnInPanModeChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
833         {
834             DesignerView designerView = (DesignerView)dependencyObject;
835             if (designerView.scrollViewerPanner != null)
836             {
837                 designerView.scrollViewerPanner.InPanMode = designerView.InPanMode;
838             }
839         }
840
841         void HideBottomPane()
842         {
843             bottomPaneHeight = this.designerViewGrid.RowDefinitions[2].Height;
844             this.designerViewGrid.RowDefinitions[2].Height = new GridLength(0);
845             this.splitter.Visibility = Visibility.Collapsed;
846             this.bottomPanel.Visibility = Visibility.Collapsed;
847         }
848
849         void OnActivitySchemaChanged()
850         {
851             if (null != this.ActivitySchema && typeof(ActivityBuilder).IsAssignableFrom(this.ActivitySchema.ItemType))
852             {
853                 UpdateArgumentsButtonVisibility(true);
854             }
855             else
856             {
857                 UpdateArgumentsButtonVisibility(false);
858             }
859         }
860
861         private void OnBottomPanelClose(object sender, RoutedEventArgs e)
862         {
863             ToggleButton toggleButton = this.bottomPanel.Tag as ToggleButton;
864             Fx.Assert(toggleButton != null, "toggleButton cannot be null");
865             toggleButton.IsChecked = false;
866         }
867
868         void OnBreadCrumbClick(object sender, RoutedEventArgs e)
869         {
870             //this method can be invoked two ways - left mouse click on element or key press
871             ListBoxItem listBoxItem = sender as ListBoxItem;
872             //handle only events for items which are actual model items
873             if (null != listBoxItem && listBoxItem.Content is ModelItem)
874             {
875                 //determine which event are we handling
876                 KeyEventArgs keyArgs = e as KeyEventArgs;
877                 MouseButtonEventArgs mouseArgs = e as MouseButtonEventArgs;
878                 //in case of key events - accept only Enter, in case of mouse events - i know it is left mouse button
879                 if ((null != keyArgs && keyArgs.Key == Key.Enter && Keyboard.Modifiers == ModifierKeys.None) || null != mouseArgs)
880                 {
881                     //make selection new root designer
882                     this.MakeRootDesigner((ModelItem)listBoxItem.Content);
883                     //mark event as handled
884                     e.Handled = true;
885                     // SQM: Breadcrumb
886                     FeatureUsageCounter.ReportUsage(sqmService, WorkflowDesignerFeatureId.Breadcrumb);
887                 }
888             }
889         }
890
891         void OnBreadCrumbNavigation(object sender, KeyEventArgs e)
892         {
893             //this method is invoked whenever user presses any key while breadcrumb has focus
894             ItemsControl breadcrumbItems = sender as ItemsControl;
895             //i expect that there is at least one item in the collection, arrow key is pressed and no keyboard modifiers are active
896             if (null != breadcrumbItems && breadcrumbItems.Items.Count > 0 && Keyboard.Modifiers == ModifierKeys.None && (e.Key == Key.Left || e.Key == Key.Right))
897             {
898                 //get first entry from collection
899                 UIElement first = (UIElement)breadcrumbItems.ItemContainerGenerator.ContainerFromIndex(0);
900                 //get last entry from collection
901                 UIElement last = (UIElement)breadcrumbItems.ItemContainerGenerator.ContainerFromIndex(breadcrumbItems.Items.Count - 1);
902                 //if last is selected, then set focus to the first, so Tab doesn't escape to other control
903                 if (e.Key == Key.Right && last.IsKeyboardFocusWithin)
904                 {
905                     first.Focus();
906                     e.Handled = true;
907                 }
908                 else if (e.Key == Key.Left && first.IsKeyboardFocusWithin)
909                 {
910                     last.Focus();
911                     e.Handled = true;
912                 }
913             }
914         }
915
916         void OnExtensionWindowClosing(object sender, ExtensionWindowClosingRoutedEventArgs e)
917         {
918             e.Cancel = true;
919             e.Handled = true;
920             ((ExtensionWindow)sender).IsEnabled = false;
921         }
922
923         void OnRootDesignerChanged(DependencyPropertyChangedEventArgs e)
924         {
925             WorkflowViewElement previousRoot = (WorkflowViewElement)e.OldValue;
926             WorkflowViewElement currentRoot = (WorkflowViewElement)e.NewValue;
927             if (previousRoot != null)
928             {
929                 previousRoot.IsRootDesigner = false;
930             }
931             if (currentRoot != null)
932             {
933                 currentRoot.IsRootDesigner = true;
934             }
935         }
936
937         [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.ReviewUnusedParameters,
938             Justification = "The parameters are defined by DependencyPropertyChangedEventHandler delegate")]
939         void OnMinimapVisibilityChanged(object sender, DependencyPropertyChangedEventArgs e)
940         {
941             ExtensionSurface.PlacementMode mode = ExtensionSurface.GetMode(this.miniMap);
942             if (mode == ExtensionSurface.PlacementMode.Relative)
943             {
944                 ExtensionSurface.SetMode(this.miniMap, ExtensionSurface.PlacementMode.Absolute);
945             }
946         }
947
948         void OnBottomPanelIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
949         {
950             if ((bool)e.NewValue == false)
951             {
952                 DependencyObject focusedElement = Keyboard.FocusedElement as DependencyObject;
953                 // Move the keyboard focus on a proper designer when the bottom panel is closed
954                 if (focusedElement == null || focusedElement == sender || ((Visual)sender).IsAncestorOf(focusedElement))
955                 {
956                     Keyboard.Focus(this.GetDesignerToFocus());
957                 }
958             }
959         }
960
961         void OnToggleButtonCheckChanged(object sender, RoutedEventArgs e)
962         {
963             ToggleButton button = sender as ToggleButton;
964             Fx.Assert(button != null, "Button cannot be null");
965             ExtensionWindow window = button.Tag as ExtensionWindow;
966             if (null != window)
967             {
968                 window.Visibility = button.IsChecked.Value ? Visibility.Visible : Visibility.Hidden;
969             }
970             else
971             {
972                 UIElement uiElement = button.Tag as UIElement;
973                 if (null != uiElement)
974                 {
975                     if (uiElement.Visibility == Visibility.Collapsed)
976                     {
977                         //remove the previous userControl
978                         if (this.bottomPanel.Tag != null)
979                         {
980                             ToggleButton toggleButton = this.bottomPanel.Tag as ToggleButton;
981                             Fx.Assert(toggleButton != null, "toggleButton should not be null");
982                             if (button != toggleButton)
983                             {
984                                 toggleButton.IsChecked = false;
985                             }
986                         }
987                         //add the new userControl
988                         this.bottomPanel.Visibility = Visibility.Visible;
989                         this.bottomPanel.Tag = button;
990                         this.splitter.Visibility = Visibility.Visible;
991                         uiElement.Visibility = Visibility.Visible;
992                         this.designerViewGrid.RowDefinitions[2].Height = bottomPaneHeight;
993                     }
994                     else
995                     {
996                         //remove the current userControl
997                         this.bottomPanel.Tag = null;
998                         HideBottomPane();
999                         uiElement.Visibility = Visibility.Collapsed;
1000                     }
1001                 }
1002             }
1003         }
1004
1005         void OnZoomSliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
1006         {
1007             if (this.ZoomFactor == 1)
1008             {
1009                 TextOptions.SetTextFormattingMode(this.scrollableContent, TextFormattingMode.Display);
1010             }
1011             else
1012             {
1013                 TextOptions.SetTextFormattingMode(this.scrollableContent, TextFormattingMode.Ideal);
1014             }
1015             this.scrollableContent.LayoutTransform = new ScaleTransform(this.ZoomFactor, this.ZoomFactor);
1016         }
1017
1018         void SetAsRootDesignerView(ModelItem root, bool setAsSelection)
1019         {
1020             VirtualizedContainerService containerService = this.Context.Services.GetService<VirtualizedContainerService>();
1021
1022             this.RootDesigner = null;
1023             //get the root view (route the call through virtualized container serivce, so Loaded and Unloaded events get hooked up)
1024             VirtualizedContainerService.VirtualizingContainer rootContainer = (VirtualizedContainerService.VirtualizingContainer)containerService.GetContainer(root, null);
1025             rootContainer.Populate();
1026             this.RootDesigner = (WorkflowViewElement)rootContainer.Child;
1027
1028             if (setAsSelection)
1029             {
1030                 ModelItem selection = root;
1031                 if (selectionMap.ContainsKey(root))
1032                 {
1033                     ModelItem prevSelection = selectionMap[root];
1034                     selection = null;
1035                     if (prevSelection != null && ViewUtilities.IsViewVisible(prevSelection, root, context))
1036                     {
1037                         selection = prevSelection;
1038                     }
1039                 }
1040                 if (selection != null)
1041                 {
1042                     selection.Focus();
1043                 }
1044                 else
1045                 {
1046                     this.Context.Items.SetValue(new Selection());
1047                 }
1048             }
1049         }
1050
1051         private void SplitterDragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
1052         {
1053             bottomPaneHeight = this.designerViewGrid.RowDefinitions[2].Height;
1054         }
1055
1056         void CreateXPSDocument(string fileName)
1057         {
1058             using (FileStream fs = new FileStream(fileName, FileMode.Create))
1059             {
1060                 Package package = Package.Open(fs, FileMode.Create);
1061                 XpsDocument document = new XpsDocument(package);
1062                 XpsDocumentWriter documentWriter = XpsDocument.CreateXpsDocumentWriter(document);
1063
1064                 int imageWidth = (int)this.designerPresenter.DesiredSize.Width;
1065                 int imageHeight = (int)this.designerPresenter.DesiredSize.Height;
1066
1067                 PrintTicket ticket = new PrintTicket() { PageMediaSize = new PageMediaSize(imageWidth, imageHeight) };
1068                 if (IsRightToLeft(this.designerPresenter))
1069                 {
1070                     Transform originalTransform = this.designerPresenter.RenderTransform;
1071                     try
1072                     {
1073                         this.designerPresenter.RenderTransform = new ScaleTransform(-1, 1, imageWidth / 2, 0);
1074                         documentWriter.Write(this.designerPresenter, ticket);
1075                     }
1076                     finally
1077                     {
1078                         this.designerPresenter.RenderTransform = originalTransform;
1079                     }
1080                 }
1081                 else
1082                 {
1083                     documentWriter.Write(this.designerPresenter, ticket);
1084                 }
1085
1086                 document.Close();
1087                 package.Close();
1088                 fs.Flush();
1089             }
1090         }
1091
1092         void CreateImageFile(string fileName, Type encoderType)
1093         {
1094             using (FileStream fs = new FileStream(fileName, FileMode.Create))
1095             {
1096                 BitmapEncoder encoder = (BitmapEncoder)Activator.CreateInstance(encoderType);
1097                 encoder.Frames.Add(BitmapFrame.Create(this.CreateScreenShot()));
1098                 encoder.Save(fs);
1099                 fs.Close();
1100             }
1101         }
1102
1103         // CreateScreenShot should handle the situation when the FlowDirection of Designer's 
1104         // parent is RightToLeft
1105         // 
1106         // The structure:
1107         // Root
1108         //   |--DesignerView
1109         // The DesignerView is what we're trying to capture.
1110         // 
1111         // If Root.FlowDirection is RightToLeft, the DesignerView's capture is a flipped image.
1112         // Say, if DesignerView is diplayed on screen:
1113         // -->==>
1114         // the captured image would be:
1115         // <==<--
1116         // It is Root who flips the image before diplaying on screen.
1117         // But, in our capture, Root will not do the flipping work for us, so we flip the image
1118         // before return.
1119         BitmapSource CreateScreenShot()
1120         {
1121             const double DPI = 96.0;
1122
1123             Rect bounds = VisualTreeHelper.GetDescendantBounds(this.designerPresenter);
1124             int imageWidth = (int)Math.Ceiling(bounds.Right);
1125             int imageHeight = (int)Math.Ceiling(bounds.Bottom);
1126             Rectangle background = new Rectangle()
1127             {
1128                 // WindowBrush:
1129                 //  Gets a SolidColorBrush that is the background 
1130                 //  color in the client area of a window.  
1131                 Fill = new SolidColorBrush(WorkflowDesignerColors.DesignerViewBackgroundColor),
1132                 Width = imageWidth,
1133                 Height = imageHeight,
1134             };
1135
1136             background.Arrange(new Rect(0, 0, imageWidth, imageHeight));
1137
1138             RenderTargetBitmap renderBitmap = new RenderTargetBitmap(imageWidth, imageHeight, DPI, DPI, PixelFormats.Pbgra32);
1139             renderBitmap.Render(background);
1140             renderBitmap.Render(this.designerPresenter);
1141
1142             BitmapSource source = BitmapFrame.Create(renderBitmap);
1143
1144             if (IsRightToLeft(this.designerPresenter))
1145             {
1146                 return new TransformedBitmap(source, new ScaleTransform(-1, 1, imageWidth / 2, 0));
1147             }
1148
1149             return source;
1150         }
1151
1152         public void OnReferenceUpdated(AssemblyName updatedReference, bool isAdded)
1153         {
1154             //Queue the work item instead of execute it directly in reference updated handler. Otherwise when in VS, we cannot get the assembly through multi-targeting service.
1155             Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() => this.imports1.OnReferenceUpdated(updatedReference, isAdded)));
1156         }
1157
1158         internal void CheckButtonVariables()
1159         {
1160             this.buttonVariables1.IsChecked = true;
1161         }
1162
1163         internal void CheckButtonArguments()
1164         {
1165             this.buttonArguments1.IsChecked = true;
1166         }
1167
1168         void OnVariablesCollectionChanged(object sender, RoutedEventArgs e)
1169         {
1170             // Suppress showing the variable designer for 4.0 compatibility. See TFS 631027 for details.
1171             // CheckButtonVariables();
1172         }
1173
1174         void OnArgumentsCollectionChanged(object sender, RoutedEventArgs e)
1175         {
1176             CheckButtonArguments();
1177         }
1178
1179         void ApplyShellBarItemVisibility(ShellBarItemVisibility visibility)
1180         {
1181             // Store user preferences
1182             this.shellBarItemVisibility = visibility;
1183
1184             // Variable, Arguments, Imports
1185             UpdateStatusBarItemVisibility(this.variablesStatusBarItem, CheckItemVisibility(visibility, ShellBarItemVisibility.Variables), this.variables1);
1186             UpdateArgumentsButtonVisibility(CheckItemVisibility(visibility, ShellBarItemVisibility.Arguments));
1187             UpdateStatusBarItemVisibility(this.importsStatusBarItem, CheckItemVisibility(visibility, ShellBarItemVisibility.Imports), this.imports1);
1188
1189             // PanMode
1190             this.panModeStatusBarItem.Visibility = CheckItemVisibility(visibility, ShellBarItemVisibility.PanMode) ? Visibility.Visible : Visibility.Collapsed;
1191
1192             // Zoom
1193             Visibility zoomVisibility = CheckItemVisibility(visibility, ShellBarItemVisibility.Zoom) ? Visibility.Visible : Visibility.Collapsed;
1194             this.zoomFitToScreenStatusBar.Visibility = zoomVisibility;
1195             this.zoomIconStatusBar.Visibility = zoomVisibility;
1196             this.zoomPickerStatusBar.Visibility = zoomVisibility;
1197             this.zoomSliderStatusBar.Visibility = zoomVisibility;
1198
1199             // MiniMap
1200             this.minimapStatusBar.Visibility = CheckItemVisibility(visibility, ShellBarItemVisibility.MiniMap) ? Visibility.Visible : Visibility.Collapsed;
1201
1202             // Hide entire status bar if nothing is visible 
1203             this.shellBar.Visibility = (ShellBarItemVisibility.None == visibility) ? Visibility.Collapsed : Visibility.Visible;
1204         }
1205
1206         void ApplyShellHeaderItemsVisibility(ShellHeaderItemsVisibility visibility)
1207         {
1208             // If all the items on shell header are invisible, the shell header
1209             // will be hiden automatically. 
1210             // 
1211             // Expand All/ Collapse All / Breadcrumb
1212             this.breadCrumbListBox.Visibility = CheckItemVisibility(visibility, ShellHeaderItemsVisibility.Breadcrumb) ? Visibility.Visible : Visibility.Collapsed;
1213             this.expandAllButton.Visibility = CheckItemVisibility(visibility, ShellHeaderItemsVisibility.ExpandAll) ? Visibility.Visible : Visibility.Collapsed;
1214             this.collapseAllButton.Visibility = CheckItemVisibility(visibility, ShellHeaderItemsVisibility.CollapseAll) ? Visibility.Visible : Visibility.Collapsed;
1215         }
1216
1217         private static bool CheckItemVisibility(ShellHeaderItemsVisibility visibility, ShellHeaderItemsVisibility itemToCheck)
1218         {
1219             return (itemToCheck & visibility) == itemToCheck;
1220         }
1221
1222         private static bool CheckItemVisibility(ShellBarItemVisibility visibility, ShellBarItemVisibility itemToCheck)
1223         {
1224             return (itemToCheck & visibility) == itemToCheck;
1225         }
1226
1227         private static bool IsRightToLeft(FrameworkElement element)
1228         {
1229             Fx.Assert(element != null, "element should not be null");
1230             return element.FlowDirection == FlowDirection.RightToLeft;
1231         }
1232
1233         private AdornerLayer GetAdornerLayerForDragShadow()
1234         {
1235             return AdornerLayer.GetAdornerLayer(this.scrollableContent);
1236         }
1237
1238         private void UpdateArgumentsButtonVisibility(bool visible)
1239         {
1240             UpdateStatusBarItemVisibility(this.argumentsStatusBarItem, visible, this.arguments1);
1241         }
1242
1243         private void UpdateStatusBarItemVisibility(StatusBarItem item, bool visible, UIElement element)
1244         {
1245             if (item == null || element == null)
1246             {
1247                 return;
1248             }
1249
1250             item.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
1251
1252             // Hide the correponding UIElement (VariableDesigner, ArgumentDesigner, etc.) if the button shouldn't be visible
1253             if (item.Visibility != Visibility.Visible)
1254             {
1255                 element.IsEnabled = false;
1256             }
1257             else
1258             {
1259                 element.IsEnabled = true;
1260             }
1261         }
1262
1263         public void FlushState()
1264         {
1265             this.SaveDesignerStates();
1266         }
1267
1268
1269         void SaveDesignerStates()
1270         {
1271             this.SaveBreadCrumbRoot();
1272             this.SaveSelection();
1273             this.SaveZoomFactor();
1274         }
1275
1276         internal void RestoreDesignerStates()
1277         {
1278             this.RestoreBreadCrumbRoot();
1279             this.RestoreSelection();
1280             this.RestoreZoomFactor();
1281         }
1282
1283         void SaveSelection()
1284         {
1285             IWorkflowDesignerStorageService service = this.Context.Services.GetService<IWorkflowDesignerStorageService>();
1286             ModelTreeManager modelTreeManager = this.Context.Services.GetService<ModelTreeManager>();
1287             if (service != null && modelTreeManager != null)
1288             {
1289                 Selection selection = this.Context.Items.GetValue<Selection>();
1290                 var selectionPathList = new List<string>();
1291                 foreach (ModelItem item in selection.SelectedObjects)
1292                 {
1293                     if (item.Root == modelTreeManager.Root)
1294                     {
1295                         selectionPathList.Add(item.GetModelPath());
1296                     }
1297                 }
1298                 if (service.ContainsKey(selectionKey))
1299                 {
1300                     service.SetData(selectionKey, selectionPathList);
1301                 }
1302                 else
1303                 {
1304                     service.AddData(selectionKey, selectionPathList);
1305                 }
1306             }
1307         }
1308
1309         void RestoreSelection()
1310         {
1311             IWorkflowDesignerStorageService service = this.Context.Services.GetService<IWorkflowDesignerStorageService>();
1312             ModelTreeManager modelTreeManager = this.Context.Services.GetService<ModelTreeManager>();
1313             if (service != null && service.ContainsKey(selectionKey) && modelTreeManager != null && modelTreeManager.Root != null)
1314             {
1315                 var selectionPathList = service.GetData(selectionKey) as List<string>;
1316                 if (selectionPathList != null)
1317                 {
1318                     var modelItemList = new List<ModelItem>();
1319                     foreach (string path in selectionPathList)
1320                     {
1321                         ModelItem item = ModelItemExtensions.GetModelItemFromPath(path, modelTreeManager.Root);
1322                         if (item != null)
1323                         {
1324                             modelItemList.Add(item);
1325                         }
1326                     }
1327                     Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
1328                     {
1329                         this.Context.Items.SetValue(new Selection(modelItemList));
1330                     }));
1331                 }
1332             }
1333         }
1334
1335         void SaveBreadCrumbRoot()
1336         {
1337             IWorkflowDesignerStorageService service = this.Context.Services.GetService<IWorkflowDesignerStorageService>();
1338             DesignerView designerView = this.Context.Services.GetService<DesignerView>();
1339             if (service != null && designerView != null && designerView.RootDesigner != null)
1340             {
1341                 WorkflowViewElement rootDesigner = designerView.RootDesigner as WorkflowViewElement;
1342                 if (rootDesigner != null)
1343                 {
1344                     if (service.ContainsKey(breadCrumbRootKey))
1345                     {
1346                         service.SetData(breadCrumbRootKey, rootDesigner.ModelItem.GetModelPath());
1347                     }
1348                     else
1349                     {
1350                         service.AddData(breadCrumbRootKey, rootDesigner.ModelItem.GetModelPath());
1351                     }
1352                 }
1353             }
1354         }
1355
1356         void RestoreBreadCrumbRoot()
1357         {
1358             IWorkflowDesignerStorageService service = this.Context.Services.GetService<IWorkflowDesignerStorageService>();
1359             ModelTreeManager modelTreeManager = this.Context.Services.GetService<ModelTreeManager>();
1360             DesignerView designerView = this.context.Services.GetService<DesignerView>();
1361             if (service != null && service.ContainsKey(breadCrumbRootKey) && modelTreeManager != null && modelTreeManager.Root != null && designerView != null)
1362             {
1363                 string path = service.GetData(breadCrumbRootKey) as string;
1364                 if (path != null)
1365                 {
1366                     ModelItem item = ModelItemExtensions.GetModelItemFromPath(path, modelTreeManager.Root);
1367                     if (item != null)
1368                     {
1369                         designerView.MakeRootDesigner(item);
1370                     }
1371                 }
1372             }
1373         }
1374
1375         void SaveZoomFactor()
1376         {
1377             IWorkflowDesignerStorageService service = this.Context.Services.GetService<IWorkflowDesignerStorageService>();
1378             if (service != null)
1379             {
1380                 if (service.ContainsKey(zoomFactorKey))
1381                 {
1382                     service.SetData(zoomFactorKey, this.zoomSlider.Value);
1383                 }
1384                 else
1385                 {
1386                     service.AddData(zoomFactorKey, this.zoomSlider.Value);
1387                 }
1388             }
1389         }
1390
1391         void RestoreZoomFactor()
1392         {
1393             IWorkflowDesignerStorageService service = this.Context.Services.GetService<IWorkflowDesignerStorageService>();
1394             if (service != null && service.ContainsKey(zoomFactorKey))
1395             {
1396                 object data = service.GetData(zoomFactorKey);
1397                 if (data is double)
1398                 {
1399                     this.zoomSlider.Value = (double)data;
1400                 }
1401             }
1402         }
1403
1404         //this class is used to convert zoom slider ticks to actual zoom percantage
1405         //the speced range of supported zoom values is between 25 % - 400% (with 25, 50, 100, 200 and 400 predefined steps)
1406         //since increments are non linear, i use y = a(x*x) + c equation, to calculate zoom factor - zoom will be more glanular
1407         //for small values, and more coarse for larger ones
1408         private sealed class ZoomToTicksConverter : IValueConverter
1409         {
1410             const double minValue = 25;
1411             const double maxValue = 400;
1412
1413             //predefined a value - calculated on assumption that maximum zoom value is 400% 
1414             const double a = 0.15;
1415             //predefined c value - calculated on assumption that minimum zoom value is 25%
1416             const double c = 25;
1417             IValueConverter baseConverter;
1418
1419             DesignerView view;
1420             string zoomFitToScreenLabel;
1421             double[] keyboardZoomTicks;
1422
1423             internal ZoomToTicksConverter(DesignerView designer, Slider zoomSlider, ComboBox zoomPicker)
1424             {
1425                 this.view = designer;
1426                 this.zoomFitToScreenLabel = (this.view.TryFindResource("zoomFitToScreenLabel") as string) ?? "Fit to screen";
1427                 //this.baseConverter = new ZoomPercentageConverter();
1428                 //right now, we want to use our custom ZoomToPercantageConverter due to localization issues with WPF one
1429                 this.baseConverter = new CustomZoomPercentageConverter();
1430
1431                 //initialize zoom slider
1432                 zoomSlider.Minimum = 0;
1433                 zoomSlider.Maximum = 50;
1434                 zoomSlider.Ticks = new DoubleCollection(new double[] { 0, 10, 20, 30, 40, 50 });
1435
1436                 //set initial value - initially, zoom is set to 100%
1437                 zoomSlider.Value = (double)this.ConvertBack(
1438                     this.baseConverter.Convert(100.0, typeof(string), null, CultureInfo.InvariantCulture), typeof(double), null, CultureInfo.InvariantCulture);
1439
1440                 //insert predefined values to zoomPicker - i use converter to percantage, to ensure text will be formated accordingly to user settings
1441                 zoomPicker.ItemsSource = new object[]
1442                     {
1443                         this.baseConverter.Convert(25.0, typeof(string), null, CultureInfo.InvariantCulture),
1444                         this.baseConverter.Convert(50.0, typeof(string), null, CultureInfo.InvariantCulture),
1445                         this.baseConverter.Convert(100.0, typeof(string), null, CultureInfo.InvariantCulture),
1446                         this.baseConverter.Convert(200.0, typeof(string), null, CultureInfo.InvariantCulture),
1447                         this.baseConverter.Convert(400.0, typeof(string), null, CultureInfo.InvariantCulture)
1448                     };
1449
1450                 //setup bindings
1451                 zoomPicker.SetBinding(ComboBox.SelectedItemProperty, new Binding()
1452                 {
1453                     Source = zoomSlider,
1454                     Path = new PropertyPath(Slider.ValueProperty),
1455                     Converter = this
1456                 });
1457
1458                 zoomPicker.SetBinding(ComboBox.TextProperty, new Binding()
1459                 {
1460                     Source = zoomSlider,
1461                     Path = new PropertyPath(Slider.ValueProperty),
1462                     Converter = this
1463                 });
1464
1465                 this.keyboardZoomTicks = new double[]
1466                 {
1467                     (double)this.ConvertBack(
1468                         this.baseConverter.Convert(25.0, typeof(string), null, CultureInfo.InvariantCulture), typeof(double), null, CultureInfo.InvariantCulture),
1469                     (double)this.ConvertBack(
1470                         this.baseConverter.Convert(37.5, typeof(string), null, CultureInfo.InvariantCulture), typeof(double), null, CultureInfo.InvariantCulture),
1471                     (double)this.ConvertBack(
1472                         this.baseConverter.Convert(50.0, typeof(string), null, CultureInfo.InvariantCulture), typeof(double), null, CultureInfo.InvariantCulture),
1473                     (double)this.ConvertBack(
1474                         this.baseConverter.Convert(75.0, typeof(string), null, CultureInfo.InvariantCulture), typeof(double), null, CultureInfo.InvariantCulture),
1475                     (double)this.ConvertBack(
1476                         this.baseConverter.Convert(100.0, typeof(string), null, CultureInfo.InvariantCulture), typeof(double), null, CultureInfo.InvariantCulture),
1477                     (double)this.ConvertBack(
1478                         this.baseConverter.Convert(150.0, typeof(string), null, CultureInfo.InvariantCulture), typeof(double), null, CultureInfo.InvariantCulture),
1479                     (double)this.ConvertBack(
1480                         this.baseConverter.Convert(200.0, typeof(string), null, CultureInfo.InvariantCulture), typeof(double), null, CultureInfo.InvariantCulture),
1481                     (double)this.ConvertBack(
1482                         this.baseConverter.Convert(300.0, typeof(string), null, CultureInfo.InvariantCulture), typeof(double), null, CultureInfo.InvariantCulture),
1483                     (double)this.ConvertBack(
1484                         this.baseConverter.Convert(400.0, typeof(string), null, CultureInfo.InvariantCulture), typeof(double), null, CultureInfo.InvariantCulture),
1485
1486                 };
1487
1488                 this.view.zoomPicker.LostFocus += (s, e) =>
1489                 {
1490                     string text = this.Convert(this.view.zoomSlider.Value, typeof(string), null, CultureInfo.InvariantCulture) as string;
1491                     if (null != text)
1492                     {
1493                         this.view.zoomPicker.Text = string.Empty;
1494                         this.view.zoomPicker.Text = text;
1495                     }
1496                 };
1497
1498             }
1499
1500             double CalculateY(double x)
1501             {
1502                 return ((x * x) * a) + c;
1503             }
1504
1505             double CalculateX(double y)
1506             {
1507                 return Math.Sqrt((y - c) / a);
1508             }
1509
1510             internal double ZoomFactor
1511             {
1512                 get
1513                 {
1514                     return this.CalculateY(this.view.zoomSlider.Value) / 100.0;
1515                 }
1516             }
1517
1518             public bool CanZoomIn()
1519             {
1520                 return this.view.zoomSlider.Value < this.view.zoomSlider.Maximum;
1521             }
1522
1523             public void ZoomIn()
1524             {
1525                 double x = this.view.zoomSlider.Value;
1526                 for (int i = 0; i < this.keyboardZoomTicks.Length; ++i)
1527                 {
1528                     if (x < this.keyboardZoomTicks[i])
1529                     {
1530                         this.view.zoomSlider.Value = this.keyboardZoomTicks[i];
1531                         break;
1532                     }
1533                 }
1534             }
1535
1536             public void ZoomOut()
1537             {
1538                 double x = this.view.zoomSlider.Value;
1539                 for (int i = this.keyboardZoomTicks.Length - 1; i >= 0; --i)
1540                 {
1541                     if (x > this.keyboardZoomTicks[i])
1542                     {
1543                         this.view.zoomSlider.Value = this.keyboardZoomTicks[i];
1544                         break;
1545                     }
1546                 }
1547             }
1548
1549             public bool CanZoomOut()
1550             {
1551                 return this.view.zoomSlider.Value > this.view.zoomSlider.Minimum;
1552             }
1553
1554             public void FitToScreen()
1555             {
1556                 double y1 = (this.view.scrollViewer.ViewportWidth / this.view.scrollableContent.ActualWidth) * 100.0;
1557                 double y2 = (this.view.scrollViewer.ViewportHeight / this.view.scrollableContent.ActualHeight) * 100.0;
1558                 double y = Math.Min(maxValue, Math.Max(minValue, Math.Min(y1, y2)));
1559                 this.view.zoomSlider.Value = this.CalculateX(y);
1560             }
1561
1562             public void ResetZoom()
1563             {
1564                 this.view.zoomSlider.Value = this.CalculateX(100.0);
1565             }
1566
1567             [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DoNotCatchGeneralExceptionTypes,
1568                 Justification = "Catching all exceptions to avoid VS Crash")]
1569             [SuppressMessage("Reliability", "Reliability108", Justification = "Catching all exceptions to avoid VS Crash")]
1570             public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
1571             {
1572                 if (null != value && value is double)
1573                 {
1574                     try
1575                     {
1576                         return this.baseConverter.Convert(this.CalculateY((double)value), targetType, parameter, culture);
1577                     }
1578                     catch (Exception e)
1579                     {
1580                         System.Diagnostics.Debug.WriteLine(e.ToString());
1581                     }
1582                 }
1583                 return Binding.DoNothing;
1584             }
1585
1586             [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DoNotCatchGeneralExceptionTypes,
1587                 Justification = "Catching all exceptions to avoid VS Crash")]
1588             [SuppressMessage("Reliability", "Reliability108", Justification = "Catching all exceptions to avoid VS Crash")]
1589
1590             public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
1591             {
1592                 if (null != value)
1593                 {
1594                     try
1595                     {
1596                         double y = 0.0;
1597                         if (string.Equals(this.zoomFitToScreenLabel, value))
1598                         {
1599                             double y1 = (this.view.scrollViewer.ViewportWidth / this.view.scrollableContent.ActualWidth) * 100.0;
1600                             double y2 = (this.view.scrollViewer.ViewportHeight / this.view.scrollableContent.ActualHeight) * 100.0;
1601                             y = Math.Min(maxValue, Math.Max(minValue, Math.Min(y1, y2)));
1602                         }
1603                         else
1604                         {
1605                             y = (double)this.baseConverter.ConvertBack(value, targetType, parameter, culture);
1606                         }
1607                         return this.CalculateX(y);
1608                     }
1609                     catch (Exception e)
1610                     {
1611                         System.Diagnostics.Debug.WriteLine(e.ToString());
1612                     }
1613                 }
1614                 return Binding.DoNothing;
1615             }
1616         }
1617
1618         internal static bool IsMouseInViewport(MouseButtonEventArgs e, ScrollViewer scrollViewer)
1619         {
1620             Point mousePosition = e.GetPosition(scrollViewer);
1621             return mousePosition.X > 0 && mousePosition.X < scrollViewer.ViewportWidth &&
1622                 mousePosition.Y > 0 && mousePosition.Y < scrollViewer.ViewportHeight;
1623         }
1624
1625         /// <summary>
1626         /// CustomZoomPercentageConverter - used temporary instead of WPF provided ZoomToPercantageConverter due to the problems
1627         /// in localized builds
1628         /// </summary>
1629         private sealed class CustomZoomPercentageConverter : IValueConverter
1630         {
1631             public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
1632             {
1633                 object result = DependencyProperty.UnsetValue;
1634                 if (null != value)
1635                 {
1636                     double valueAsDouble = System.Convert.ToDouble(value, CultureInfo.CurrentCulture);
1637                     if (valueAsDouble == Math.Floor(valueAsDouble))
1638                     {
1639                         // Ignore decimal part if it is an Int value.
1640                         result = string.Format(CultureInfo.CurrentCulture, "{0}%", valueAsDouble.ToString("F0", CultureInfo.CurrentCulture));
1641                     }
1642                     else
1643                     {
1644                         result = string.Format(CultureInfo.CurrentCulture, "{0}%", valueAsDouble.ToString("F2", CultureInfo.CurrentCulture));
1645                     }
1646                 }
1647                 return result;
1648             }
1649
1650             public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
1651             {
1652                 object result = DependencyProperty.UnsetValue;
1653                 if (null != value)
1654                 {
1655                     string valueAsString = value.ToString().Replace("%", "").Trim();
1656                     result = System.Convert.ToDouble(valueAsString, CultureInfo.CurrentCulture);
1657                 }
1658                 return result;
1659             }
1660         }
1661
1662         //BreadCrumbObjectSeparator - right now, this class has no functionality - object of this class is used as 
1663         //a separator between different breadcrumb elements. however, i can imagine scenario when additional functionality
1664         //is added here (i.e. similar to breadcrumb in Vista explorer)
1665         sealed class BreadCrumbObjectSeparator
1666         {
1667             //ItemType property - to avoid binding errors and make this object similar to ModelItem
1668             public Type ItemType
1669             {
1670                 get { return typeof(BreadCrumbObjectSeparator); }
1671             }
1672         }
1673
1674         private sealed class DesignerViewProxy
1675         {
1676             private WeakReference reference;
1677
1678             public DesignerViewProxy(DesignerView designerView)
1679             {
1680                 this.reference = new WeakReference(designerView);
1681             }
1682
1683             public void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
1684             {
1685                 DesignerView designerView = this.reference.Target as DesignerView;
1686                 if (designerView != null)
1687                 {
1688                     designerView.OnDispatcherUnhandledException(sender, e);
1689                 }
1690             }
1691         }
1692     }
1693
1694
1695     internal sealed class ContextMenuIconProvider : IMultiValueConverter
1696     {
1697         //glyph image cache
1698         IDictionary<KeyValuePair<string, bool>, DrawingBrush> glyphCache = new Dictionary<KeyValuePair<string, bool>, DrawingBrush>();
1699
1700         public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
1701         {
1702             //get the menu item i'm reffering to
1703             var menuItem = values[0] as MenuItem;
1704             //get the icon name as defined in /Resources dictionary
1705             var iconName = parameter as string;
1706             if (null != menuItem && !string.IsNullOrEmpty(iconName))
1707             {
1708                 DrawingBrush glyph = null;
1709                 //check if image has been used alreay - if yes - get it from cache.
1710                 if (!glyphCache.TryGetValue(new KeyValuePair<string, bool>(iconName, menuItem.IsEnabled), out glyph))
1711                 {
1712                     string key = string.Format(CultureInfo.InvariantCulture, "Operation{0}{1}Icon", iconName, menuItem.IsEnabled ? string.Empty : "Disabled");
1713                     glyph = WorkflowDesignerIcons.IconResourceDictionary[key] as DrawingBrush;
1714                     //add it to the cache
1715                     glyphCache[new KeyValuePair<string, bool>(iconName, menuItem.IsEnabled)] = glyph;
1716                 }
1717                 //return glyph
1718                 return new Rectangle() { Width = 16, Height = 16, Fill = glyph };
1719             }
1720             return Binding.DoNothing;
1721         }
1722
1723         public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
1724         {
1725             throw FxTrace.Exception.AsError(new NotSupportedException());
1726         }
1727     }
1728
1729     [SuppressMessage(FxCop.Category.Naming, FxCop.Rule.FlagsEnumsShouldHavePluralNames)]
1730     [SuppressMessage(FxCop.Category.Usage, "CA2217", Justification = "This is enum value, we don't have enough enum values to fill 32 discrete values")]
1731     [Flags]
1732     public enum ShellBarItemVisibility
1733     {
1734         None = 0x0,
1735         Variables = 0x1,
1736         Arguments = 0x2,
1737         Imports = 0x4,
1738         Zoom = 0x8,
1739         MiniMap = 0x10,
1740         PanMode = 0x20,
1741         All = -1
1742     }
1743
1744     [SuppressMessage(FxCop.Category.Naming, FxCop.Rule.FlagsEnumsShouldHavePluralNames)]
1745     [SuppressMessage(FxCop.Category.Usage, "CA2217", Justification = "This is enum value, we don't have enough enum values to fill 32 discrete values")]
1746     [Flags]
1747     public enum ShellHeaderItemsVisibility
1748     {
1749         None = 0x0,
1750         Breadcrumb = 0x1,
1751         ExpandAll = 0x2,
1752         CollapseAll = 0x4,
1753         All = -1
1754     }
1755
1756
1757     internal sealed class ExpandAllCollapseAllToggleConverter : IMultiValueConverter
1758     {
1759         public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
1760         {
1761             //values[0] is the corresponding property - For ExpandAllButton - ShouldExpandAllProperty
1762             //values[1] is the opposite property - For ExpandAllButton - ShouldCollapseAllProperty
1763             return values[0];
1764         }
1765
1766         public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
1767         {
1768             //Whenever ExpandAll/CollapseAll toggle button state is changed, the opposite property is always reset.
1769             return new object[] { value, false };
1770         }
1771     }
1772 }