Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Activities.Core.Presentation / System / Activities / Presentation / DynamicArgumentDesigner.xaml.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4
5 namespace System.Activities.Presentation
6 {
7     using System.Activities.Presentation.Model;
8     using System.Activities.Presentation.View;
9     using System.Activities.Presentation.Hosting;
10     using System.Runtime;
11     using System.Collections;
12     using System.Collections.ObjectModel;
13     using System.Collections.Generic;
14     using System.Collections.Specialized;
15     using System.ComponentModel;
16     using System.Linq;
17     using System.Globalization;
18     using System.Windows;
19     using System.Windows.Controls;
20     using System.Windows.Data;
21     using System.Windows.Input;
22     using System.Windows.Threading;
23     using System.Reflection;
24
25     internal sealed partial class DynamicArgumentDesigner : UserControl
26     {
27         public static readonly DependencyProperty ContextProperty =
28             DependencyProperty.Register("Context",
29             typeof(EditingContext),
30             typeof(DynamicArgumentDesigner));
31
32         public static readonly DependencyProperty OwnerActivityProperty =
33            DependencyProperty.Register("OwnerActivity",
34            typeof(ModelItem),
35            typeof(DynamicArgumentDesigner));
36
37         public static readonly DependencyProperty IsDirectionReadOnlyProperty =
38             DependencyProperty.Register("IsDirectionReadOnly",
39             typeof(bool),
40             typeof(DynamicArgumentDesigner),
41             new UIPropertyMetadata(true, OnIsDirectionReadOnlyChanged));
42
43         public static readonly DependencyProperty DynamicArgumentsProperty =
44             DependencyProperty.Register("DynamicArguments",
45             typeof(ObservableCollection<DynamicArgumentWrapperObject>),
46             typeof(DynamicArgumentDesigner),
47             new PropertyMetadata(new ObservableCollection<DynamicArgumentWrapperObject>()));
48
49         public static readonly DependencyProperty IsDictionaryProperty =
50             DependencyProperty.Register("IsDictionary",
51             typeof(bool?),
52             typeof(DynamicArgumentDesigner),
53             new PropertyMetadata(false));
54
55         public static readonly DependencyProperty UnderlyingArgumentTypeProperty =
56             DependencyProperty.Register("UnderlyingArgumentType",
57             typeof(Type),
58             typeof(DynamicArgumentDesigner),
59             new PropertyMetadata(typeof(Argument)));
60
61         public static readonly RoutedCommand CreateDynamicArgumentCommand   = new RoutedCommand("CreateDynamicArgumentCommand", typeof(DynamicArgumentDesigner));
62         public static readonly RoutedCommand MoveUpArgumentCommand          = new RoutedCommand("MoveUpArgumentCommand", typeof(DynamicArgumentDesigner));
63         public static readonly RoutedCommand MoveDownArgumentCommand        = new RoutedCommand("MoveDownArgumentCommand", typeof(DynamicArgumentDesigner));
64         public static readonly RoutedCommand DeleteArgumentCommand          = new RoutedCommand("DeleteArgumentCommand", typeof(DynamicArgumentDesigner));
65         public const string DefaultArgumentPrefix = "Argument";
66
67         SubscribeContextCallback<ReadOnlyState> onReadOnlyStateChangedCallback;
68
69         static readonly Type InArgumentType = typeof(InArgument);
70         static readonly Type OutArgumentType = typeof(OutArgument);
71         static readonly Type InOutArgumentType = typeof(InOutArgument);
72         static readonly Type ArgumentType = typeof(Argument);
73
74         const int NameColumn = 0;
75         const int DirectionColumn = 1;
76         const int ArgumentTypeColumn = 2;
77         const int ExpressionColumn = 3;
78
79         bool isReadOnly;
80         bool hideDirection;
81         string argumentPrefix = DefaultArgumentPrefix;
82         string hintText;
83         DataGridHelper dgHelper;
84         ContextItemManager contextItemManager;        
85
86         public DynamicArgumentDesigner()
87         {
88             InitializeComponent();
89
90             this.dgHelper = new DataGridHelper(this.WPF_DataGrid, this);
91             this.dgHelper.AddNewRowCommand = DynamicArgumentDesigner.CreateDynamicArgumentCommand;
92             this.HintText = null;
93
94             this.WPF_DataGrid.LoadingRow += this.DataGrid_Standard_LoadingRow;
95
96             this.Loaded += (sender, e) =>
97             {
98                 OnReadOnlyStateChanged(new ReadOnlyState());
99                 this.ContextItemManager.Subscribe<ReadOnlyState>(this.OnReadOnlyStateChangedCallback);
100                 this.OnDynamicArgumentsLoaded();
101                 this.OnUnderlyingArgumentTypeChanged();
102             };
103             this.Unloaded += (sender, e) =>
104             {
105                 this.ContextItemManager.Unsubscribe<ReadOnlyState>(this.OnReadOnlyStateChangedCallback);
106             };
107
108             DynamicArgumentWrapperObject.Editor = this;
109         }
110
111         ContextItemManager ContextItemManager
112         {
113             get
114             {
115                 if (this.contextItemManager == null)
116                 {
117                     this.contextItemManager = this.OwnerActivity.GetEditingContext().Items;
118                 }
119                 return this.contextItemManager;
120             }
121         }
122
123         void OnReadOnlyStateChanged(ReadOnlyState state)
124         {
125             UpdateChildrenElementStatus();
126         }
127
128         void UpdateChildrenElementStatus()
129         {
130             this.isReadOnly = this.ContextItemManager.GetValue<ReadOnlyState>().IsReadOnly || this.DynamicArguments == null;
131
132             if (this.isReadOnly)
133             {
134                 this.WPF_DataGrid.IsReadOnly = true;
135                 this.ButtonMovDown.IsEnabled = false;
136                 this.ButtonMovUp.IsEnabled = false;
137                 this.ButtonDelete.IsEnabled = false;
138             }
139         }
140
141         void OnDataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
142         {
143             if (this.isReadOnly 
144                 || this.WPF_DataGrid.SelectedItems == null
145                 || this.WPF_DataGrid.SelectedItems.Count == 0)
146             {
147                 this.ButtonMovUp.IsEnabled   = false;
148                 this.ButtonMovDown.IsEnabled = false;
149                 this.ButtonDelete.IsEnabled  = false;
150                 return;
151             }
152
153             // delete button
154             this.ButtonDelete.IsEnabled = true;
155             
156             // up/down button.
157             if (this.WPF_DataGrid.SelectedItems.Count == 1)
158             {
159                 bool upHadFocus = ButtonMovUp.IsFocused;
160                 bool downHadFocus = ButtonMovDown.IsFocused;
161                 this.ButtonMovUp.IsEnabled = this.WPF_DataGrid.SelectedIndex > 0;
162                 this.ButtonMovDown.IsEnabled = this.WPF_DataGrid.SelectedIndex < this.DynamicArguments.Count - 1;
163                 if (!this.ButtonMovDown.IsEnabled && downHadFocus)
164                 {
165                     this.ButtonMovUp.Focus();
166                 }
167                 if (!this.ButtonMovUp.IsEnabled && upHadFocus)
168                 {
169                     this.ButtonMovDown.Focus();
170                 }
171             }
172             else
173             {
174                 this.ButtonMovUp.IsEnabled = this.ButtonMovDown.IsEnabled = false;
175             }
176         }
177
178         // The DataGrid does not bubble up KeyDown event and we expect the upper window to be closed when ESC key is down.
179         // Thus we added an event handler in DataGrid to handle ESC key and closes the uppper window.
180         void OnDataGridRowKeyDown(object sender, KeyEventArgs args)
181         {
182             DataGridRow row = (DataGridRow)sender;
183             if (args.Key == Key.Escape && !row.IsEditing && this.ParentDialog != null)
184             {
185                 this.ParentDialog.CloseDialog(false);
186             }
187         }        
188
189         protected override void OnInitialized(EventArgs e)
190         {
191             base.OnInitialized(e);
192             this.Unloaded += this.OnDynamicArgumentDesignerUnloaded;
193         }
194
195         SubscribeContextCallback<ReadOnlyState> OnReadOnlyStateChangedCallback
196         {
197             get
198             {
199                 if (onReadOnlyStateChangedCallback == null)
200                 {
201                     onReadOnlyStateChangedCallback = new SubscribeContextCallback<ReadOnlyState>(OnReadOnlyStateChanged);
202                 }
203                 return onReadOnlyStateChangedCallback;
204             }
205         }
206
207         void OnDynamicArgumentDesignerUnloaded(object sender, RoutedEventArgs e)
208         {
209             this.WPF_DataGrid.LoadingRow -= this.DataGrid_Standard_LoadingRow;
210             this.Unloaded -= this.OnDynamicArgumentDesignerUnloaded;
211         }
212
213         public ObservableCollection<DynamicArgumentWrapperObject> DynamicArguments
214         {
215             get
216             {
217                 return (ObservableCollection<DynamicArgumentWrapperObject>)GetValue(DynamicArgumentsProperty);
218             }
219             set
220             {
221                 SetValue(DynamicArgumentsProperty, value);
222                 if (value != null)
223                 {
224                     this.WPF_DataGrid.ItemsSource = value;
225                 }
226             }
227         }
228
229         [Fx.Tag.KnownXamlExternal]
230         public ModelItem OwnerActivity
231         {
232             get { return (ModelItem)GetValue(OwnerActivityProperty); }
233             set { SetValue(OwnerActivityProperty, value); }
234         }
235
236         [Fx.Tag.KnownXamlExternal]
237         public EditingContext Context
238         {
239             get { return (EditingContext)GetValue(ContextProperty); }
240             set { SetValue(ContextProperty, value); }
241         }
242
243         public bool IsDirectionReadOnly
244         {
245             get { return (bool)GetValue(IsDirectionReadOnlyProperty); }
246             set { SetValue(IsDirectionReadOnlyProperty, value); }
247         }
248
249         public bool HideDirection
250         {
251             get
252             {
253                 return this.hideDirection;
254             }
255             set
256             {
257                 this.hideDirection = value;
258                 if (this.hideDirection)
259                 {
260                     this.WPF_DataGrid.Columns[DynamicArgumentDesigner.DirectionColumn].Visibility = Visibility.Hidden;
261                 }
262                 else
263                 {
264                     this.WPF_DataGrid.Columns[DynamicArgumentDesigner.DirectionColumn].Visibility = Visibility.Visible;
265                 }
266             }
267         }
268
269         public string ArgumentPrefix
270         {
271             get
272             {
273                 return this.argumentPrefix;
274             }
275             set
276             {
277                 this.argumentPrefix = value;
278             }
279         }
280
281         public string HintText
282         {
283             get
284             {
285                 if (this.hintText != null)
286                 {
287                     return this.hintText;
288                 }
289                 else
290                 {
291                     return (string)this.FindResource("addDynamicArgumentNewRowLabel");
292                 }
293             }
294             set
295             {
296                 this.hintText = value;
297                 if (hintText == null)
298                 {
299                     dgHelper.AddNewRowContent = (string)this.FindResource("addDynamicArgumentNewRowLabel");
300                 }
301                 else
302                 {
303                     dgHelper.AddNewRowContent = hintText;
304                 }
305             }
306         }
307
308         public WorkflowElementDialog ParentDialog
309         {
310             get;
311             set;
312         }
313
314         static void OnIsDirectionReadOnlyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
315         {
316             ((DynamicArgumentDesigner)dependencyObject).OnIsDirectionReadOnlyChanged();
317         }
318
319         void OnIsDirectionReadOnlyChanged()
320         {
321             if (!this.IsDirectionReadOnly)
322             {
323                 DataGridTemplateColumn directionCol = this.WPF_DataGrid.Columns[1] as DataGridTemplateColumn;
324                 directionCol.CellEditingTemplate = (DataTemplate)this.WPF_DataGrid.FindResource("argumentDirectionEditingTemplate");
325             }
326         }
327
328         internal Type UnderlyingArgumentType
329         {
330             get
331             {
332                 return (Type)GetValue(UnderlyingArgumentTypeProperty);
333             }
334             set
335             {
336                 if (!typeof(Argument).IsAssignableFrom(value))
337                 {
338                     ErrorReporting.ShowErrorMessage(SR.NonSupportedDynamicArgumentType);
339                 }
340                 else
341                 {
342                     SetValue(UnderlyingArgumentTypeProperty, value);
343                     OnUnderlyingArgumentTypeChanged();
344                 }
345             }
346         }
347
348         void OnUnderlyingArgumentTypeChanged()
349         {
350             Type currentArgumentType = this.UnderlyingArgumentType;
351
352             if (currentArgumentType != null && (OutArgumentType.IsAssignableFrom(currentArgumentType) || InOutArgumentType.IsAssignableFrom(currentArgumentType)))
353             {
354                 this.WPF_DataGrid.Columns[DynamicArgumentDesigner.ExpressionColumn].Header = (string)this.FindResource("assignToHeader");
355             }
356             else
357             {
358                 this.WPF_DataGrid.Columns[DynamicArgumentDesigner.ExpressionColumn].Header = (string)this.FindResource("valueHeader");
359             }
360         }
361
362         internal bool? IsDictionary
363         {
364             get
365             {
366                 return (bool?)GetValue(IsDictionaryProperty);
367             }
368             set
369             {
370                 SetValue(IsDictionaryProperty, value);
371             }
372         }
373
374         internal static ObservableCollection<DynamicArgumentWrapperObject> ModelItemToWrapperCollection(ModelItem model, out bool isDictionary, out Type underlyingArgumentType)
375         {
376             string errorMessage = string.Empty;
377             underlyingArgumentType = null;
378             isDictionary = false;
379             if (model is ModelItemCollection)
380             {
381                 underlyingArgumentType = model.GetCurrentValue().GetType().GetGenericArguments()[0];
382                 if (!typeof(Argument).IsAssignableFrom(underlyingArgumentType))
383                 {
384                     errorMessage = SR.NonSupportedDynamicArgumentType;
385                 }
386             }
387             else if (model is ModelItemDictionary)
388             {
389                 Type underlyingKeyType = model.GetCurrentValue().GetType().GetGenericArguments()[0];
390                 underlyingArgumentType = model.GetCurrentValue().GetType().GetGenericArguments()[1];
391                 if (!typeof(Argument).IsAssignableFrom(underlyingArgumentType))
392                 {
393                     errorMessage = SR.NonSupportedDynamicArgumentType;
394                 }
395                 if (underlyingKeyType != typeof(string))
396                 {
397                     errorMessage += SR.NonSupportedDynamicArgumentKeyType;
398                 }
399                 isDictionary = true;
400             }
401             else
402             {
403                 errorMessage = SR.NonSupportedModelItemCollectionOrDictionary;
404             }
405             if (!string.IsNullOrEmpty(errorMessage))
406             {
407                 ErrorReporting.ShowErrorMessage(SR.NonSupportedModelItemCollectionOrDictionary);
408                 return null;
409             }
410             if (isDictionary)
411             {
412                 ObservableCollection<DynamicArgumentWrapperObject> wrappers = new ObservableCollection<DynamicArgumentWrapperObject>();
413                 foreach (ModelItem item in GetArgumentCollection(model))
414                 {
415                     wrappers.Add(new DynamicArgumentWrapperObject(item.Properties["Key"].ComputedValue as string, item.Properties["Value"].Value));
416                 }
417                 return wrappers;
418             }
419             else
420             {
421                 ObservableCollection<DynamicArgumentWrapperObject> wrappers = new ObservableCollection<DynamicArgumentWrapperObject>();
422                 foreach (ModelItem item in GetArgumentCollection(model))
423                 {
424                     wrappers.Add(new DynamicArgumentWrapperObject(null, item));
425                 }
426                 return wrappers;
427             }
428         }
429
430         internal static void WrapperCollectionToModelItem(ObservableCollection<DynamicArgumentWrapperObject> wrappers, ModelItem data, bool isDictionary, Type underlyingArgumentType)
431         {
432             ModelItemCollection collection = GetArgumentCollection(data);
433             using (ModelEditingScope change = collection.BeginEdit(SR.UpdateDynamicArgumentsDescription))
434             {
435                 if (isDictionary)
436                 {
437                     collection.Clear();
438                     Type dictionaryEntryType = typeof(ModelItemKeyValuePair<,>).MakeGenericType(new Type[] { typeof(string), underlyingArgumentType });
439                     foreach (DynamicArgumentWrapperObject wrapper in wrappers)
440                     {
441                         Argument argument = Argument.Create(wrapper.Type, wrapper.Direction);
442                         object mutableKVPair = Activator.CreateInstance(dictionaryEntryType, new object[] { wrapper.Name, argument });
443                         ModelItem argumentKVPair = collection.Add(mutableKVPair);
444                         if (wrapper.Expression != null)
445                         {
446                             argumentKVPair.Properties["Value"].Value.Properties["Expression"].SetValue(wrapper.Expression.GetCurrentValue());
447                         }
448                     }
449                 }
450                 else
451                 {
452                     collection.Clear();
453                     foreach (DynamicArgumentWrapperObject wrapper in wrappers)
454                     {
455                         Argument argument = Argument.Create(wrapper.Type, wrapper.Direction);
456                         ModelItem argumentItem = collection.Add(argument);
457                         if (wrapper.Expression != null)
458                         {
459                             argumentItem.Properties["Expression"].SetValue(wrapper.Expression.GetCurrentValue());
460                         }
461                     }
462                 }
463
464                 change.Complete();
465             }
466         }
467
468         static ModelItemCollection GetArgumentCollection(ModelItem data)
469         {
470             if (data is ModelItemCollection)
471             {
472                 return (data as ModelItemCollection);
473             }
474             else if (data is ModelItemDictionary)
475             {
476                 return (data as ModelItemDictionary).Properties["ItemsCollection"].Collection;
477             }
478             else
479             {
480                 ErrorReporting.ShowErrorMessage(SR.NonSupportedModelItemCollectionOrDictionary);
481                 return null;
482             }
483         }
484
485         void OnDynamicArgumentsLoaded()
486         {
487             Fx.Assert(this.Context != null, "EditingContext cannot be null");
488             Fx.Assert(this.IsDictionary != null, "IsDictionary is not set");
489             Fx.Assert(this.UnderlyingArgumentType != null, "UnderlyingArgumentType is not set");            
490             if (!(this.IsDictionary.Value))
491             {
492                 this.WPF_DataGrid.Columns[DynamicArgumentDesigner.NameColumn].Visibility = Visibility.Hidden;
493             }
494
495             if (null != this.DynamicArguments)
496             {
497                 if (this.UnderlyingArgumentType == ArgumentType)
498                 {
499                     this.IsDirectionReadOnly = false;
500                 }
501
502                 if (this.UnderlyingArgumentType.IsGenericType)
503                 {
504                     Type[] innerArgumentTypes = this.UnderlyingArgumentType.GetGenericArguments();
505                     if (innerArgumentTypes.Length > 0)
506                     {
507                         Type innerArgumentType = innerArgumentTypes[0];
508                         this.WPF_DataGrid.Columns[DynamicArgumentDesigner.ArgumentTypeColumn].IsReadOnly = !innerArgumentType.IsGenericParameter;
509                     }
510                 }
511             }
512
513             this.WPF_DataGrid.ItemsSource = this.DynamicArguments;
514
515             UpdateChildrenElementStatus();
516         }
517
518         internal void ValidateEntry(DynamicArgumentWrapperObject entry, DependencyPropertyChangedEventArgs e)
519         {
520             if (e.Property == DynamicArgumentWrapperObject.NameProperty)
521             {
522                 if (this.IsDictionary.Value)
523                 {
524                     DataGridRow row = entry.Row;
525                     string newName = e.NewValue as string;
526
527                     bool duplicates =
528                         this.DynamicArguments.Any<DynamicArgumentWrapperObject>(
529                             p => string.Equals(p.Name, newName) && p != entry);
530                     if (duplicates || string.IsNullOrEmpty(newName))
531                     {
532                         entry.Name = e.OldValue as string;
533                         if (duplicates)
534                         {
535                             ErrorReporting.ShowErrorMessage(string.Format(CultureInfo.CurrentCulture, SR.DuplicateArgumentName, newName));
536                         }
537                         else
538                         {
539                             ErrorReporting.ShowErrorMessage(string.Format(CultureInfo.CurrentCulture, SR.EmptyArgumentName));
540                         }
541                     }
542                     entry.IsValidating = false;
543                 }
544             }
545             else
546             {
547                 if (e.Property == DynamicArgumentWrapperObject.DirectionProperty)
548                 {
549                     entry.UseLocationExpression = (entry.Direction != ArgumentDirection.In);
550                 }                
551                 if ((e.Property != DynamicArgumentWrapperObject.ExpressionProperty) && (entry.Expression != null))
552                 {
553                     ActivityWithResult expression = entry.Expression.GetCurrentValue() as ActivityWithResult;                    
554                     if (expression != null)
555                     {
556                         ActivityWithResult newExpression;
557                         if (ExpressionHelper.TryMorphExpression(expression, entry.UseLocationExpression, entry.Type, this.Context, out newExpression))
558                         {
559                             entry.Expression = (this.OwnerActivity as IModelTreeItem).ModelTreeManager.WrapAsModelItem(newExpression);
560                         }
561                         else
562                         {
563                             //Microsoft 
564
565                             entry.Expression = null;
566                         }
567                     }
568                 }
569                 entry.IsValidating = false;
570             }
571         }
572
573         internal string GetDefaultName()
574         {
575             if (!this.IsDictionary.Value)
576             {
577                 return string.Empty;
578             }
579             else
580             {
581                 var defaultNames = this.DynamicArguments
582                         .Select<DynamicArgumentWrapperObject, string>(p => (string)p.Name)
583                         .Where<string>(p => 0 == string.Compare(p, 0, this.ArgumentPrefix, 0, this.ArgumentPrefix.Length, StringComparison.Ordinal))
584                         .Select(p => p.Substring(this.ArgumentPrefix.Length));
585
586                 int maxNum = 1;
587                 foreach (string numberPart in defaultNames)
588                 {
589                     int current;
590                     if (int.TryParse(numberPart, out current))
591                     {
592                         if (current >= maxNum)
593                         {
594                             maxNum = current + 1;
595                         }
596                     }
597                 }
598                 return string.Format(CultureInfo.InvariantCulture, "{0}{1}", this.ArgumentPrefix, maxNum);
599             }
600
601         }
602
603         internal Type GetDefaultType()
604         {
605             Type[] genericArguments = this.UnderlyingArgumentType.GetGenericArguments();
606             if (genericArguments.Length == 0)
607             {
608                 return typeof(string);
609             }
610             else
611             {
612                 return genericArguments[0];
613             }
614         }
615
616         internal ArgumentDirection GetDefaultDirection()
617         {
618             if (this.UnderlyingArgumentType == ArgumentType)
619             {
620                 return ArgumentDirection.In;
621             }
622
623             if (InArgumentType.IsAssignableFrom(this.UnderlyingArgumentType))
624             {
625                 return ArgumentDirection.In;
626             }
627
628             if (OutArgumentType.IsAssignableFrom(this.UnderlyingArgumentType))
629             {
630                 return ArgumentDirection.Out;
631             }
632
633             Fx.Assert(InOutArgumentType.IsAssignableFrom(this.UnderlyingArgumentType), "UnderlyingArgumentType should be of type OutArgumentType");
634             return ArgumentDirection.InOut;
635         }
636
637         //Hook LoadingRow event to set different row template (<Click here to add new item>) for new place holder
638         void DataGrid_Standard_LoadingRow(object sender, DataGridRowEventArgs e)
639         {
640             if (e.Row.Item != CollectionView.NewItemPlaceholder)
641             {
642                 DynamicArgumentWrapperObject wrapper = e.Row.Item as DynamicArgumentWrapperObject;
643                 wrapper.Row = e.Row;
644             }
645         }
646
647         void OnCreateDynamicArgumentExecute(object sender, ExecutedRoutedEventArgs e)
648         {
649             DynamicArgumentWrapperObject wrapper = new DynamicArgumentWrapperObject();
650             this.DynamicArguments.Add(wrapper);
651             this.dgHelper.BeginRowEdit(wrapper);
652         }
653
654         void OnMoveUpArgumentExecute(object sender, RoutedEventArgs e)
655         {
656             if (null != this.WPF_DataGrid.SelectedItem)
657             {
658                 int selectedArgumentIndex = this.WPF_DataGrid.SelectedIndex;
659                 if (selectedArgumentIndex > 0)
660                 {
661                     this.DynamicArguments.Move(selectedArgumentIndex, selectedArgumentIndex - 1);
662                 }
663                 this.OnDataGridSelectionChanged(this, null);
664             }
665         }
666
667         void OnMoveDownArgumentExecute(object sender, RoutedEventArgs e)
668         {
669             if (null != this.WPF_DataGrid.SelectedItem)
670             {
671                 int selectedArgumentIndex = this.WPF_DataGrid.SelectedIndex;
672                 if (selectedArgumentIndex < this.DynamicArguments.Count - 1)
673                 {
674                     this.DynamicArguments.Move(selectedArgumentIndex, selectedArgumentIndex + 1);
675                 }
676                 this.OnDataGridSelectionChanged(this, null);
677             }
678         }
679
680         void OnDeleteArgumentExecute(object sender, RoutedEventArgs e)
681         {
682             DataGridHelper.OnDeleteSelectedItems(this.WPF_DataGrid);
683         }
684
685         void OnExpressionTextBoxLoaded(object sender, RoutedEventArgs args)
686         {
687             ExpressionTextBox etb = (ExpressionTextBox)sender;
688             etb.IsIndependentExpression = true;
689             if (!etb.IsReadOnly)
690             {
691                 DataGridHelper.OnEditingControlLoaded(sender, args);
692             }
693         }
694
695         void OnExpressionTextBoxUnloaded(object sender, RoutedEventArgs args)
696         {
697             ExpressionTextBox etb = (ExpressionTextBox)sender;
698             if (!etb.IsReadOnly)
699             {
700                 DataGridHelper.OnEditingControlUnloaded(sender, args);
701             }
702         }
703
704         void OnEditingControlLoaded(object sender, RoutedEventArgs args)
705         {
706             DataGridHelper.OnEditingControlLoaded(sender, args);
707         }
708
709         void OnEditingControlUnloaded(object sender, RoutedEventArgs args)
710         {
711             DataGridHelper.OnEditingControlUnloaded(sender, args);
712         }
713     }
714
715     sealed class DynamicArgumentWrapperObject : DependencyObject
716     {
717         public static readonly DependencyProperty ModelItemProperty = DependencyProperty.Register(
718             "ModelItem",
719             typeof(ModelItem),
720             typeof(DynamicArgumentWrapperObject),
721             new UIPropertyMetadata(null));
722
723         public static readonly DependencyProperty NameProperty = DependencyProperty.Register(
724             "Name",
725             typeof(string),
726             typeof(DynamicArgumentWrapperObject),
727             new UIPropertyMetadata(string.Empty, OnArgumentPropertyChanged));
728
729         public static readonly DependencyProperty ArgumentTypeProperty = DependencyProperty.Register(
730             "Type",
731             typeof(Type),
732             typeof(DynamicArgumentWrapperObject),
733             new UIPropertyMetadata(typeof(string), OnArgumentPropertyChanged));
734
735         public static readonly DependencyProperty DirectionProperty = DependencyProperty.Register(
736             "Direction",
737             typeof(ArgumentDirection),
738             typeof(DynamicArgumentWrapperObject),
739             new UIPropertyMetadata(ArgumentDirection.In, OnArgumentPropertyChanged));
740
741         public static readonly DependencyProperty ExpressionProperty =
742             DependencyProperty.Register(
743             "Expression",
744             typeof(ModelItem),
745             typeof(DynamicArgumentWrapperObject),
746             new UIPropertyMetadata(OnArgumentPropertyChanged));
747
748         public static readonly DependencyProperty UseLocationExpressionProperty =
749             DependencyProperty.Register(
750             "UseLocationExpression",
751             typeof(bool),
752             typeof(DynamicArgumentWrapperObject));
753
754         public event PropertyChangedEventHandler PropertyChanged;
755
756         bool isInitializing;
757
758         const string ExpressionPropertyName = "Expression";
759
760         public string Name
761         {
762             get { return (string)GetValue(NameProperty); }
763             set { SetValue(NameProperty, value); }
764         }
765
766         public Type Type
767         {
768             get { return (Type)GetValue(ArgumentTypeProperty); }
769             set { SetValue(ArgumentTypeProperty, value); }
770         }
771
772         public ArgumentDirection Direction
773         {
774             get { return (ArgumentDirection)GetValue(DirectionProperty); }
775             set { SetValue(DirectionProperty, value); }
776         }
777
778         public ModelItem Expression
779         {
780             get { return (ModelItem)GetValue(ExpressionProperty); }
781             set { SetValue(ExpressionProperty, value); }
782         }
783
784         public bool UseLocationExpression
785         {
786             get { return (bool)GetValue(UseLocationExpressionProperty); }
787             set { SetValue(UseLocationExpressionProperty, value); }
788         }
789
790         internal bool IsValidating
791         {
792             get;
793             set;
794         }
795
796         internal DataGridRow Row
797         {
798             get;
799             set;
800         }
801
802         public static DynamicArgumentDesigner Editor
803         {
804             get;
805             set;
806         }
807
808         public DynamicArgumentWrapperObject()
809         {
810             this.isInitializing = true;
811             this.IsValidating = false;
812             this.Name = DynamicArgumentWrapperObject.Editor.GetDefaultName();
813             this.Type = DynamicArgumentWrapperObject.Editor.GetDefaultType();
814             this.Direction = DynamicArgumentWrapperObject.Editor.GetDefaultDirection();
815             this.UseLocationExpression = (this.Direction != ArgumentDirection.In);
816             this.isInitializing = false;
817         }
818
819         public DynamicArgumentWrapperObject(string argumentName, ModelItem argumentItem)
820         {
821             Fx.Assert(argumentItem != null, "argumentItem canot be null");
822             this.isInitializing = true;
823             this.IsValidating = false;            
824             Argument argument = (Argument)argumentItem.GetCurrentValue();
825             this.Name = argumentName;
826             this.Direction = argument.Direction;
827             this.UseLocationExpression = (this.Direction != ArgumentDirection.In);
828             this.Type = argument.ArgumentType;
829             this.Expression = argumentItem.Properties[ExpressionPropertyName].Value;
830             this.isInitializing = false;
831         }
832
833         static void OnArgumentPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
834         {
835             DynamicArgumentWrapperObject wrapper = (DynamicArgumentWrapperObject)sender;
836             if (!wrapper.IsValidating && !wrapper.isInitializing)
837             {
838                 wrapper.OnArgumentPropertyChanged(e);
839             }
840         }
841
842         void OnArgumentPropertyChanged(DependencyPropertyChangedEventArgs e)
843         {
844             Fx.Assert(DynamicArgumentWrapperObject.Editor != null, "collection editor is null!");
845             this.IsValidating = true;
846             DynamicArgumentWrapperObject.Editor.ValidateEntry(this, e);
847
848             if (this.PropertyChanged != null)
849             {
850                 this.PropertyChanged(this, new PropertyChangedEventArgs(e.Property.Name));
851             }
852         }
853
854         // For screen reader to read the DataGrid row.
855         public override string ToString()
856         {
857             return string.IsNullOrEmpty(this.Name) ? "Parameter" : this.Name;
858         }
859     }
860 }