1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
5 namespace System.Activities.Presentation.View
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;
18 using System.IO.Packaging;
19 using System.Printing;
20 using System.Reflection;
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;
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;
44 // Interaction logic for DesignerView.xaml
46 [Fx.Tag.XamlVisible(false)]
47 public partial class DesignerView : UserControl
49 public static readonly DependencyProperty RootDesignerProperty =
50 DependencyProperty.Register("RootDesigner", typeof(UIElement), typeof(DesignerView), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(DesignerView.OnRootDesignerChanged)));
52 public static readonly DependencyProperty IsReadOnlyProperty =
53 DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(DesignerView), new UIPropertyMetadata(OnIsReadOnlyChanged));
55 static readonly DependencyPropertyKey ActivitySchemaPropertyKey =
56 DependencyProperty.RegisterReadOnly("ActivitySchema", typeof(ModelItem), typeof(DesignerView), new UIPropertyMetadata(OnActivitySchemaChanged));
58 public static readonly DependencyProperty ActivitySchemaProperty = ActivitySchemaPropertyKey.DependencyProperty;
60 static readonly DependencyPropertyKey FocusedViewElementPropertyKey =
61 DependencyProperty.RegisterReadOnly("FocusedViewElement", typeof(WorkflowViewElement), typeof(DesignerView), new UIPropertyMetadata(null));
63 public static readonly DependencyProperty InPanModeProperty =
64 DependencyProperty.Register("InPanMode", typeof(bool), typeof(DesignerView), new UIPropertyMetadata(OnInPanModeChanged));
66 public static readonly DependencyProperty FocusedViewElementProperty = FocusedViewElementPropertyKey.DependencyProperty;
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)));
71 const double scrollDeltaDivider = 100.0;
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;
82 const string breadCrumbRootKey = "BreadCrumbRoot";
83 const string selectionKey = "Selection";
84 const string zoomFactorKey = "ZoomFactor";
87 internal WorkflowViewElement lastClickedDesigner;
88 IVSSqmService sqmService;
89 ScrollViewerPanner scrollViewerPanner;
90 RubberBandSelector rubberBandSelector;
92 private DesignerViewProxy proxy;
94 private DesignerView()
98 internal DesignerView(EditingContext context)
100 this.proxy = new DesignerViewProxy(this);
101 this.context = context;
102 InitializeComponent();
103 this.InitializeMenuActions();
104 foreach (UIElement element in this.designerExtensionSurface.Children)
106 element.IsEnabled = false;
109 this.buttonArguments1.IsChecked = false;
110 UpdateArgumentsButtonVisibility(false);
112 this.zoomToTicksConverter = new ZoomToTicksConverter(this, this.zoomSlider, this.zoomPicker);
113 this.zoomSlider.ValueChanged += new RoutedPropertyChangedEventHandler<double>(OnZoomSliderValueChanged);
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;
125 void OnReadOnlyStateChanged(ReadOnlyState state)
127 this.IsReadOnly = state.IsReadOnly;
130 void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
136 isInErrorState = true;
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)
142 ICollection<WorkflowViewElement> deepElements = VisualTreeUtils.PrunVisualTree<WorkflowViewElement>(this.RootDesigner);
143 foreach (WorkflowViewElement viewElement in deepElements)
145 viewElement.ForceCollapse();
148 Exception ex = e.Exception.InnerException ?? e.Exception;
149 ErrorReporting.ShowErrorMessage(ex);
150 isInErrorState = false;
156 public bool IsMultipleSelectionMode
162 void OnDesignerViewLoaded(object sender, RoutedEventArgs e)
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)
170 this.ShouldExpandAll = (bool)expandAllState;
172 if (!this.ShouldExpandAll)
174 object collapseAllState = viewStateService.RetrieveViewState(modelTreeManager.Root, DesignerView.ShouldCollapseAllProperty.Name);
175 if (collapseAllState != null)
177 this.ShouldCollapseAll = (bool)collapseAllState;
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);
185 if (this.IsPanModeEnabled)
187 this.scrollViewerPanner = new ScrollViewerPanner(this.ScrollViewer);
188 this.scrollViewerPanner.Hand = (Cursor)this.Resources["ReadyToPanCursor"];
189 this.scrollViewerPanner.DraggingHand = (Cursor)this.Resources["PanningCursor"];
193 void OnDesignerViewUnloaded(object sender, RoutedEventArgs e)
195 if (this.scrollViewerPanner != null)
197 this.scrollViewerPanner.ScrollViewer = null;
198 this.scrollViewerPanner = null;
202 void SqmCollapseAll(object sender, RoutedEventArgs e)
204 if (this.collapseAllButton.IsChecked == true)
206 FeatureUsageCounter.ReportUsage(sqmService, WorkflowDesignerFeatureId.CollapseAll);
210 FeatureUsageCounter.ReportUsage(sqmService, WorkflowDesignerFeatureId.Restore);
214 void SqmExpandAll(object sender, RoutedEventArgs e)
216 if (this.expandAllButton.IsChecked == true)
218 FeatureUsageCounter.ReportUsage(sqmService, WorkflowDesignerFeatureId.ExpandAll);
222 FeatureUsageCounter.ReportUsage(sqmService, WorkflowDesignerFeatureId.Restore);
226 void SqmOpenMinimap(object sender, RoutedEventArgs e)
228 FeatureUsageCounter.ReportUsage(sqmService, WorkflowDesignerFeatureId.Minimap);
231 static void OnExpandAllCollapseAllChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
233 ((DesignerView)o).OnExpandAllCollapseAllChanged(e);
236 void OnExpandAllCollapseAllChanged(DependencyPropertyChangedEventArgs e)
238 ViewStateService viewStateService = this.Context.Services.GetService<ViewStateService>();
239 ModelTreeManager modelTreeManager = this.context.Services.GetService<ModelTreeManager>();
241 viewStateService.StoreViewState(modelTreeManager.Root, e.Property.Name, e.NewValue);
246 protected override void OnInitialized(EventArgs e)
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;
256 this.IsKeyboardFocusWithinChanged += this.OnDesignerKeyboardFocusWithinChanged;
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");
263 ReadOnlyState state = this.Context.Items.GetValue<ReadOnlyState>();
264 this.IsReadOnly = state.IsReadOnly;
265 this.Context.Items.Subscribe<ReadOnlyState>(OnReadOnlyStateChanged);
268 public ModelItem ActivitySchema
270 get { return (ModelItem)GetValue(ActivitySchemaProperty); }
271 private set { SetValue(ActivitySchemaPropertyKey, value); }
274 public EditingContext Context
276 get { return this.context; }
279 public UIElement RootDesigner
281 get { return (UIElement)GetValue(RootDesignerProperty); }
282 set { SetValue(RootDesignerProperty, value); }
285 public bool ShouldExpandAll
287 get { return (bool)GetValue(ShouldExpandAllProperty); }
288 set { SetValue(ShouldExpandAllProperty, value); }
291 public bool ShouldCollapseAll
293 get { return (bool)GetValue(ShouldCollapseAllProperty); }
294 set { SetValue(ShouldCollapseAllProperty, value); }
297 public bool IsReadOnly
299 get { return (bool)GetValue(IsReadOnlyProperty); }
300 set { SetValue(IsReadOnlyProperty, value); }
303 public WorkflowViewElement FocusedViewElement
305 get { return (WorkflowViewElement)GetValue(FocusedViewElementProperty); }
306 private set { SetValue(FocusedViewElementPropertyKey, value); }
309 public double ZoomFactor
313 return this.zoomToTicksConverter.ZoomFactor;
317 internal ScrollViewer ScrollViewer
321 return this.scrollViewer;
325 internal UIElement ScrollableContent
329 return this.scrollableContent;
333 internal bool SuppressSelectionOnMouseUp
337 if (this.rubberBandSelector == null)
341 return this.rubberBandSelector.IsSelected;
345 internal bool ShouldIgnoreDataGridAutoCommit
351 internal bool ShouldStillAllowRubberBandEvenIfMouseLeftButtonDownIsHandled
357 internal bool InPanMode
359 get { return (bool)GetValue(InPanModeProperty); }
360 set { SetValue(InPanModeProperty, value); }
363 private bool IsPanModeEnabled
367 DesignerConfigurationService configurationService = this.Context.Services.GetService<DesignerConfigurationService>();
368 if (configurationService != null)
370 return configurationService.PanModeEnabled;
376 private bool IsRubberBandSelectionEnabled
380 DesignerConfigurationService configurationService = this.Context.Services.GetService<DesignerConfigurationService>();
381 if (configurationService != null)
383 return configurationService.RubberBandSelectionEnabled;
389 public ShellBarItemVisibility WorkflowShellBarItemVisibility
391 get { return this.shellBarItemVisibility; }
392 set { this.ApplyShellBarItemVisibility(value); }
395 public ShellHeaderItemsVisibility WorkflowShellHeaderItemsVisibility
397 get { return this.shellHeaderItemsVisibility; }
398 set { this.ApplyShellHeaderItemsVisibility(value); }
401 public void MakeRootDesigner(ModelItem modelItem)
403 bool checkIfCanBeMadeRoot = true;
404 if (modelItem == modelItem.Root)
406 checkIfCanBeMadeRoot = false;
408 MakeRootDesigner(modelItem, /* setAsSelection = */ true, checkIfCanBeMadeRoot);
411 internal void MakeRootDesigner(ModelItem modelItem, bool setAsSelection)
413 MakeRootDesigner(modelItem, setAsSelection, true);
416 internal void ForceMakeRootDesigner(ModelItem modelItem)
418 MakeRootDesigner(modelItem, /* setAsSelection = */ true, false);
423 WorkflowViewElement root = this.RootDesigner as WorkflowViewElement;
424 ModelItem rootModelItem = null;
427 rootModelItem = root.ModelItem;
429 if (rootModelItem != null)
431 ModelTreeManager modelTreeManager = this.Context.Services.GetService<ModelTreeManager>();
432 IEnumerable<ModelItem> items = ModelTreeManager.Find(rootModelItem, delegate(ModelItem current)
434 WorkflowViewService viewService = this.Context.Services.GetService<ViewService>() as WorkflowViewService;
435 return (typeof(WorkflowViewElement).IsAssignableFrom(viewService.GetDesignerType(current.ItemType)));
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);
448 internal void BeginDragShadowTracking(DragDropHelper.ViewElementDragShadow dragShadow)
450 // Returns the first adorner layer in the visual tree above a specified Visual.
451 AdornerLayer layer = this.GetAdornerLayerForDragShadow();
454 layer.Add(dragShadow);
455 this.viewElementDragShadow = dragShadow;
456 //register for window messages notification
457 this.Context.Services.GetService<WindowHelperService>().RegisterWindowMessageHandler(new WindowMessage(OnMessage));
461 internal void EndDragShadowTracking(DragDropHelper.ViewElementDragShadow dragShadow)
463 AdornerLayer layer = this.GetAdornerLayerForDragShadow();
466 //unregister from window message notification
467 this.Context.Services.GetService<WindowHelperService>().UnregisterWindowMessageHandler(new WindowMessage(OnMessage));
468 layer.Remove(dragShadow);
469 this.viewElementDragShadow = null;
473 static void UpdateAncestorFlag(ModelItem oldRoot, ModelItem newRoot)
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)
481 bool hitOldRoot = false;
484 WorkflowViewElement viewElement = newRoot.View as WorkflowViewElement;
485 if (viewElement != null)
487 viewElement.IsAncestorOfRootDesigner = false;
489 ModelItem parent = newRoot.Parent;
490 while (parent != null)
492 WorkflowViewElement view = parent.View as WorkflowViewElement;
495 view.IsAncestorOfRootDesigner = true;
497 if (parent == oldRoot)
501 parent = parent.Parent;
504 if (oldRoot != null && !hitOldRoot)
506 ModelItem parent = oldRoot.Parent;
507 while (parent != null && parent != newRoot)
509 WorkflowViewElement view = parent.View as WorkflowViewElement;
512 view.IsAncestorOfRootDesigner = false;
514 parent = parent.Parent;
519 internal void MakeRootDesigner(ModelItem modelItem, bool setAsSelection, bool checkIfCanBeMadeRoot)
521 ModelItem currentRootModelItem = (this.RootDesigner != null) ? ((WorkflowViewElement)this.RootDesigner).ModelItem : null;
522 if (modelItem == currentRootModelItem)
526 if (typeof(ActivityBuilder).IsAssignableFrom(modelItem.ItemType))
528 this.ActivitySchema = modelItem;
531 WorkflowViewService viewService = this.Context.Services.GetService<ViewService>() as WorkflowViewService;
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)
538 modelItem = modelItem.Parent;
539 designerType = viewService.GetDesignerType(modelItem.ItemType);
542 if (viewService.ShouldAppearOnBreadCrumb(modelItem, checkIfCanBeMadeRoot))
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)
552 bool shouldCheckIfCanBeMadeRoot = true;
555 shouldCheckIfCanBeMadeRoot = checkIfCanBeMadeRoot;
557 if (viewService.ShouldAppearOnBreadCrumb(modelItem, shouldCheckIfCanBeMadeRoot))
561 breadCrumbObjectConnector = new BreadCrumbObjectSeparator();
562 breadCrumbCollection.Insert(0, breadCrumbObjectConnector);
564 breadCrumbCollection.Insert(0, modelItem);
566 if (selectionMap.ContainsKey(modelItem))
568 newSelectionMap.Add(modelItem, selectionMap[modelItem]);
571 modelItem = modelItem.Parent;
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))
579 if (currentRootModelItem != null)
581 newSelectionMap[currentRootModelItem] = newRootModelItem;
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();
593 void OnMessage(int msgId, IntPtr wParam, IntPtr lParam)
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)
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);
612 void OnWorkflowElementGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
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)
618 //select first visual, which is of type WorkflowViewElement
619 if (typeof(WorkflowViewElement).IsAssignableFrom(source.GetType()))
623 source = VisualTreeHelper.GetParent(source) as FrameworkElement;
625 //try to cast source element as WorkflowViewElement
626 if (this.FocusedViewElement != source)
628 this.FocusedViewElement = source as WorkflowViewElement;
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));
636 void OnDesignerKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
638 //if current designer lost keyboard focus - commit pending edits
639 if (!this.IsKeyboardFocusWithin)
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(() =>
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)
648 if (null != this.variables1)
650 DataGridHelper.CommitPendingEdits(this.variables1.variableDataGrid);
652 if (null != this.arguments1)
654 DataGridHelper.CommitPendingEdits(this.arguments1.argumentsDataGrid);
657 }), DispatcherPriority.Input);
661 ErrorReporting.ActiveDesignerView = this;
665 //Suppress handling arrow keys in ScrollViewer
666 void OnScrollViewerKeyDown(object sender, KeyEventArgs e)
670 if ((e.Key == Key.Up) || (e.Key == Key.Down) || (e.Key == Key.Left) || (e.Key == Key.Right))
675 if (e.Key == Key.Escape)
677 if (this.rubberBandSelector != null)
679 this.rubberBandSelector.OnScrollViewerEscapeKeyDown();
685 protected override void OnKeyDown(KeyEventArgs e)
687 //look up for unhandled Enter key events
688 if (!e.Handled && Keyboard.Modifiers == ModifierKeys.None && e.OriginalSource is WorkflowViewElement)
693 this.navigateToChildFunction((WorkflowViewElement)e.OriginalSource, true);
697 this.navigateToParentFunction((WorkflowViewElement)e.OriginalSource, true);
704 protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
706 if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
708 this.zoomSlider.Value += e.Delta / scrollDeltaDivider;
713 base.OnPreviewMouseWheel(e);
717 protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
719 this.ShouldStillAllowRubberBandEvenIfMouseLeftButtonDownIsHandled = false;
720 base.OnPreviewMouseLeftButtonDown(e);
723 protected override void OnPreviewDragOver(DragEventArgs e)
725 AutoScrollHelper.AutoScroll(e, this.scrollViewer, 10);
726 base.OnPreviewDragOver(e);
729 public void RegisterViewElement(WorkflowViewElement viewElement)
731 if (this.rubberBandSelector != null)
733 this.rubberBandSelector.RegisterViewElement(viewElement);
737 public void UnregisterViewElement(WorkflowViewElement viewElement)
739 if (this.rubberBandSelector != null)
741 this.rubberBandSelector.UnregisterViewElement(viewElement);
745 private void OnScrollViewerMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
747 if (this.rubberBandSelector != null)
749 this.rubberBandSelector.OnScrollViewerMouseLeftButtonDown(e);
753 private void OnScrollViewerMouseMove(object sender, MouseEventArgs e)
755 if (this.rubberBandSelector != null)
757 this.rubberBandSelector.OnScrollViewerMouseMove(e);
761 private void OnScrollViewerMouseLeave(object sender, MouseEventArgs e)
763 if (this.rubberBandSelector != null)
765 this.rubberBandSelector.OnScrollViewerMouseLeave();
769 private void OnScrollViewerPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
771 if (this.rubberBandSelector != null)
773 this.rubberBandSelector.OnScrollViewerPreviewMouseLeftButtonUp(e);
777 private void OnScrollViewerPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
779 this.ShouldStillAllowRubberBandEvenIfMouseLeftButtonDownIsHandled = true;
782 private void OnRootDesignerPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
784 this.ShouldStillAllowRubberBandEvenIfMouseLeftButtonDownIsHandled = false;
787 void OnDesignerSurfaceMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
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)
792 //get root designer in given breadcrumb scope
793 var root = this.RootDesigner as WorkflowViewElement;
796 //if Ctrl is pressed, handle toggling
797 if (Keyboard.Modifiers == ModifierKeys.Control)
799 Selection.Toggle(this.Context, root.ModelItem);
801 //else, select the root
804 Selection.SelectOnly(this.Context, root.ModelItem);
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;
814 static void OnActivitySchemaChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
816 DesignerView control = (DesignerView)dependencyObject;
817 control.OnActivitySchemaChanged();
820 static void OnRootDesignerChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
822 DesignerView control = (DesignerView)dependencyObject;
823 control.OnRootDesignerChanged(e);
826 static void OnIsReadOnlyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
828 DesignerView designerView = (DesignerView)dependencyObject;
829 designerView.Context.Items.SetValue(new ReadOnlyState() { IsReadOnly = (bool)e.NewValue });
832 static void OnInPanModeChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
834 DesignerView designerView = (DesignerView)dependencyObject;
835 if (designerView.scrollViewerPanner != null)
837 designerView.scrollViewerPanner.InPanMode = designerView.InPanMode;
841 void HideBottomPane()
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;
849 void OnActivitySchemaChanged()
851 if (null != this.ActivitySchema && typeof(ActivityBuilder).IsAssignableFrom(this.ActivitySchema.ItemType))
853 UpdateArgumentsButtonVisibility(true);
857 UpdateArgumentsButtonVisibility(false);
861 private void OnBottomPanelClose(object sender, RoutedEventArgs e)
863 ToggleButton toggleButton = this.bottomPanel.Tag as ToggleButton;
864 Fx.Assert(toggleButton != null, "toggleButton cannot be null");
865 toggleButton.IsChecked = false;
868 void OnBreadCrumbClick(object sender, RoutedEventArgs e)
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)
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)
881 //make selection new root designer
882 this.MakeRootDesigner((ModelItem)listBoxItem.Content);
883 //mark event as handled
886 FeatureUsageCounter.ReportUsage(sqmService, WorkflowDesignerFeatureId.Breadcrumb);
891 void OnBreadCrumbNavigation(object sender, KeyEventArgs e)
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))
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)
908 else if (e.Key == Key.Left && first.IsKeyboardFocusWithin)
916 void OnExtensionWindowClosing(object sender, ExtensionWindowClosingRoutedEventArgs e)
920 ((ExtensionWindow)sender).IsEnabled = false;
923 void OnRootDesignerChanged(DependencyPropertyChangedEventArgs e)
925 WorkflowViewElement previousRoot = (WorkflowViewElement)e.OldValue;
926 WorkflowViewElement currentRoot = (WorkflowViewElement)e.NewValue;
927 if (previousRoot != null)
929 previousRoot.IsRootDesigner = false;
931 if (currentRoot != null)
933 currentRoot.IsRootDesigner = true;
937 [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.ReviewUnusedParameters,
938 Justification = "The parameters are defined by DependencyPropertyChangedEventHandler delegate")]
939 void OnMinimapVisibilityChanged(object sender, DependencyPropertyChangedEventArgs e)
941 ExtensionSurface.PlacementMode mode = ExtensionSurface.GetMode(this.miniMap);
942 if (mode == ExtensionSurface.PlacementMode.Relative)
944 ExtensionSurface.SetMode(this.miniMap, ExtensionSurface.PlacementMode.Absolute);
948 void OnBottomPanelIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
950 if ((bool)e.NewValue == false)
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))
956 Keyboard.Focus(this.GetDesignerToFocus());
961 void OnToggleButtonCheckChanged(object sender, RoutedEventArgs e)
963 ToggleButton button = sender as ToggleButton;
964 Fx.Assert(button != null, "Button cannot be null");
965 ExtensionWindow window = button.Tag as ExtensionWindow;
968 window.Visibility = button.IsChecked.Value ? Visibility.Visible : Visibility.Hidden;
972 UIElement uiElement = button.Tag as UIElement;
973 if (null != uiElement)
975 if (uiElement.Visibility == Visibility.Collapsed)
977 //remove the previous userControl
978 if (this.bottomPanel.Tag != null)
980 ToggleButton toggleButton = this.bottomPanel.Tag as ToggleButton;
981 Fx.Assert(toggleButton != null, "toggleButton should not be null");
982 if (button != toggleButton)
984 toggleButton.IsChecked = false;
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;
996 //remove the current userControl
997 this.bottomPanel.Tag = null;
999 uiElement.Visibility = Visibility.Collapsed;
1005 void OnZoomSliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
1007 if (this.ZoomFactor == 1)
1009 TextOptions.SetTextFormattingMode(this.scrollableContent, TextFormattingMode.Display);
1013 TextOptions.SetTextFormattingMode(this.scrollableContent, TextFormattingMode.Ideal);
1015 this.scrollableContent.LayoutTransform = new ScaleTransform(this.ZoomFactor, this.ZoomFactor);
1018 void SetAsRootDesignerView(ModelItem root, bool setAsSelection)
1020 VirtualizedContainerService containerService = this.Context.Services.GetService<VirtualizedContainerService>();
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;
1030 ModelItem selection = root;
1031 if (selectionMap.ContainsKey(root))
1033 ModelItem prevSelection = selectionMap[root];
1035 if (prevSelection != null && ViewUtilities.IsViewVisible(prevSelection, root, context))
1037 selection = prevSelection;
1040 if (selection != null)
1046 this.Context.Items.SetValue(new Selection());
1051 private void SplitterDragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
1053 bottomPaneHeight = this.designerViewGrid.RowDefinitions[2].Height;
1056 void CreateXPSDocument(string fileName)
1058 using (FileStream fs = new FileStream(fileName, FileMode.Create))
1060 Package package = Package.Open(fs, FileMode.Create);
1061 XpsDocument document = new XpsDocument(package);
1062 XpsDocumentWriter documentWriter = XpsDocument.CreateXpsDocumentWriter(document);
1064 int imageWidth = (int)this.designerPresenter.DesiredSize.Width;
1065 int imageHeight = (int)this.designerPresenter.DesiredSize.Height;
1067 PrintTicket ticket = new PrintTicket() { PageMediaSize = new PageMediaSize(imageWidth, imageHeight) };
1068 if (IsRightToLeft(this.designerPresenter))
1070 Transform originalTransform = this.designerPresenter.RenderTransform;
1073 this.designerPresenter.RenderTransform = new ScaleTransform(-1, 1, imageWidth / 2, 0);
1074 documentWriter.Write(this.designerPresenter, ticket);
1078 this.designerPresenter.RenderTransform = originalTransform;
1083 documentWriter.Write(this.designerPresenter, ticket);
1092 void CreateImageFile(string fileName, Type encoderType)
1094 using (FileStream fs = new FileStream(fileName, FileMode.Create))
1096 BitmapEncoder encoder = (BitmapEncoder)Activator.CreateInstance(encoderType);
1097 encoder.Frames.Add(BitmapFrame.Create(this.CreateScreenShot()));
1103 // CreateScreenShot should handle the situation when the FlowDirection of Designer's
1104 // parent is RightToLeft
1109 // The DesignerView is what we're trying to capture.
1111 // If Root.FlowDirection is RightToLeft, the DesignerView's capture is a flipped image.
1112 // Say, if DesignerView is diplayed on screen:
1114 // the captured image would be:
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
1119 BitmapSource CreateScreenShot()
1121 const double DPI = 96.0;
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()
1129 // Gets a SolidColorBrush that is the background
1130 // color in the client area of a window.
1131 Fill = new SolidColorBrush(WorkflowDesignerColors.DesignerViewBackgroundColor),
1133 Height = imageHeight,
1136 background.Arrange(new Rect(0, 0, imageWidth, imageHeight));
1138 RenderTargetBitmap renderBitmap = new RenderTargetBitmap(imageWidth, imageHeight, DPI, DPI, PixelFormats.Pbgra32);
1139 renderBitmap.Render(background);
1140 renderBitmap.Render(this.designerPresenter);
1142 BitmapSource source = BitmapFrame.Create(renderBitmap);
1144 if (IsRightToLeft(this.designerPresenter))
1146 return new TransformedBitmap(source, new ScaleTransform(-1, 1, imageWidth / 2, 0));
1152 public void OnReferenceUpdated(AssemblyName updatedReference, bool isAdded)
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)));
1158 internal void CheckButtonVariables()
1160 this.buttonVariables1.IsChecked = true;
1163 internal void CheckButtonArguments()
1165 this.buttonArguments1.IsChecked = true;
1168 void OnVariablesCollectionChanged(object sender, RoutedEventArgs e)
1170 // Suppress showing the variable designer for 4.0 compatibility. See TFS 631027 for details.
1171 // CheckButtonVariables();
1174 void OnArgumentsCollectionChanged(object sender, RoutedEventArgs e)
1176 CheckButtonArguments();
1179 void ApplyShellBarItemVisibility(ShellBarItemVisibility visibility)
1181 // Store user preferences
1182 this.shellBarItemVisibility = visibility;
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);
1190 this.panModeStatusBarItem.Visibility = CheckItemVisibility(visibility, ShellBarItemVisibility.PanMode) ? Visibility.Visible : Visibility.Collapsed;
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;
1200 this.minimapStatusBar.Visibility = CheckItemVisibility(visibility, ShellBarItemVisibility.MiniMap) ? Visibility.Visible : Visibility.Collapsed;
1202 // Hide entire status bar if nothing is visible
1203 this.shellBar.Visibility = (ShellBarItemVisibility.None == visibility) ? Visibility.Collapsed : Visibility.Visible;
1206 void ApplyShellHeaderItemsVisibility(ShellHeaderItemsVisibility visibility)
1208 // If all the items on shell header are invisible, the shell header
1209 // will be hiden automatically.
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;
1217 private static bool CheckItemVisibility(ShellHeaderItemsVisibility visibility, ShellHeaderItemsVisibility itemToCheck)
1219 return (itemToCheck & visibility) == itemToCheck;
1222 private static bool CheckItemVisibility(ShellBarItemVisibility visibility, ShellBarItemVisibility itemToCheck)
1224 return (itemToCheck & visibility) == itemToCheck;
1227 private static bool IsRightToLeft(FrameworkElement element)
1229 Fx.Assert(element != null, "element should not be null");
1230 return element.FlowDirection == FlowDirection.RightToLeft;
1233 private AdornerLayer GetAdornerLayerForDragShadow()
1235 return AdornerLayer.GetAdornerLayer(this.scrollableContent);
1238 private void UpdateArgumentsButtonVisibility(bool visible)
1240 UpdateStatusBarItemVisibility(this.argumentsStatusBarItem, visible, this.arguments1);
1243 private void UpdateStatusBarItemVisibility(StatusBarItem item, bool visible, UIElement element)
1245 if (item == null || element == null)
1250 item.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
1252 // Hide the correponding UIElement (VariableDesigner, ArgumentDesigner, etc.) if the button shouldn't be visible
1253 if (item.Visibility != Visibility.Visible)
1255 element.IsEnabled = false;
1259 element.IsEnabled = true;
1263 public void FlushState()
1265 this.SaveDesignerStates();
1269 void SaveDesignerStates()
1271 this.SaveBreadCrumbRoot();
1272 this.SaveSelection();
1273 this.SaveZoomFactor();
1276 internal void RestoreDesignerStates()
1278 this.RestoreBreadCrumbRoot();
1279 this.RestoreSelection();
1280 this.RestoreZoomFactor();
1283 void SaveSelection()
1285 IWorkflowDesignerStorageService service = this.Context.Services.GetService<IWorkflowDesignerStorageService>();
1286 ModelTreeManager modelTreeManager = this.Context.Services.GetService<ModelTreeManager>();
1287 if (service != null && modelTreeManager != null)
1289 Selection selection = this.Context.Items.GetValue<Selection>();
1290 var selectionPathList = new List<string>();
1291 foreach (ModelItem item in selection.SelectedObjects)
1293 if (item.Root == modelTreeManager.Root)
1295 selectionPathList.Add(item.GetModelPath());
1298 if (service.ContainsKey(selectionKey))
1300 service.SetData(selectionKey, selectionPathList);
1304 service.AddData(selectionKey, selectionPathList);
1309 void RestoreSelection()
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)
1315 var selectionPathList = service.GetData(selectionKey) as List<string>;
1316 if (selectionPathList != null)
1318 var modelItemList = new List<ModelItem>();
1319 foreach (string path in selectionPathList)
1321 ModelItem item = ModelItemExtensions.GetModelItemFromPath(path, modelTreeManager.Root);
1324 modelItemList.Add(item);
1327 Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
1329 this.Context.Items.SetValue(new Selection(modelItemList));
1335 void SaveBreadCrumbRoot()
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)
1341 WorkflowViewElement rootDesigner = designerView.RootDesigner as WorkflowViewElement;
1342 if (rootDesigner != null)
1344 if (service.ContainsKey(breadCrumbRootKey))
1346 service.SetData(breadCrumbRootKey, rootDesigner.ModelItem.GetModelPath());
1350 service.AddData(breadCrumbRootKey, rootDesigner.ModelItem.GetModelPath());
1356 void RestoreBreadCrumbRoot()
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)
1363 string path = service.GetData(breadCrumbRootKey) as string;
1366 ModelItem item = ModelItemExtensions.GetModelItemFromPath(path, modelTreeManager.Root);
1369 designerView.MakeRootDesigner(item);
1375 void SaveZoomFactor()
1377 IWorkflowDesignerStorageService service = this.Context.Services.GetService<IWorkflowDesignerStorageService>();
1378 if (service != null)
1380 if (service.ContainsKey(zoomFactorKey))
1382 service.SetData(zoomFactorKey, this.zoomSlider.Value);
1386 service.AddData(zoomFactorKey, this.zoomSlider.Value);
1391 void RestoreZoomFactor()
1393 IWorkflowDesignerStorageService service = this.Context.Services.GetService<IWorkflowDesignerStorageService>();
1394 if (service != null && service.ContainsKey(zoomFactorKey))
1396 object data = service.GetData(zoomFactorKey);
1399 this.zoomSlider.Value = (double)data;
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
1410 const double minValue = 25;
1411 const double maxValue = 400;
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;
1420 string zoomFitToScreenLabel;
1421 double[] keyboardZoomTicks;
1423 internal ZoomToTicksConverter(DesignerView designer, Slider zoomSlider, ComboBox zoomPicker)
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();
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 });
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);
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[]
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)
1451 zoomPicker.SetBinding(ComboBox.SelectedItemProperty, new Binding()
1453 Source = zoomSlider,
1454 Path = new PropertyPath(Slider.ValueProperty),
1458 zoomPicker.SetBinding(ComboBox.TextProperty, new Binding()
1460 Source = zoomSlider,
1461 Path = new PropertyPath(Slider.ValueProperty),
1465 this.keyboardZoomTicks = new double[]
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),
1488 this.view.zoomPicker.LostFocus += (s, e) =>
1490 string text = this.Convert(this.view.zoomSlider.Value, typeof(string), null, CultureInfo.InvariantCulture) as string;
1493 this.view.zoomPicker.Text = string.Empty;
1494 this.view.zoomPicker.Text = text;
1500 double CalculateY(double x)
1502 return ((x * x) * a) + c;
1505 double CalculateX(double y)
1507 return Math.Sqrt((y - c) / a);
1510 internal double ZoomFactor
1514 return this.CalculateY(this.view.zoomSlider.Value) / 100.0;
1518 public bool CanZoomIn()
1520 return this.view.zoomSlider.Value < this.view.zoomSlider.Maximum;
1523 public void ZoomIn()
1525 double x = this.view.zoomSlider.Value;
1526 for (int i = 0; i < this.keyboardZoomTicks.Length; ++i)
1528 if (x < this.keyboardZoomTicks[i])
1530 this.view.zoomSlider.Value = this.keyboardZoomTicks[i];
1536 public void ZoomOut()
1538 double x = this.view.zoomSlider.Value;
1539 for (int i = this.keyboardZoomTicks.Length - 1; i >= 0; --i)
1541 if (x > this.keyboardZoomTicks[i])
1543 this.view.zoomSlider.Value = this.keyboardZoomTicks[i];
1549 public bool CanZoomOut()
1551 return this.view.zoomSlider.Value > this.view.zoomSlider.Minimum;
1554 public void FitToScreen()
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);
1562 public void ResetZoom()
1564 this.view.zoomSlider.Value = this.CalculateX(100.0);
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)
1572 if (null != value && value is double)
1576 return this.baseConverter.Convert(this.CalculateY((double)value), targetType, parameter, culture);
1580 System.Diagnostics.Debug.WriteLine(e.ToString());
1583 return Binding.DoNothing;
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")]
1590 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
1597 if (string.Equals(this.zoomFitToScreenLabel, value))
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)));
1605 y = (double)this.baseConverter.ConvertBack(value, targetType, parameter, culture);
1607 return this.CalculateX(y);
1611 System.Diagnostics.Debug.WriteLine(e.ToString());
1614 return Binding.DoNothing;
1618 internal static bool IsMouseInViewport(MouseButtonEventArgs e, ScrollViewer scrollViewer)
1620 Point mousePosition = e.GetPosition(scrollViewer);
1621 return mousePosition.X > 0 && mousePosition.X < scrollViewer.ViewportWidth &&
1622 mousePosition.Y > 0 && mousePosition.Y < scrollViewer.ViewportHeight;
1626 /// CustomZoomPercentageConverter - used temporary instead of WPF provided ZoomToPercantageConverter due to the problems
1627 /// in localized builds
1629 private sealed class CustomZoomPercentageConverter : IValueConverter
1631 public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
1633 object result = DependencyProperty.UnsetValue;
1636 double valueAsDouble = System.Convert.ToDouble(value, CultureInfo.CurrentCulture);
1637 if (valueAsDouble == Math.Floor(valueAsDouble))
1639 // Ignore decimal part if it is an Int value.
1640 result = string.Format(CultureInfo.CurrentCulture, "{0}%", valueAsDouble.ToString("F0", CultureInfo.CurrentCulture));
1644 result = string.Format(CultureInfo.CurrentCulture, "{0}%", valueAsDouble.ToString("F2", CultureInfo.CurrentCulture));
1650 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
1652 object result = DependencyProperty.UnsetValue;
1655 string valueAsString = value.ToString().Replace("%", "").Trim();
1656 result = System.Convert.ToDouble(valueAsString, CultureInfo.CurrentCulture);
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
1667 //ItemType property - to avoid binding errors and make this object similar to ModelItem
1668 public Type ItemType
1670 get { return typeof(BreadCrumbObjectSeparator); }
1674 private sealed class DesignerViewProxy
1676 private WeakReference reference;
1678 public DesignerViewProxy(DesignerView designerView)
1680 this.reference = new WeakReference(designerView);
1683 public void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
1685 DesignerView designerView = this.reference.Target as DesignerView;
1686 if (designerView != null)
1688 designerView.OnDispatcherUnhandledException(sender, e);
1695 internal sealed class ContextMenuIconProvider : IMultiValueConverter
1698 IDictionary<KeyValuePair<string, bool>, DrawingBrush> glyphCache = new Dictionary<KeyValuePair<string, bool>, DrawingBrush>();
1700 public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
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))
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))
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;
1718 return new Rectangle() { Width = 16, Height = 16, Fill = glyph };
1720 return Binding.DoNothing;
1723 public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
1725 throw FxTrace.Exception.AsError(new NotSupportedException());
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")]
1732 public enum ShellBarItemVisibility
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")]
1747 public enum ShellHeaderItemsVisibility
1757 internal sealed class ExpandAllCollapseAllToggleConverter : IMultiValueConverter
1759 public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
1761 //values[0] is the corresponding property - For ExpandAllButton - ShouldExpandAllProperty
1762 //values[1] is the opposite property - For ExpandAllButton - ShouldCollapseAllProperty
1766 public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
1768 //Whenever ExpandAll/CollapseAll toggle button state is changed, the opposite property is always reset.
1769 return new object[] { value, false };