Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Activities.Core.Presentation / System / Activities / Core / Presentation / TryCatchDesigner.xaml.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5 namespace System.Activities.Core.Presentation
6 {
7     using System.Activities.Presentation;
8     using System.Activities.Presentation.Metadata;
9     using System.Activities.Presentation.Model;
10     using System.Activities.Presentation.View;
11     using System.Activities.Presentation.Services;
12     using System.Activities.Statements;
13     using System.Collections.ObjectModel;
14     using System.Collections.Specialized;
15     using System.ComponentModel;
16     using System.Diagnostics.CodeAnalysis;
17     using System.IO;
18     using System.Runtime;
19     using System.Windows;
20     using System.Windows.Input;
21     using System.Windows.Threading;
22     using System.Windows.Controls;
23     using System.Activities.Presentation.View.OutlineView;
24
25     /// <summary>
26     /// Interaction logic for TryCatchDesigner.xaml
27     /// </summary>
28     partial class TryCatchDesigner
29     {
30         const string CatchesPropertyName = "Catches";
31         const string ExceptionTypePropertyName = "ExceptionType";
32         const string ExpandViewStateKey = "IsExpanded";
33
34         public static readonly DependencyProperty ShowTryExpandedProperty =
35             DependencyProperty.Register(
36                 "ShowTryExpanded",
37                 typeof(bool),
38                 typeof(TryCatchDesigner),
39                 new UIPropertyMetadata(true)
40             );
41
42         public static readonly DependencyProperty ShowFinallyExpandedProperty =
43             DependencyProperty.Register(
44                 "ShowFinallyExpanded",
45                 typeof(bool),
46                 typeof(TryCatchDesigner),
47                 new UIPropertyMetadata(false)
48             );
49
50         public static readonly DependencyProperty ShowTypePresenterExpandedProperty =
51             DependencyProperty.Register(
52                 "ShowTypePresenterExpanded",
53                 typeof(bool),
54                 typeof(TryCatchDesigner),
55                 new UIPropertyMetadata(false)
56             );
57
58         public static readonly DependencyProperty SelectedCatchProperty =
59             DependencyProperty.Register(
60             "SelectedCatch",
61             typeof(ModelItem),
62             typeof(TryCatchDesigner),
63             new UIPropertyMetadata(null));
64
65         static ObservableCollection<Type> mostRecentlyUsedTypes;
66         static ObservableCollection<Type> MostRecentlyUsedTypes
67         {
68             get
69             {
70                 if (mostRecentlyUsedTypes == null)
71                 {
72                     mostRecentlyUsedTypes = new ObservableCollection<Type>
73                     {
74                         typeof(ArgumentException),
75                         typeof(NullReferenceException),
76                         typeof(IOException),
77                         typeof(InvalidOperationException),
78                         typeof(Exception),
79                     };
80                 }
81                 return mostRecentlyUsedTypes;
82             }
83         }
84
85         public bool ShowTryExpanded
86         {
87             get
88             {
89                 return (bool)this.GetValue(ShowTryExpandedProperty);
90             }
91             set
92             {
93                 this.SetValue(ShowTryExpandedProperty, value);
94             }
95         }
96
97         public bool ShowFinallyExpanded
98         {
99             get
100             {
101                 return (bool)this.GetValue(ShowFinallyExpandedProperty);
102             }
103             set
104             {
105                 this.SetValue(ShowFinallyExpandedProperty, value);
106             }
107         }
108
109         public bool ShowTypePresenterExpanded
110         {
111             get
112             {
113                 return (bool)this.GetValue(ShowTypePresenterExpandedProperty);
114             }
115             set
116             {
117                 this.SetValue(ShowTypePresenterExpandedProperty, value);
118             }
119         }
120
121         ModelItem SelectedCatch
122         {
123             get
124             {
125                 return (ModelItem)this.GetValue(SelectedCatchProperty);
126             }
127             set
128             {
129                 this.SetValue(SelectedCatchProperty, value);
130             }
131         }
132
133         TypePresenter typePresenter;
134         Label addCatchHintLabel;
135
136         internal static void RegisterMetadata(AttributeTableBuilder builder)
137         {
138             Type type = typeof(TryCatch);
139             builder.AddCustomAttributes(type, new DesignerAttribute(typeof(TryCatchDesigner)));
140             builder.AddCustomAttributes(type, type.GetProperty("Try"), BrowsableAttribute.No);
141             builder.AddCustomAttributes(type, type.GetProperty("Finally"), BrowsableAttribute.No);
142             builder.AddCustomAttributes(type, type.GetProperty("Catches"), BrowsableAttribute.No);
143             builder.AddCustomAttributes(type, type.GetProperty("Variables"), BrowsableAttribute.No);
144
145             // Make Catches collection's node visible in the document treeview but hide Catches node itself.
146             builder.AddCustomAttributes(type, type.GetProperty("Catches"), new ShowPropertyInOutlineViewAttribute() { CurrentPropertyVisible = false });
147         }
148
149         [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
150         public TryCatchDesigner()
151         {
152             InitializeComponent();
153
154             this.Loaded += OnLoaded;
155             this.Unloaded += OnUnloaded;
156         }
157
158         void OnLoaded(object sender, RoutedEventArgs e)
159         {
160             this.Context.Items.Subscribe<Selection>(OnSelectionChanged);
161             // at this time, this.ModelItem is already set
162             this.ModelItem.PropertyChanged += OnModelItemPropertyChanged;
163             this.ModelItem.Properties[CatchesPropertyName].Collection.CollectionChanged += OnModelItemCollectionChanged;
164
165             ViewStateService viewStateService = this.Context.Services.GetService<ViewStateService>();
166
167             foreach (ModelItem modelItem in this.ModelItem.Properties["Catches"].Collection)
168             {
169                 bool? isExpanded = (bool?)viewStateService.RetrieveViewState(modelItem, ExpandViewStateKey);
170                 if (isExpanded != null && isExpanded.Value)
171                 {
172                     this.SelectedCatch = modelItem;
173                     CollapseTryView();
174                     CollapseFinallyView();
175                     break;
176                 }
177             }
178         }
179
180         void OnUnloaded(object sender, RoutedEventArgs e)
181         {
182             this.ModelItem.PropertyChanged -= OnModelItemPropertyChanged;
183             this.ModelItem.Properties[CatchesPropertyName].Collection.CollectionChanged -= OnModelItemCollectionChanged;
184             this.Context.Items.Unsubscribe<Selection>(OnSelectionChanged);
185         }
186
187         void OnModelItemPropertyChanged(object sender, PropertyChangedEventArgs e)
188         {
189             switch (e.PropertyName)
190             {
191                 case "Try":
192                     ExpandTryView();
193                     break;
194
195                 case "Finally":
196                     ExpandFinallyView();
197                     break;
198
199                 default:
200                     break;
201             }
202         }
203
204         void OnModelItemCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
205         {
206             // to update the filter
207             this.typePresenter.Filter = this.ExceptionTypeFilter;
208         }
209
210         void OnSelectionChanged(Selection selection)
211         {
212             if (IsDescendantOfTry(selection.PrimarySelection))
213             {
214                 this.ExpandTryView();
215             }
216             else if (IsDescendantOfFinally(selection.PrimarySelection))
217             {
218                 this.ExpandFinallyView();
219             }
220             else
221             {
222                 foreach (ModelItem catchObject in this.ModelItem.Properties["Catches"].Collection)
223                 {
224                     if (IsDescendantOfCatch(catchObject, selection.PrimarySelection))
225                     {
226                         UpdateSelection(catchObject);
227                         break;
228                     }
229                 }
230             }
231         }
232
233         bool IsDescendantOfTry(ModelItem descendant)
234         {
235             return IsDescendantOf(descendant, "Try");
236         }
237
238         bool IsDescendantOfFinally(ModelItem descendant)
239         {
240             return IsDescendantOf(descendant, "Finally");
241         }
242
243         static bool IsAncestorOf(ModelItem ancester, ModelItem descendant)
244         {
245             if (ancester == null)
246             {
247                 return false;
248             }
249
250             ModelItem itr = descendant;
251             while (itr != null)
252             {
253                 if (itr == ancester)
254                 {
255                     return true;
256                 }
257                 itr = itr.Parent;
258             }
259             return false;
260         }
261
262         bool IsDescendantOf(ModelItem descendant, string property)
263         {
264             if (descendant == null)
265             {
266                 return false;
267             }
268             else
269             {
270                 ModelItem propertyValue = this.ModelItem.Properties[property].Value;
271                 return IsAncestorOf(propertyValue, descendant);
272             }
273         }
274
275         internal static bool IsDescendantOfCatch(ModelItem catchObject, ModelItem descendant)
276         {
277             Fx.Assert(catchObject != null, "Catch object mustn't be null.");
278             if (catchObject == descendant)
279             {
280                 return true;
281             }
282             else
283             {
284                 ModelItem activityAction = catchObject.Properties["Action"].Value;
285                 if (activityAction != null)
286                 {
287                     ModelItem activityActionHandler = activityAction.Properties["Handler"].Value;
288                     if (activityActionHandler != null)
289                     {
290                         return IsAncestorOf(activityActionHandler, descendant);
291                     }
292                 }
293                 return false;
294             }
295         }
296
297         void UpdateSelection(ModelItem newSelectedCatch)
298         {
299             ModelItem oldSelectedCatch = this.SelectedCatch;
300             this.SelectedCatch = newSelectedCatch;
301
302             this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
303             {
304                 if (oldSelectedCatch != null)
305                 {
306                     CatchDesigner oldSelectedCatchDesigner = (CatchDesigner)oldSelectedCatch.View;
307                     if (oldSelectedCatchDesigner != null)
308                     {
309                         oldSelectedCatchDesigner.ExpandState = false;
310                         oldSelectedCatchDesigner.PinState = false;
311                     }
312                 }
313                 if (newSelectedCatch != null)
314                 {
315                     CollapseTryView();
316                     CollapseFinallyView();
317                     CatchDesigner newSelectedCatchDesigner = (CatchDesigner)newSelectedCatch.View;
318                     if (newSelectedCatchDesigner != null)
319                     {
320                         newSelectedCatchDesigner.ExpandState = true;
321                         newSelectedCatchDesigner.PinState = true;
322                     }
323                 }
324             }));
325         }
326
327         void CreateCatch(Type exceptionType)
328         {
329             if (exceptionType != null)
330             {
331                 Type catchType = typeof(Catch<>).MakeGenericType(exceptionType);
332                 object catchObject = Activator.CreateInstance(catchType);
333
334                 Type activityActionType = typeof(ActivityAction<>).MakeGenericType(exceptionType);
335                 object activityAction = Activator.CreateInstance(activityActionType);
336
337                 Type argumentType = typeof(DelegateInArgument<>).MakeGenericType(exceptionType);
338                 object exceptionArgument = Activator.CreateInstance(argumentType);
339                 DelegateInArgument delegateArgument = exceptionArgument as DelegateInArgument;
340                 Fx.Assert(null != delegateArgument, "delegate argument must be of DelegateInArgument type!");
341                 delegateArgument.Name = "exception";
342
343                 catchType.GetProperty(PropertyNames.Action).SetValue(catchObject, activityAction, null);
344                 activityActionType.GetProperty(PropertyNames.ActionArgument).SetValue(activityAction, exceptionArgument, null);
345
346                 this.ModelItem.Properties["Catches"].Collection.Add(catchObject);
347             }
348         }
349
350         void OnFinallyViewMouseDown(object sender, MouseButtonEventArgs e)
351         {
352             if (e.LeftButton == MouseButtonState.Pressed && e.ClickCount == 2)
353             {
354                 SwitchTryCatchDesignerHelper.MakeRootDesigner(this);
355                 e.Handled = true;
356             }
357             else if (e.LeftButton == MouseButtonState.Pressed)
358             {
359                 ExpandFinallyView();
360                 Keyboard.Focus((IInputElement)sender);
361             }
362             else if (e.RightButton == MouseButtonState.Pressed)
363             {
364                 if (this.IsExpanded(this.ShowFinallyExpanded))
365                 {
366                     Keyboard.Focus((IInputElement)sender);
367                 }
368                 e.Handled = true;
369             }
370         }
371
372         void OnFinallyViewMouseUp(object sender, MouseButtonEventArgs e)
373         {
374             // avoid context menu upon right-click when it's collapsed
375             if (!IsExpanded(this.ShowFinallyExpanded) && e.RightButton == MouseButtonState.Released)
376             {
377                 e.Handled = true;
378             }
379         }
380
381         bool IsExpanded(bool isExpanded)
382         {
383             DesignerView designerView = this.Context.Services.GetService<DesignerView>();
384             return isExpanded || designerView.ShouldExpandAll;
385         }
386
387         void OnTryViewMouseDown(object sender, MouseButtonEventArgs e)
388         {
389             if (e.LeftButton == MouseButtonState.Pressed && e.ClickCount == 2)
390             {
391                 SwitchTryCatchDesignerHelper.MakeRootDesigner(this);
392                 e.Handled = true;
393             }
394             else if (e.LeftButton == MouseButtonState.Pressed)
395             {
396                 ExpandTryView();
397                 Keyboard.Focus((IInputElement)sender);
398             }
399             else if (e.RightButton == MouseButtonState.Pressed)
400             {
401                 if (this.IsExpanded(this.ShowTryExpanded))
402                 {
403                     Keyboard.Focus((IInputElement)sender);
404                 }
405                 e.Handled = true;
406             }
407         }
408
409         void OnTryViewMouseUp(object sender, MouseButtonEventArgs e)
410         {
411             // avoid context menu upon right-click when it's collapsed
412             if (!IsExpanded(this.ShowTryExpanded) && e.RightButton == MouseButtonState.Released)
413             {
414                 e.Handled = true;
415             }
416         }
417
418         void ExpandFinallyView()
419         {
420             UpdateSelection(null);
421             this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
422             {
423                 this.ShowTryExpanded = false;
424                 this.ShowFinallyExpanded = true;
425             }));
426         }
427
428         void ExpandTryView()
429         {
430             UpdateSelection(null);
431             this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
432             {
433                 this.ShowFinallyExpanded = false;
434                 this.ShowTryExpanded = true;
435             }));
436         }
437
438         void CollapseFinallyView()
439         {
440             this.ShowFinallyExpanded = false;
441         }
442
443         void CollapseTryView()
444         {
445             this.ShowTryExpanded = false;
446         }
447
448         void OnTryViewKeyDown(object sender, KeyEventArgs e)
449         {
450             if (sender == e.OriginalSource && (e.Key == Key.Space || e.Key == Key.Enter))
451             {
452                 ExpandTryView();
453                 e.Handled = true;
454             }
455         }
456
457         void OnFinallyViewKeyDown(object sender, KeyEventArgs e)
458         {
459             if (sender == e.OriginalSource && (e.Key == Key.Space || e.Key == Key.Enter))
460             {
461                 ExpandFinallyView();
462                 e.Handled = true;
463             }
464         }
465
466         #region AddCatch Label & TypePresenter
467
468         void OnAddCatchMouseDown(object sender, MouseButtonEventArgs e)
469         {
470             if (e.LeftButton == MouseButtonState.Pressed)
471             {
472                 this.SwitchToChooseException();
473                 e.Handled = true;
474             }
475         }
476
477         void OnAddCatchGotFocus(object sender, RoutedEventArgs e)
478         {
479             this.SwitchToChooseException();
480             e.Handled = true;
481         }
482
483         void SwitchToChooseException()
484         {
485             this.ShowTypePresenterExpanded = true;
486             this.typePresenter.FocusOnVisibleControl();
487         }
488
489         void SwitchToHintText()
490         {
491             this.typePresenter.Type = null;
492             this.ShowTypePresenterExpanded = false;
493             Keyboard.Focus((IInputElement)this);
494         }
495
496         void OnAddCatchHintLabelLoaded(object sender, RoutedEventArgs e)
497         {
498             this.addCatchHintLabel = (Label)sender;
499         }
500
501         void OnAddCatchHintLabelUnloaded(object sender, RoutedEventArgs e)
502         {
503             this.addCatchHintLabel = null;
504         }
505
506         void OnTypePresenterLoaded(object sender, RoutedEventArgs e)
507         {
508             TypePresenter tp = (TypePresenter)sender;
509             Fx.Assert(tp != null, "sender must be a TypePresenter.");
510
511             this.typePresenter = tp;
512             this.typePresenter.Filter = this.ExceptionTypeFilter;
513             this.typePresenter.MostRecentlyUsedTypes = MostRecentlyUsedTypes;
514             //UnRegistering because of 137896: Inside tab control multiple Loaded events happen without an Unloaded event.
515             this.typePresenter.TypeBrowserClosed -= OnTypePresenterTypeBrowserClosed;
516             this.typePresenter.TypeBrowserClosed += OnTypePresenterTypeBrowserClosed;
517         }
518
519         void OnTypePresenterUnloaded(object sender, RoutedEventArgs e)
520         {
521             if (this.typePresenter != null)
522             {
523                 this.typePresenter.TypeBrowserClosed -= OnTypePresenterTypeBrowserClosed;
524                 this.typePresenter = null;
525             }
526         }
527
528         void OnTypePresenterTypeBrowserClosed(object sender, RoutedEventArgs e)
529         {
530             this.typePresenter.FocusOnVisibleControl();
531         }
532
533         void OnTypePresenterKeyDown(object sender, KeyEventArgs e)
534         {
535             switch (e.Key)
536             {
537                 case Key.Escape:
538                     this.SwitchToHintText();
539                     e.Handled = true;
540                     break;
541
542                 case Key.Enter:
543                     this.AddCatch();
544                     e.Handled = true;
545                     break;
546             }
547         }
548
549         void OnTypePresenterLostFocus(object sender, RoutedEventArgs e)
550         {
551             if (this.ShowTypePresenterExpanded)
552             {
553                 this.AddCatch();
554                 e.Handled = true;
555             }
556         }
557
558         void AddCatch()
559         {
560             if (this.typePresenter != null)
561             {
562                 Type type = this.typePresenter.Type;
563                 if (type != null && this.ExceptionTypeFilter(type))
564                 {
565                     CreateCatch(type);
566                 }
567                 this.SwitchToHintText();
568             }
569         }
570
571         #endregion
572
573         bool ExceptionTypeFilter(Type type)
574         {
575             if (type == null)
576             {
577                 return false;
578             }
579
580             if (type != typeof(Exception) && !type.IsSubclassOf(typeof(Exception)))
581             {
582                 return false;
583             }
584
585             ModelProperty catchesProperty = this.ModelItem.Properties[CatchesPropertyName];
586             Fx.Assert(catchesProperty != null, "TryCatch.Catches could not be null");
587             ModelItemCollection catches = catchesProperty.Collection;
588             Fx.Assert(catches != null, "Catches.Collection could not be null");
589             foreach (ModelItem catchItem in catches)
590             {
591                 ModelProperty exceptionTypeProperty = catchItem.Properties[ExceptionTypePropertyName];
592                 Fx.Assert(exceptionTypeProperty != null, "Catch.ExceptionType could not be null");
593                 Type exceptionType = exceptionTypeProperty.ComputedValue as Type;
594                 Fx.Assert(exceptionType != null, "Catch.ExceptionType.Value could not be null");
595
596                 if (exceptionType == type)
597                 {
598                     return false;
599                 }
600             }
601
602             return true;
603         }
604     }
605 }