[corlib] Avoid unnecessary ephemeron array resizes
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / View / DataGridHelper.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5 namespace System.Activities.Presentation.View
6 {
7     using System;
8     using System.Activities.Presentation.Internal.PropertyEditing;
9     using System.Activities.Presentation.Internal.PropertyEditing.Model;
10     using System.Activities.Presentation.Model;
11     using System.Activities.Presentation.PropertyEditing;
12     using System.Collections;
13     using System.Collections.Generic;
14     using System.Diagnostics.CodeAnalysis;
15     using System.Globalization;
16     using System.Linq;
17     using System.Runtime;
18     using System.Windows;
19     using System.Windows.Automation;
20     using System.Windows.Controls;
21     using System.Windows.Controls.Primitives;
22     using System.Windows.Data;
23     using System.Windows.Input;
24     using System.Windows.Media;
25     using System.Windows.Threading;
26     using System.ComponentModel;
27     using System.Collections.ObjectModel;
28     using System.Windows.Media.Effects;
29
30     [SuppressMessage(FxCop.Category.Xaml, FxCop.Rule.TypesShouldHavePublicParameterlessConstructors,
31         Justification = "This class is never supposed to be created in xaml directly.")]
32     sealed partial class DataGridHelper
33     {
34         public static readonly string PART_ButtonAdd = "PART_ButtonAdd";
35         static readonly string dynamicContentControlName = "PART_Dynamic";
36
37         //content of the Add new row button
38         public static readonly DependencyProperty AddNewRowContentProperty =
39             DependencyProperty.Register("AddNewRowContent", typeof(object), typeof(DataGridHelper), new UIPropertyMetadata("<Add new row>"));
40
41         //binding to the command, which gets executed when new row button is clicked
42         public static readonly DependencyProperty AddNewRowCommandProperty =
43             DependencyProperty.Register("AddNewRowCommand", typeof(ICommand), typeof(DataGridHelper), new UIPropertyMetadata(null));
44
45         //attached property - used to store reference to data grid helper within data grid instance
46         static readonly DependencyProperty DGHelperProperty =
47             DependencyProperty.RegisterAttached("DGHelper", typeof(DataGridHelper), typeof(DataGrid), new UIPropertyMetadata(null));
48
49         static readonly DependencyProperty ControlBehaviorProperty =
50             DependencyProperty.RegisterAttached("ControlBehavior", typeof(EditingControlBehavior), typeof(DataGridHelper), new UIPropertyMetadata(null));
51
52         static readonly DependencyProperty NewRowLoadedProperty =
53             DependencyProperty.RegisterAttached("NewRowLoaded", typeof(bool), typeof(DataGridHelper), new UIPropertyMetadata(false));
54
55         static readonly DependencyProperty IsCommitInProgressProperty =
56             DependencyProperty.RegisterAttached("IsCommitInProgress", typeof(bool), typeof(DataGridHelper), new UIPropertyMetadata(false));
57
58         public static readonly DependencyProperty ShowValidationErrorAsToolTipProperty =
59             DependencyProperty.Register("ShowValidationErrorAsToolTip", typeof(bool), typeof(DataGridHelper), new UIPropertyMetadata(false));
60
61         public static readonly DependencyProperty IsCustomEditorProperty =
62             DependencyProperty.RegisterAttached("IsCustomEditor", typeof(bool), typeof(DataGridHelper), new UIPropertyMetadata(false));
63
64         public event EventHandler<DataGridCellEditEndingEventArgs> DataGridCellEditEnding;
65
66         static DataTemplate dynamicCellContentTemplate;
67         static Dictionary<Type, Type> EditorBehaviorTypeMapping = new Dictionary<Type, Type>
68         {
69             { typeof(ExpressionTextBox), typeof(ExpressionTextBoxBehavior) },
70             { typeof(TextBox), typeof(TextBoxBehavior) },
71             { typeof(TypePresenter), typeof(TypePresenterBehavior) },
72             { typeof(VBIdentifierDesigner), typeof(VBIdentifierDesignerBehavior) },
73         };
74
75         DataGrid dataGrid;
76         bool isNewRowAdded;
77
78         Func<ResolveTemplateParams, bool> resolveDynamicTemplateCallback;
79         Dictionary<string, DataGridColumn> MemberPathToColumnDict = new Dictionary<string, DataGridColumn>();
80
81         [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.ReviewUnusedParameters, Justification = "Existing code")]
82         public DataGridHelper(DataGrid instance, Control owner)
83         {
84             this.InitializeComponent();
85
86             this.dataGrid = instance;
87             //apply default cell style
88             this.ApplyCellStyle();
89             //apply default row style
90             this.ApplyRowStyle();
91             //apply default datagrid style
92             this.dataGrid.Style = (Style)this.FindResource("defaultDataGridStyle");
93             //handle data grid's loading event
94             this.dataGrid.LoadingRow += OnDataGridRowLoading;
95             //store reference to data grid helper within datagrid
96             DataGridHelper.SetDGHelper(this.dataGrid, this);
97             this.dataGrid.MouseDown += OnDataGridMouseDown;
98             this.dataGrid.Sorting += OnDataGridSorting;
99             this.dataGrid.CellEditEnding += OnDataGridCellEditEnding;
100             this.InitMemberPathToColumnDict();
101         }
102
103         public static bool GetIsCustomEditor(DependencyObject obj)
104         {
105             return (bool)obj.GetValue(IsCustomEditorProperty);
106         }
107
108         public static void SetIsCustomEditor(DependencyObject obj, bool value)
109         {
110             obj.SetValue(IsCustomEditorProperty, value);
111         }
112
113         void OnDataGridCellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
114         {
115             if (this.DataGridCellEditEnding != null)
116             {
117                 this.DataGridCellEditEnding(sender, e);
118             }
119
120             if ((!e.Cancel) && (!GetIsCommitInProgress(this.dataGrid)))
121             {
122                 SetIsCommitInProgress(this.dataGrid, true);
123                 //try to commit edit
124                 bool commitSucceeded = false;
125                 try
126                 {
127                     commitSucceeded = this.dataGrid.CommitEdit(DataGridEditingUnit.Row, true);
128                 }
129                 catch (InvalidOperationException)
130                 {
131                     // Ignore and cancel edit
132                 }
133                 finally
134                 {
135                     //if commit fails - undo change
136                     if (!commitSucceeded)
137                     {
138                         this.dataGrid.CancelEdit();
139                     }
140                 }
141                 SetIsCommitInProgress(this.dataGrid, false);
142             }
143         }
144
145         void InitMemberPathToColumnDict()
146         {
147             MemberPathToColumnDict.Clear();
148             foreach (DataGridColumn column in this.dataGrid.Columns)
149             {
150                 if (column.CanUserSort &&
151                     !string.IsNullOrEmpty(column.SortMemberPath) &&
152                     !MemberPathToColumnDict.ContainsKey(column.SortMemberPath))
153                 {
154                     MemberPathToColumnDict.Add(column.SortMemberPath, column);
155                 }
156             }
157         }
158
159         void OnDataGridSorting(object sender, DataGridSortingEventArgs e)
160         {
161             bool primaryColumnSorted = false;
162             ListSortDirection direction = (e.Column.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending;
163             e.Column.SortDirection = null;
164
165             foreach (SortDescription description in this.dataGrid.Items.SortDescriptions.Reverse())
166             {
167                 if (MemberPathToColumnDict[description.PropertyName].SortDirection == null)
168                 {
169                     this.dataGrid.Items.SortDescriptions.Remove(description);
170                 }
171                 else if (description.PropertyName == this.dataGrid.Columns[0].SortMemberPath)
172                 {
173                     primaryColumnSorted = true;
174                 }
175             }
176
177             this.dataGrid.Items.SortDescriptions.Add(new SortDescription(e.Column.SortMemberPath, direction));
178             e.Column.SortDirection = direction;
179             if (e.Column != this.dataGrid.Columns[0] && !primaryColumnSorted)
180             {
181                 this.dataGrid.Items.SortDescriptions.Add(new SortDescription(this.dataGrid.Columns[0].SortMemberPath, ListSortDirection.Ascending));
182             }
183             this.dataGrid.Items.Refresh();
184
185             e.Handled = true;
186         }
187
188         //Hook KeyDown event on DataGrid row to workaround DataGrid bug with customized NewItemPlaceHolder        
189         void OnDataGridRowKeyDown(object sender, KeyEventArgs e)
190         {
191             if (e.Handled)
192             {
193                 return;
194             }
195             if (e.Key == Key.Enter || e.Key == Key.Escape)
196             {
197                 // If currentCell is the cell containing AddNewRowTemplate, its Column will be null since this cell
198                 // spread across all columns in the grid. In this case, consume the event with no action.
199                 if (dataGrid.CurrentCell.Column != null)
200                 {
201                     DataGridCellInfo currentCell = dataGrid.CurrentCell;
202                     ObservableCollection<DataGridColumn> columns = dataGrid.Columns;
203                     ItemCollection items = dataGrid.Items;
204                     int currentColumnIndex = columns.IndexOf(dataGrid.ColumnFromDisplayIndex(currentCell.Column.DisplayIndex));
205                     DataGridCell currentCellContainer = GetCell(dataGrid, items.IndexOf(currentCell.Item), currentColumnIndex);
206                     if ((currentCellContainer != null) && (columns.Count > 0))
207                     {
208                         int numItems = items.Count;
209                         bool shiftModifier = ((e.KeyboardDevice.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift);
210                         int index = Math.Max(0, Math.Min(numItems - 1, items.IndexOf(currentCell.Item) + (shiftModifier ? -1 : 1)));
211                         if (index < numItems)
212                         {
213                             if (items[index] == CollectionView.NewItemPlaceholder)
214                             {
215                                 CommitAnyEdit(currentCellContainer);
216                                 e.Handled = true;
217                             }
218                         }
219                     }
220                     else
221                     {
222                         e.Handled = true;
223                     }
224                 }
225                 else
226                 {
227                     e.Handled = true;
228                 }
229             }
230         }
231
232         void CommitAnyEdit(DataGridCell currentCellContainer)
233         {
234             IEditableCollectionView editableItems = (IEditableCollectionView)(this.dataGrid.Items);
235             DataGridCell cell = currentCellContainer;
236             bool isCurrentCellEditing = false;
237             this.ExplicitCommit = true;
238             if (cell != null)
239             {
240                 isCurrentCellEditing = cell.IsEditing;
241             }
242             if (editableItems.IsAddingNew || editableItems.IsEditingItem)
243             {
244                 this.dataGrid.CommitEdit(DataGridEditingUnit.Row, true);
245             }
246             else if (isCurrentCellEditing)
247             {
248                 this.dataGrid.CommitEdit(DataGridEditingUnit.Cell, true);
249             }
250             this.ExplicitCommit = false;
251         }
252
253         //callback executed whenever user clicks AddNewRow
254         public Func<DataGrid, object, object> NotifyNewRowAddedCallback
255         {
256             get;
257             set;
258         }
259
260         //callback executed whenever users starts editing cell
261         public Action<Control, DataGridCell, bool> NotifyBeginCellEditCallback
262         {
263             get;
264             set;
265         }
266
267         //callback executed whenver cell edit is complete
268         public Action<Control, DataGridCell> NotifyEndCellEditCallback
269         {
270             get;
271             set;
272         }
273
274         public bool ExplicitCommit
275         {
276             get;
277             private set;
278         }
279
280         internal DataGrid DataGrid
281         {
282             get { return this.dataGrid; }
283         }
284
285         internal bool IsEditInProgress
286         {
287             get { return GetIsCommitInProgress(this.dataGrid); }
288         }
289
290         internal EditingContext Context
291         {
292             get;
293             set;
294         }
295
296         //callback executed whenever dynamic content cell is loaded
297         //parameters: 
298         //  - clicked data grid cell,
299         //  - reference to data item in given dg row
300         //  - boolean value indicating whether cell is beeing edited or viewewd
301         //returns:
302         // - data template to be applied
303         public Func<ResolveTemplateParams, bool> ResolveDynamicTemplateCallback
304         {
305             get { return this.resolveDynamicTemplateCallback; }
306             set
307             {
308                 this.resolveDynamicTemplateCallback = value;
309                 //if user adds dynamic template, we need to hook for EditMode button event - ShowDialogEditor; 
310                 //otherwise, clicking on that button wouldn't have any effect, since it is normally handled by property grid
311                 bool containsBinding = this.dataGrid.CommandBindings
312                     .Cast<CommandBinding>()
313                     .Any(cb => ICommand.Equals(cb.Command, PropertyValueEditorCommands.ShowDialogEditor));
314
315                 if (!containsBinding)
316                 {
317                     var cb = new CommandBinding(PropertyValueEditorCommands.ShowDialogEditor, this.OnShowPropertyValueEditor, this.OnCanShowPropertyValueEditor);
318                     this.dataGrid.CommandBindings.Add(cb);
319                 }
320             }
321         }
322
323         //callback executed whenever user clicks extended dialog property editor in the data grid - 
324         //client has to specify reference to edited model property, which will be placed in extended editor dialog
325         //parameters:
326         // - clicked data grid cell
327         // - reference to data item in given dg row
328         //returns:
329         // - reference to model property which should be displayed
330         public Func<DataGridCell, object, ModelProperty> LoadDynamicContentDataCallback
331         {
332             get;
333             set;
334         }
335
336         //callback executed whenever user clicks extended dialog property editor in the data grid
337         //parameters:
338         // - clicked data grid cell
339         // - reference to data item in given dg row
340         //returns:
341         // - instance of dialog property value editor
342         public Func<DataGridCell, object, DialogPropertyValueEditor> LoadCustomPropertyValueEditorCallback
343         {
344             get;
345             set;
346         }
347
348         //default row template 
349         ControlTemplate DefaultRowControlTemplate
350         {
351             get;
352             set;
353         }
354
355         ContentPresenter AddNewRowContentPresenter
356         {
357             get;
358             set;
359         }
360
361         //property containing content displayed on the Add new row button
362         public object AddNewRowContent
363         {
364             get { return (object)GetValue(AddNewRowContentProperty); }
365             set { SetValue(AddNewRowContentProperty, value); }
366         }
367
368         //command bound to add new row button
369         public ICommand AddNewRowCommand
370         {
371             get { return (ICommand)GetValue(AddNewRowCommandProperty); }
372             set { SetValue(AddNewRowCommandProperty, value); }
373         }
374
375         public bool ShowValidationErrorAsToolTip
376         {
377             get { return (bool)GetValue(ShowValidationErrorAsToolTipProperty); }
378             set { SetValue(ShowValidationErrorAsToolTipProperty, value); }
379         }
380
381         //helper method - returns selected data grid item casted to the target type
382         public T SelectedItem<T>() where T : class
383         {
384             return this.dataGrid.SelectedItem as T;
385         }
386
387         public T Source<T>() where T : class
388         {
389             return (T)this.dataGrid.ItemsSource;
390         }
391
392         public void BeginRowEdit(object value, DataGridColumn column)
393         {
394             if (null == value)
395             {
396                 throw FxTrace.Exception.AsError(new ArgumentNullException("value"));
397             }
398             if (null == column)
399             {
400                 throw FxTrace.Exception.AsError(new ArgumentNullException("column"));
401             }
402             int columnIndex = this.dataGrid.Columns.IndexOf(column);
403             if (columnIndex < 0)
404             {
405                 throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("column"));
406             }
407             ICollectionView items = CollectionViewSource.GetDefaultView(this.dataGrid.ItemsSource);
408             if (null != items)
409             {
410                 this.CommitDataGrid();
411                 this.dataGrid.SelectedItem = null;
412                 //lookup element in the collection
413                 if (items.MoveCurrentTo(value))
414                 {
415                     //set the SelectedItem to passed value
416                     this.dataGrid.SelectedItem = value;
417                     //get the cell which contains given elemnt
418                     DataGridCell cell = DataGridHelper.GetCell(this.dataGrid, items.CurrentPosition, columnIndex);
419                     //and begin edit
420                     if (null != cell)
421                     {
422                         cell.Focus();
423                         dataGrid.BeginEdit();
424                     }
425                 }
426                 else
427                 {
428                     throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("value"));
429                 }
430             }
431         }
432
433         public void BeginRowEdit(object value)
434         {
435             var column = this.dataGrid.Columns[0];
436             int index = 1;
437             while (null != column && column.Visibility == Visibility.Hidden && this.dataGrid.Columns.Count > index)
438             {
439                 column = this.dataGrid.Columns[index];
440                 ++index;
441             }
442             this.BeginRowEdit(value, column);
443         }
444
445         void OnDataGridRowLoading(object sender, DataGridRowEventArgs e)
446         {
447             if (this.DefaultRowControlTemplate == null)
448             {
449                 this.DefaultRowControlTemplate = e.Row.Template;
450             }
451
452             if (e.Row.Item == CollectionView.NewItemPlaceholder)
453             {
454                 e.Row.Style = (Style)this.FindResource("defaultNewRowStyle");
455                 e.Row.UpdateLayout();
456             }
457         }
458
459         void OnAddNewRowContentPresenterLoaded(object sender, RoutedEventArgs args)
460         {
461             var presenter = (ContentPresenter)sender;
462             this.AddNewRowContentPresenter = presenter;
463             if (null != this.AddNewRowContent)
464             {
465                 if (this.AddNewRowContent is DataTemplate)
466                 {
467                     presenter.ContentTemplate = (DataTemplate)this.AddNewRowContent;
468                     presenter.ApplyTemplate();
469                 }
470                 else
471                 {
472                     presenter.ContentTemplate = (DataTemplate)this.FindResource("defaultAddNewRowTemplate");
473                     presenter.ApplyTemplate();
474                     presenter.Content = this.AddNewRowContent.ToString();
475                 }
476             }
477         }
478
479         void OnAddNewRowClick(object sender, RoutedEventArgs args)
480         {
481             //user clicked on AddNew row - commit all pending changes
482             this.CommitDataGrid();
483             Button btn = (Button)sender;
484             //if there is callback registered 
485             if (null != this.NotifyNewRowAddedCallback)
486             {
487                 //execute it
488                 object added = this.NotifyNewRowAddedCallback(this.dataGrid, btn.CommandParameter);
489                 //if add was successfull, begin editing new row
490                 this.isNewRowAdded = (null != added);
491                 if (this.isNewRowAdded)
492                 {
493                     this.BeginRowEdit(added);
494                 }
495             }
496             //if there is command registered
497             else if (null != this.AddNewRowCommand)
498             {
499                 //try to invoke command as routed command, the as the interface command
500                 RoutedCommand cmd = this.AddNewRowCommand as RoutedCommand;
501                 if (null == cmd)
502                 {
503                     if (this.AddNewRowCommand.CanExecute(btn.CommandParameter))
504                     {
505                         this.AddNewRowCommand.Execute(btn.CommandParameter);
506                         this.isNewRowAdded = true;
507                     }
508                 }
509                 else
510                 {
511                     if (cmd.CanExecute(btn.CommandParameter, this.dataGrid))
512                     {
513                         cmd.Execute(btn.CommandParameter, this.dataGrid);
514                         this.isNewRowAdded = true;
515                     }
516                 }
517             }
518         }
519
520         void OnAddNewRowGotFocus(object sender, RoutedEventArgs e)
521         {
522             //When tab over the last row, the last column won't get commit by default, which is a bug of DataGrid with
523             //customized new place holder template. Call commit explicitly here to workaround this issue
524             this.CommitDataGrid();
525             this.dataGrid.SelectedItem = null;
526         }
527
528         void CommitDataGrid()
529         {
530             if (!GetIsCommitInProgress(this.dataGrid))
531             {
532                 SetIsCommitInProgress(this.dataGrid, true);
533                 this.ExplicitCommit = true;                
534                 this.dataGrid.CommitEdit(DataGridEditingUnit.Row, true);
535                 this.ExplicitCommit = false;
536                 SetIsCommitInProgress(this.dataGrid, false);
537             }
538         }
539
540         void NotifyEditingControlLoaded(Control control, DataGridCell cell, bool isNewRowLoaded)
541         {
542             Type controlType = control.GetType();
543             Type editorBehaviorType;
544             if (EditorBehaviorTypeMapping.ContainsKey(controlType))
545             {
546                 editorBehaviorType = EditorBehaviorTypeMapping[controlType];
547             }
548             else
549             {
550                 editorBehaviorType = typeof(DefaultControlBehavior);
551             }
552
553             EditingControlBehavior behavior = Activator.CreateInstance(editorBehaviorType, this.dataGrid) as EditingControlBehavior;
554             bool isHandled = behavior.HandleControlLoaded(control, cell, isNewRowLoaded);
555             if (isHandled)
556             {
557                 SetControlBehavior(control, behavior);
558             }
559
560             if (null != this.NotifyBeginCellEditCallback)
561             {
562                 this.NotifyBeginCellEditCallback(control, cell, isNewRowLoaded);
563             }
564         }
565
566         void NotifyEditingControlUnloaded(Control control, DataGridCell cell)
567         {
568             bool isHandled = false;
569
570             EditingControlBehavior behavior = GetControlBehavior(control);
571
572             if (null != behavior)
573             {
574                 isHandled = behavior.ControlUnloaded(control, cell);
575             }
576
577             if (null != this.NotifyEndCellEditCallback)
578             {
579                 this.NotifyEndCellEditCallback(control, cell);
580             }
581         }
582
583         void OnCanShowPropertyValueEditor(object sender, CanExecuteRoutedEventArgs args)
584         {
585             Fx.Assert(this.LoadCustomPropertyValueEditorCallback != null, "LoadCustomPropertyValueEditorCallback is not set!");
586             Fx.Assert(this.LoadDynamicContentDataCallback != null, "LoadDynamicContentDataCallback is not set!");
587
588             if (null != this.LoadDynamicContentDataCallback && null != this.LoadCustomPropertyValueEditorCallback)
589             {
590                 var cell = VisualTreeUtils.FindVisualAncestor<DataGridCell>((DependencyObject)args.OriginalSource);
591                 var row = VisualTreeUtils.FindVisualAncestor<DataGridRow>(cell);
592                 args.CanExecute = null != this.LoadCustomPropertyValueEditorCallback(cell, row.Item);
593             }
594         }
595
596         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
597             Justification = "Propagating exceptions might lead to VS crash.")]
598         [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
599             Justification = "Propagating exceptions might lead to VS crash.")]
600         void OnShowPropertyValueEditor(object sender, ExecutedRoutedEventArgs args)
601         {
602             //user clicked on dialog's property editor's button - now we need to show custom designer in dialog mode
603             var cell = VisualTreeUtils.FindVisualAncestor<DataGridCell>((DependencyObject)args.OriginalSource);
604             var row = VisualTreeUtils.FindVisualAncestor<DataGridRow>(cell);
605             //ask client for custom editor, given for currently selected row
606             var editor = this.LoadCustomPropertyValueEditorCallback(cell, row.Item);
607
608             Fx.Assert(editor != null, "Custom property value editor is not set or doesn't derive from DialogPropertyValueEditor!");
609             if (null != editor)
610             {
611                 //out of currently selected row, get actual property which is beeing edited
612                 var value = this.LoadDynamicContentDataCallback(cell, row.Item);
613
614                 Fx.Assert(value != null, "ModelProperty shouldn't be null");
615
616                 //create model property entry - it is required by dialog property editor
617                 var propertyEntry = new ModelPropertyEntry(value, null);
618                 try
619                 {
620                     editor.ShowDialog(propertyEntry.PropertyValue, (IInputElement)args.OriginalSource);
621                 }
622                 catch (Exception err)
623                 {
624                     ErrorReporting.ShowErrorMessage(err);
625                 }
626             }
627         }
628
629         internal static void OnEditingControlLoaded(object sender, RoutedEventArgs args)
630         {
631             //editing control has been loaded - user starts editing the cell
632             Control ctrl = (Control)sender;
633
634             //get the data grid reference from control
635             DataGrid dg = VisualTreeUtils.FindVisualAncestor<DataGrid>(ctrl);
636             Fx.Assert(null != dg, string.Format(CultureInfo.CurrentCulture, "DataGrid is not in the visual tree of this control: {0}", ctrl));
637             if (null != dg)
638             {
639                 //get the target instance of data grid helper
640                 DataGridHelper helper = DataGridHelper.GetDGHelper(dg);
641                 //store data grid helper in the control
642                 DataGridHelper.SetDGHelper(ctrl, helper);
643                 if (null != helper)
644                 {
645                     //notify user that given control is becoming acive one
646                     DataGridCell cell = VisualTreeUtils.FindVisualAncestor<DataGridCell>(ctrl);
647                     helper.NotifyEditingControlLoaded(ctrl, cell, helper.isNewRowAdded);
648                     helper.isNewRowAdded = false;
649                 }
650             }
651         }
652
653         internal static void OnEditingControlUnloaded(object sender, RoutedEventArgs args)
654         {
655             //editing control has been unloaded - user ends editing the cell
656             Control ctrl = (Control)sender;
657
658             //get data grid helper out of it
659             DataGridHelper helper = DataGridHelper.GetDGHelper(ctrl);
660
661             //notify user that edit is complete
662             if (null != helper)
663             {
664                 DataGridCell cell = VisualTreeUtils.FindVisualAncestor<DataGridCell>(ctrl);
665                 helper.NotifyEditingControlUnloaded(ctrl, cell);
666             }
667         }
668
669         void OnPreviewCellMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
670         {
671             //When Ctrl of Shift is pressed, let DataGrid to handle multi-selection
672             //and DataGrid shouldn't enter editing mode in this case
673             if ((Keyboard.IsKeyDown(Key.RightShift)) || (Keyboard.IsKeyDown(Key.LeftShift)) ||
674                 (Keyboard.IsKeyDown(Key.LeftCtrl)) || (Keyboard.IsKeyDown(Key.RightCtrl)))
675             {
676                 return;
677             }
678
679             //support for single click edit
680             DataGridCell cell = sender as DataGridCell;
681             //enter this code only if cell is not beeing edited already and is not readonly
682             if (null != cell && !cell.IsEditing && !cell.IsReadOnly && null != this.dataGrid.SelectedItem)
683             {
684                 bool shouldFocus = true;
685                 //depending on the selection type - either select cell or row
686                 if (this.dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
687                 {
688                     if (!cell.IsSelected)
689                     {
690                         cell.IsSelected = true;
691                     }
692                 }
693                 else
694                 {
695                     DataGridRow row = VisualTreeUtils.FindVisualAncestor<DataGridRow>(cell);
696                     //if row was not selected - first click will select it, second will start editing cell's value
697                     if (null != row && !row.IsSelected)
698                     {
699                         this.dataGrid.SelectedItem = row;
700                         shouldFocus = false;
701                     }
702                 }
703
704                 //if allowed - begin edit
705                 if ((shouldFocus && !cell.IsFocused) && !GetIsCustomEditor(cell))
706                 {
707                     //attempt to set focus to the cell, and let DG start editing                    
708                     if (cell.Focus() && !cell.IsEditing)
709                     {
710                         if (dataGrid.SelectionUnit == DataGridSelectionUnit.FullRow)
711                         {
712                             dataGrid.SelectedItems.Clear();
713                             DataGridRow row = VisualTreeUtils.FindVisualAncestor<DataGridRow>(cell);
714                             if (row != null)
715                             {
716                                 dataGrid.SelectedItems.Add(dataGrid.ItemContainerGenerator.ItemFromContainer(row));
717                             }
718                         }
719                         else
720                         {
721                             dataGrid.SelectedCells.Clear();
722                             dataGrid.SelectedCells.Add(new DataGridCellInfo(cell));
723                         }
724                         this.dataGrid.BeginEdit();
725                     }
726                 }
727             }
728         }
729
730         void OnDataGridMouseDown(object sender, RoutedEventArgs e)
731         {
732             ICollectionView view = CollectionViewSource.GetDefaultView(this.dataGrid.ItemsSource);
733             if (null != this.dataGrid.SelectedItem && this.dataGrid.CurrentCell.IsValid && view.MoveCurrentTo(this.dataGrid.SelectedItem))
734             {
735                 int rowIndex = view.CurrentPosition;
736                 int columnIndex = this.dataGrid.Columns.IndexOf(this.dataGrid.CurrentCell.Column);
737                 var cell = DataGridHelper.GetCell(this.dataGrid, rowIndex, columnIndex);
738                 if (null != cell && cell.IsEditing)
739                 {
740                     this.CommitDataGrid();
741                     cell.Focus();
742                 }
743             }
744             else
745             {
746                 this.dataGrid.Focus();
747             }
748         }
749
750
751         void OnDynamicContentColumnLoaded(DataGridCell cell, ContentControl contentContainer)
752         {
753             //user marked at least one column in data grid with DynamicContent - now, we have to query client for
754             //cell template for given row's property
755             if (null != this.ResolveDynamicTemplateCallback)
756             {
757                 var resolveParams = new ResolveTemplateParams(cell, contentContainer.Content);
758                 if (this.ResolveDynamicTemplateCallback(resolveParams) && null != resolveParams.Template)
759                 {
760                     if (!resolveParams.IsDefaultTemplate)
761                     {
762                         var content = this.LoadDynamicContentDataCallback(cell, contentContainer.Content);
763                         var propertyEntry = new ModelPropertyEntry(content, null);
764                         contentContainer.Content = propertyEntry.PropertyValue;
765                         SetIsCustomEditor(cell, true);
766                     }
767                     else
768                     {
769                         contentContainer.Content = cell.DataContext;
770                         SetIsCustomEditor(cell, false);
771                     }
772                     contentContainer.ContentTemplate = resolveParams.Template;
773                 }
774             }
775             else
776             {
777                 System.Diagnostics.Debug.WriteLine("ResolveDynamicTemplateCallback not registered for column " + cell.Column.Header);
778             }
779         }
780
781         public void UpdateDynamicContentColumns(object entry)
782         {
783             ICollectionView view = CollectionViewSource.GetDefaultView(this.dataGrid.ItemsSource);
784             int rowIndex = -1;
785             //get index of given entry
786             if (view.MoveCurrentTo(entry))
787             {
788                 rowIndex = view.CurrentPosition;
789             }
790             if (-1 != rowIndex)
791             {
792                 //pickup all dynamic columns in this data grid
793                 var dynamicColumnsIndexes = this.dataGrid.Columns
794                     .OfType<DataGridTemplateColumn>()
795                     .Where(p => DataTemplate.Equals(p.CellEditingTemplate, DataGridHelper.DynamicCellContentTemplate) &&
796                                 DataTemplate.Equals(p.CellTemplate, DataGridHelper.DynamicCellContentTemplate))
797                     .Select<DataGridColumn, int>(p => this.dataGrid.Columns.IndexOf(p));
798
799                 //foreach dynamic column
800                 foreach (var columnIndex in dynamicColumnsIndexes)
801                 {
802                     //get the cell
803                     var cell = DataGridHelper.GetCell(this.dataGrid, rowIndex, columnIndex);
804
805                     //get the content presenter within it
806                     var dynamicContent = VisualTreeUtils.GetNamedChild<ContentControl>(cell, DataGridHelper.dynamicContentControlName, 5);
807
808                     //reload the template
809                     if (null != dynamicContent)
810                     {
811                         dynamicContent.ContentTemplate = null;
812                         this.OnDynamicContentColumnLoaded(cell, dynamicContent);
813                     }
814                 }
815             }
816
817         }
818
819         void ApplyCellStyle()
820         {
821             //create default cell style
822             Style baseStyle = this.dataGrid.CellStyle;
823             //respect any user's base styles
824             Style style = null == baseStyle ? new Style(typeof(DataGridCell)) : new Style(typeof(DataGridCell), baseStyle);
825
826             //event handler for preview mouse down - single click edit
827             style.Setters.Add(new EventSetter(DataGridCell.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(this.OnPreviewCellMouseLeftButtonDown)));
828             //width binding - prevent columns from expanding while typing long texts
829             style.Setters.Add(new Setter(DataGridCell.WidthProperty, new Binding("Column.ActualWidth")
830             {
831                 RelativeSource = new RelativeSource(RelativeSourceMode.Self),
832                 Mode = BindingMode.OneWay
833             }));
834             //automation id - for cell it is always column name
835             style.Setters.Add(new Setter(AutomationProperties.AutomationIdProperty, new Binding("Column.Header")
836             {
837                 RelativeSource = new RelativeSource(RelativeSourceMode.Self),
838                 Mode = BindingMode.OneWay
839             }));
840
841             //apply style
842             this.dataGrid.CellStyle = style;
843         }
844
845         void ApplyRowStyle()
846         {
847             //create default row style
848             Style baseStyle = this.dataGrid.RowStyle;
849             //respect any user's base styles
850             Style style = null == baseStyle ? new Style(typeof(DataGridRow)) : new Style(typeof(DataGridRow), baseStyle);
851
852             EventSetter keyDownSetter = new EventSetter
853             {
854                 Event = DataGridRow.KeyDownEvent,
855                 Handler = new KeyEventHandler(this.OnDataGridRowKeyDown)
856             };
857             style.Setters.Add(keyDownSetter);
858
859             //define a multibinding which displays a tooltip when cell validation fails (failure mean user's data was invalid and was not set in the target property)
860             //first - create a binding and add ErrorToTooltipConverter, pass reference to owning data grid helper
861             var multiBinding = new MultiBinding() { Converter = new ErrorToTooltipConverter(this) };
862             //now define bindings
863             //first - bind to actual object behind the row - only DesignObjectWrapper is supported
864             var objectWrapperBinding = new Binding() { Mode = BindingMode.OneTime };
865             //second - bind to a HasError property change notifications - this will trigger tooltip to appear
866             var hasErrorsBinding = new Binding() { Mode = BindingMode.OneWay, Path = new PropertyPath("HasErrors") };
867             //finally - bind to a row which contains the data - this will be used as tooltip placement target
868             var rowBinding = new Binding() { Mode = BindingMode.OneTime, RelativeSource = new RelativeSource(RelativeSourceMode.Self) };
869             multiBinding.Bindings.Add(objectWrapperBinding);
870             multiBinding.Bindings.Add(hasErrorsBinding);
871             multiBinding.Bindings.Add(rowBinding);
872
873             var errorTooltipTrigger = new DataTrigger()
874             {
875                 Binding = multiBinding,
876                 Value = true
877             };
878             //define a dummy setter - it will never be executed anyway, but it is required for the binding to work
879             errorTooltipTrigger.Setters.Add(new Setter(DataGridRow.TagProperty, null));
880             //add trigger to the collection
881             style.Triggers.Add(errorTooltipTrigger);
882             //apply style
883             this.dataGrid.RowStyle = style;
884         }
885
886         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
887             Justification = "Propagating exceptions might lead to VS crash.")]
888         [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
889             Justification = "Propagating exceptions might lead to VS crash.")]
890         static void OnDynamicCellContentLoaded(object sender, RoutedEventArgs e)
891         {
892             var container = (ContentControl)sender;
893             var dataGridCell = VisualTreeUtils.FindVisualAncestor<DataGridCell>(container);
894             var dataGrid = VisualTreeUtils.FindVisualAncestor<DataGrid>(dataGridCell);
895             if (dataGrid != null)
896             {
897                 var dataGridHelper = DataGridHelper.GetDGHelper(dataGrid);
898
899                 if (GetIsCustomEditor(dataGridCell) && (dataGridCell.IsEditing))
900                 {
901                     dataGridHelper.CommitDataGrid();
902                 }
903                 else
904                 {
905                     try
906                     {
907                         dataGridHelper.OnDynamicContentColumnLoaded(dataGridCell, container);
908                     }
909                     catch (Exception err)
910                     {
911                         container.Content = err.ToString();
912                         container.ContentTemplate = (DataTemplate)dataGrid.Resources["dynamicContentErrorTemplate"];
913                         System.Diagnostics.Debug.WriteLine(err.ToString());
914                     }
915                 }
916             }
917         }
918
919         public static DataTemplate DynamicCellContentTemplate
920         {
921             get
922             {
923                 if (null == dynamicCellContentTemplate)
924                 {
925                     DataTemplate template = new DataTemplate();
926
927                     FrameworkElementFactory contentControlFactory = new FrameworkElementFactory(typeof(ContentControl));
928                     contentControlFactory.SetValue(ContentControl.NameProperty, DataGridHelper.dynamicContentControlName);
929                     contentControlFactory.SetBinding(ContentControl.ContentProperty, new Binding());
930                     contentControlFactory.AddHandler(ContentControl.LoadedEvent, new RoutedEventHandler(DataGridHelper.OnDynamicCellContentLoaded));
931
932                     template.VisualTree = new FrameworkElementFactory(typeof(NoContextMenuGrid));
933                     template.VisualTree.AppendChild(contentControlFactory);
934                     template.Seal();
935                     dynamicCellContentTemplate = template;
936                 }
937                 return dynamicCellContentTemplate;
938             }
939         }
940
941         static DataGridHelper GetDGHelper(DependencyObject obj)
942         {
943             return (DataGridHelper)obj.GetValue(DGHelperProperty);
944         }
945
946         static void SetDGHelper(DependencyObject obj, DataGridHelper value)
947         {
948             obj.SetValue(DGHelperProperty, value);
949         }
950
951         static EditingControlBehavior GetControlBehavior(DependencyObject obj)
952         {
953             return (EditingControlBehavior)obj.GetValue(ControlBehaviorProperty);
954         }
955
956         static void SetControlBehavior(DependencyObject obj, EditingControlBehavior value)
957         {
958             obj.SetValue(ControlBehaviorProperty, value);
959         }
960
961         static bool GetNewRowLoaded(DependencyObject obj)
962         {
963             return (bool)obj.GetValue(NewRowLoadedProperty);
964         }
965
966         static void SetNewRowLoaded(DependencyObject obj, bool value)
967         {
968             obj.SetValue(NewRowLoadedProperty, value);
969         }
970
971         static bool GetIsCommitInProgress(DependencyObject obj)
972         {
973             return (bool)obj.GetValue(IsCommitInProgressProperty);
974         }
975
976         static void SetIsCommitInProgress(DependencyObject obj, bool value)
977         {
978             obj.SetValue(IsCommitInProgressProperty, value);
979         }
980
981         public static DataGridCell GetCell(DataGrid dataGrid, int row, int column)
982         {
983             DataGridRow rowContainer = GetRow(dataGrid, row);
984             if (rowContainer != null)
985             {
986                 DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);
987                 if (presenter != null)
988                 {
989                     // try to get the cell but it may possibly be virtualized
990                     DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
991                     if (cell == null)
992                     {
993                         // now try to bring into view and retreive the cell
994                         dataGrid.ScrollIntoView(rowContainer, dataGrid.Columns[column]);
995
996                         cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
997                     }
998
999                     return cell;
1000                 }
1001             }
1002
1003             return null;
1004         }
1005
1006         internal static ModelItem GetSingleSelectedObject(DataGrid dataGrid)
1007         {
1008             if (dataGrid == null || dataGrid.SelectedItems == null || dataGrid.SelectedItems.Count != 1)
1009             {
1010                 return null;
1011             }
1012
1013             if (dataGrid.SelectedItems[0] == CollectionView.NewItemPlaceholder)
1014             {
1015                 return null;
1016             }
1017
1018             DesignObjectWrapper designObjectWrapper = dataGrid.SelectedItems[0] as DesignObjectWrapper;
1019             if (designObjectWrapper != null)
1020             {
1021                 return designObjectWrapper.ReflectedObject;
1022             }
1023
1024             return null;
1025         }
1026
1027         /// <summary>
1028         /// Gets the DataGridRow based on the given index
1029         /// </summary>
1030         /// <param name="index">the index of the container to get</param>
1031         public static DataGridRow GetRow(DataGrid dataGrid, int index)
1032         {
1033             DataGridRow row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index);
1034             if (row == null)
1035             {
1036                 // may be virtualized, bring into view and try again
1037                 dataGrid.ScrollIntoView(dataGrid.Items[index]);
1038                 dataGrid.UpdateLayout();
1039
1040                 row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index);
1041             }
1042
1043             return row;
1044         }
1045
1046         public static T GetVisualChild<T>(DependencyObject parent) where T : Visual
1047         {
1048             T child = default(T);
1049
1050             int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
1051             for (int i = 0; i < numVisuals; i++)
1052             {
1053                 Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
1054                 child = v as T;
1055                 if (child == null)
1056                 {
1057                     child = GetVisualChild<T>(v);
1058                 }
1059                 if (child != null)
1060                 {
1061                     break;
1062                 }
1063             }
1064
1065             return child;
1066         }
1067
1068         public static void CommitPendingEdits(DataGrid dataGrid)
1069         {
1070             if (null == dataGrid)
1071             {
1072                 throw FxTrace.Exception.AsError(new ArgumentNullException("dataGrid"));
1073             }
1074
1075             if (!GetIsCommitInProgress(dataGrid))
1076             {
1077                 SetIsCommitInProgress(dataGrid, true);
1078
1079                 //try to commit edit
1080                 bool commitSucceeded = false;
1081                 DataGridHelper helper = DataGridHelper.GetDGHelper(dataGrid) as DataGridHelper;
1082                 bool orginalExplicitCommit = helper.ExplicitCommit;
1083                 helper.ExplicitCommit = true;
1084                 try
1085                 {
1086                     commitSucceeded = dataGrid.CommitEdit(DataGridEditingUnit.Row, true);
1087                 }
1088                 catch (InvalidOperationException)
1089                 {
1090                     // Ignore and cancel edit
1091                 }
1092                 finally
1093                 {
1094                     //if commit fails - undo change
1095                     if (!commitSucceeded)
1096                     {
1097                         dataGrid.CancelEdit();
1098                     }
1099                     helper.ExplicitCommit = orginalExplicitCommit;
1100                 }
1101
1102                 SetIsCommitInProgress(dataGrid, false);
1103             }
1104         }
1105
1106         public static void OnDeleteSelectedItems(DataGrid dataGrid)
1107         {
1108             if (dataGrid == null
1109                 || dataGrid.SelectedItems == null
1110                 || dataGrid.SelectedItems.Count == 0
1111                 || dataGrid.ItemsSource == null)
1112             {
1113                 return;
1114             }
1115
1116             if (!(dataGrid.ItemsSource is IList))
1117             {
1118                 // if the item source is not a list
1119                 // we can't delete items from it.
1120                 return;
1121             }
1122
1123             Int32 nextSelectedIndex = -1;
1124             ICollection<object> toBeDeleted = new HashSet<object>();
1125             foreach (object obj in dataGrid.SelectedItems)
1126             {
1127                 toBeDeleted.Add(obj);
1128             }
1129
1130             if (toBeDeleted.Count == 0)
1131             {
1132                 return;
1133             }
1134
1135             if (toBeDeleted.Count == 1)
1136             {
1137                 // if it is single selection,
1138                 // set selected index to be the current selected index.
1139                 // Set nextSelectedIndex only in single selection to keep the behavior
1140                 // consistent with the "delete" button on key board, where if you 
1141                 // select more than one items and push the "Delete" button on keyboard,
1142                 // nothing will be selected. 
1143                 nextSelectedIndex = dataGrid.Items.IndexOf(toBeDeleted.ElementAt(0));
1144             }
1145
1146             IList itemSource = (IList)dataGrid.ItemsSource;
1147             foreach (object obj in toBeDeleted)
1148             {
1149                 itemSource.Remove(obj);
1150             }
1151
1152             if (nextSelectedIndex >= 0
1153                 && nextSelectedIndex < dataGrid.Items.Count - 1)
1154             {
1155                 // The last row, whose index is (dataGrid.Items.Count - 1), is
1156                 // the "add new item" row.
1157                 dataGrid.SelectedIndex = nextSelectedIndex;
1158             }
1159         }
1160
1161         internal abstract class EditingControlBehavior
1162         {
1163             protected DesignerView DesignerView
1164             {
1165                 get;
1166                 private set;
1167             }
1168
1169             protected DataGrid OwnerDataGrid
1170             {
1171                 get;
1172                 set;
1173             }
1174
1175             public EditingControlBehavior(DataGrid dataGrid)
1176             {
1177                 this.OwnerDataGrid = dataGrid;
1178                 var helper = DataGridHelper.GetDGHelper(dataGrid);
1179                 if (null != helper && null != helper.Context)
1180                 {
1181                     this.DesignerView = helper.Context.Services.GetService<DesignerView>();
1182                 }
1183             }
1184
1185             public abstract bool HandleControlLoaded(Control control, DataGridCell cell, bool newRowLoaded);
1186             public abstract bool ControlUnloaded(Control control, DataGridCell cell);
1187
1188             protected void ToggleDesignerViewAutoCommit(bool shouldIgnore)
1189             {
1190                 if (null != this.DesignerView)
1191                 {
1192                     //enable/disable handling of lost keyboard focus events in designer view - 
1193                     //if shouldIgnore is true, designer view should ignore keyboard focus events thus, not forcing DataGrid to 
1194                     //commit any changes
1195                     this.DesignerView.ShouldIgnoreDataGridAutoCommit = shouldIgnore;
1196                 }
1197             }
1198         }
1199
1200         internal sealed class DefaultControlBehavior : EditingControlBehavior
1201         {
1202             public DefaultControlBehavior(DataGrid dataGrid)
1203                 : base(dataGrid)
1204             { }
1205
1206             public override bool HandleControlLoaded(Control control, DataGridCell cell, bool newRowLoaded)
1207             {
1208                 System.Diagnostics.Debug.WriteLine("DefaultControlBehavior.HandleControlLoaded");
1209                 control.Focus();
1210                 return true;
1211             }
1212
1213             public override bool ControlUnloaded(Control control, DataGridCell cell)
1214             {
1215                 System.Diagnostics.Debug.WriteLine("DefaultControlBehavior.ControlUnloaded");
1216                 return true;
1217             }
1218         }
1219
1220         internal sealed class TextBoxBehavior : EditingControlBehavior
1221         {
1222             public TextBoxBehavior(DataGrid dataGrid)
1223                 : base(dataGrid)
1224             { }
1225
1226             public override bool HandleControlLoaded(Control control, DataGridCell cell, bool newRowLoaded)
1227             {
1228                 System.Diagnostics.Debug.WriteLine("TextBoxBehavior.HandleControlLoaded");
1229                 bool handled = false;
1230                 TextBox tb = control as TextBox;
1231                 if (null != tb)
1232                 {
1233                     if (newRowLoaded)
1234                     {
1235                         tb.SelectAll();
1236                     }
1237                     else
1238                     {
1239                         tb.CaretIndex = tb.Text.Length;
1240                     }
1241                     tb.Focus();
1242                     handled = true;
1243                 }
1244                 return handled;
1245             }
1246
1247             public override bool ControlUnloaded(Control control, DataGridCell cell)
1248             {
1249                 System.Diagnostics.Debug.WriteLine("TextBoxBehavior.ControlUnloaded");
1250                 return true;
1251             }
1252         }
1253
1254         internal sealed class VBIdentifierDesignerBehavior : EditingControlBehavior
1255         {
1256             public VBIdentifierDesignerBehavior(DataGrid dataGrid)
1257                 : base(dataGrid)
1258             { }
1259
1260             public override bool HandleControlLoaded(Control control, DataGridCell cell, bool newRowLoaded)
1261             {
1262                 System.Diagnostics.Debug.WriteLine("VBIdentifierDesignerBehavior.HandleControlLoaded");
1263                 bool handled = false;
1264                 VBIdentifierDesigner identifierDesigner = control as VBIdentifierDesigner;
1265                 if ((null != identifierDesigner) && (!identifierDesigner.IsReadOnly))
1266                 {
1267                     if (newRowLoaded)
1268                     {
1269                         DataGridHelper.SetNewRowLoaded(identifierDesigner, true);
1270                     }
1271                     else
1272                     {
1273                         DataGridHelper.SetNewRowLoaded(identifierDesigner, false);
1274                     }
1275
1276                     identifierDesigner.TextBoxPropertyChanged += this.OnIdentifierDesignerTextBoxChanged;
1277                     identifierDesigner.Focus();
1278                     handled = true;
1279                 }
1280                 return handled;
1281             }
1282
1283             void OnIdentifierDesignerTextBoxChanged(object sender, PropertyChangedEventArgs e)
1284             {
1285                 Fx.Assert(e.PropertyName == "IdentifierTextBox", "VBIdentifierDesignerBehavior.TextBoxPropertyChanged event should only be raised when IdentifierTextBox property is changed.");
1286                 VBIdentifierDesigner identifierDesigner = sender as VBIdentifierDesigner;
1287                 TextBox textBox = identifierDesigner.IdentifierTextBox;
1288                 if (textBox != null)
1289                 {
1290                     if (DataGridHelper.GetNewRowLoaded(identifierDesigner))
1291                     {
1292                         textBox.SelectAll();
1293                         DataGridHelper.SetNewRowLoaded(identifierDesigner, false);
1294                     }
1295                     else
1296                     {
1297                         textBox.CaretIndex = textBox.Text.Length;
1298                     }
1299                     textBox.Focus();
1300                 }
1301             }
1302
1303             public override bool ControlUnloaded(Control control, DataGridCell cell)
1304             {
1305                 System.Diagnostics.Debug.WriteLine("VBIdentifierDesignerBehavior.ControlUnloaded");
1306                 VBIdentifierDesigner identifierDesigner = control as VBIdentifierDesigner;
1307                 if (identifierDesigner != null)
1308                 {
1309                     identifierDesigner.TextBoxPropertyChanged -= this.OnIdentifierDesignerTextBoxChanged;
1310                 }
1311                 return true;
1312             }
1313         }
1314
1315         internal sealed class TypePresenterBehavior : EditingControlBehavior
1316         {
1317             DataGridCell cell;
1318             TypePresenter typePresenter;
1319             bool isTypeBrowserOpen = false;
1320             bool isRegisteredForEvents = false;
1321
1322             public TypePresenterBehavior(DataGrid dataGrid)
1323                 : base(dataGrid)
1324             {
1325                 DataGridHelper helper = DataGridHelper.GetDGHelper(dataGrid) as DataGridHelper;
1326                 helper.DataGridCellEditEnding += OnCellEditEnding;
1327             }
1328
1329             public override bool HandleControlLoaded(Control control, DataGridCell cell, bool newRowLoaded)
1330             {
1331                 System.Diagnostics.Debug.WriteLine("TypePresenterBehavior.HandleControlLoaded");
1332                 bool handled = false;
1333                 this.cell = cell;
1334                 this.typePresenter = control as TypePresenter;
1335                 if (null != this.typePresenter)
1336                 {
1337                     this.isRegisteredForEvents = true;
1338                     this.typePresenter.TypeBrowserOpened += OnTypeBrowserOpened;
1339                     this.typePresenter.TypeBrowserClosed += OnTypeBrowserClosed;
1340
1341                     handled = this.typePresenter.typeComboBox.Focus();
1342                 }
1343                 return handled;
1344             }
1345
1346             public override bool ControlUnloaded(Control control, DataGridCell cell)
1347             {
1348                 System.Diagnostics.Debug.WriteLine("TypePresenterBehavior.ControlUnloaded");
1349                 this.cell = null;
1350                 if (this.isRegisteredForEvents && null != this.typePresenter)
1351                 {
1352                     this.typePresenter.TypeBrowserOpened -= OnTypeBrowserOpened;
1353                     this.typePresenter.TypeBrowserClosed -= OnTypeBrowserClosed;
1354                     this.typePresenter = null;
1355                     this.isRegisteredForEvents = false;
1356                     DataGridHelper helper = DataGridHelper.GetDGHelper(this.OwnerDataGrid) as DataGridHelper;
1357                     helper.DataGridCellEditEnding -= this.OnCellEditEnding;
1358                 }
1359                 return true;
1360             }
1361
1362             void OnCellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
1363             {
1364                 if (null != this.cell)
1365                 {
1366                     e.Cancel = this.isTypeBrowserOpen;
1367                 }
1368             }
1369
1370             void OnTypeBrowserOpened(object sender, RoutedEventArgs e)
1371             {
1372                 base.ToggleDesignerViewAutoCommit(true);
1373                 this.isTypeBrowserOpen = true;
1374             }
1375
1376             void OnTypeBrowserClosed(object sender, RoutedEventArgs e)
1377             {
1378                 base.ToggleDesignerViewAutoCommit(false);
1379                 this.isTypeBrowserOpen = false;
1380             }
1381         }
1382
1383         internal sealed class ExpressionTextBoxBehavior : EditingControlBehavior
1384         {
1385             bool isExpressionEditInProgress;
1386             DataGridCell cell;
1387
1388             public ExpressionTextBoxBehavior(DataGrid dataGrid)
1389                 : base(dataGrid)
1390             {
1391                 DataGridHelper helper = DataGridHelper.GetDGHelper(dataGrid) as DataGridHelper;
1392                 helper.DataGridCellEditEnding += OnCellEditEnding;
1393             }
1394
1395             public override bool HandleControlLoaded(Control control, DataGridCell cell, bool newRowLoaded)
1396             {
1397                 System.Diagnostics.Debug.WriteLine("ExpressionTextBoxBehavior.HandleControlLoaded");
1398                 bool handled = false;
1399                 this.cell = cell;
1400                 ExpressionTextBox etb = control as ExpressionTextBox;
1401                 if (null != etb)
1402                 {
1403                     etb.Tag = cell;
1404                     //register for logical lost focus events 
1405                     etb.EditorLostLogicalFocus += OnExpressionEditComplete;
1406
1407                     if (!etb.IsReadOnly)
1408                     {
1409                         //start editing expression
1410                         etb.BeginEdit();
1411                         //mark expression edit is in progress, so all CellEditEnding calls will be ignored by datagrid
1412                         this.isExpressionEditInProgress = true;
1413                         //disable forced keyboard focus lost events - intelisense window will trigger lost keyboard event, 
1414                         //which eventualy will lead to commit edit
1415                         base.ToggleDesignerViewAutoCommit(true);
1416                     }
1417                     handled = true;
1418                 }
1419                 return handled;
1420             }
1421
1422             public override bool ControlUnloaded(Control control, DataGridCell cell)
1423             {
1424                 System.Diagnostics.Debug.WriteLine("ExpressionTextBoxBehavior.ControlUnloaded");
1425                 ExpressionTextBox etb = control as ExpressionTextBox;
1426                 if (null != etb)
1427                 {
1428                     //control is unloaded - unregister from the event
1429                     etb.EditorLostLogicalFocus -= OnExpressionEditComplete;
1430                     //if it happens that complete row is beeing unloaded, it is possible that expression edit was still in progress
1431                     if (this.isExpressionEditInProgress)
1432                     {
1433                         //force expression update before unload is complete
1434                         this.OnExpressionEditComplete(etb, null);
1435                     }
1436                 }
1437                 DataGridHelper helper = DataGridHelper.GetDGHelper(this.OwnerDataGrid) as DataGridHelper;
1438                 helper.DataGridCellEditEnding -= OnCellEditEnding;
1439                 this.cell = null;
1440                 return true;
1441             }
1442
1443             void OnExpressionEditComplete(object sender, RoutedEventArgs e)
1444             {
1445                 if (this.isExpressionEditInProgress)
1446                 {
1447                     ExpressionTextBox etb = (ExpressionTextBox)sender;
1448                     //commit the expression value
1449                     ((RoutedCommand)DesignerView.CommitCommand).Execute(null, etb);
1450                     //allow data grid to consume cell editing events
1451                     this.isExpressionEditInProgress = false;
1452                     this.OwnerDataGrid.CommitEdit();
1453                     //restore keyboard focus handling for designer view
1454                     base.ToggleDesignerViewAutoCommit(false);
1455                 }
1456             }
1457
1458             void OnCellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
1459             {
1460                 DataGridHelper helper = DataGridHelper.GetDGHelper(this.OwnerDataGrid) as DataGridHelper;
1461                 if (this.isExpressionEditInProgress && (helper != null) && !helper.ExplicitCommit)
1462                 {
1463                     e.Cancel = true;
1464                 }
1465                 else if (this.isExpressionEditInProgress)
1466                 {
1467                     ExpressionTextBox etb = VisualTreeUtils.GetTemplateChild<ExpressionTextBox>(e.EditingElement);
1468                     this.OnExpressionEditComplete(etb, null);
1469                 }
1470             }
1471         }
1472     }
1473
1474     sealed class ResolveTemplateParams
1475     {
1476         internal ResolveTemplateParams(DataGridCell cell, object instance)
1477         {
1478             this.Cell = cell;
1479             this.Instance = instance;
1480             this.IsDefaultTemplate = true;
1481         }
1482
1483         public DataGridCell Cell { get; private set; }
1484         public object Instance { get; private set; }
1485         public bool IsDefaultTemplate { get; set; }
1486         public DataTemplate Template { get; set; }
1487     }
1488
1489     sealed class ErrorToTooltipConverter : IMultiValueConverter
1490     {
1491         DataGridHelper owner;
1492         DataTemplate toolTipTemplate;
1493
1494         public ErrorToTooltipConverter(DataGridHelper owner)
1495         {
1496             this.owner = owner;
1497             this.toolTipTemplate = (DataTemplate)this.owner.FindResource("errorToolTipTemplate");
1498         }
1499
1500         public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
1501         {
1502             var entry = values.OfType<DesignObjectWrapper>().FirstOrDefault();
1503             var row = values.OfType<DataGridRow>().FirstOrDefault();
1504             if (this.owner.IsEditInProgress && null != entry && entry.HasErrors && null != row)
1505             {
1506                 var invalidProperties = new List<string>();
1507                 if (this.owner.ShowValidationErrorAsToolTip)
1508                 {
1509                     var errorTip = new ToolTip()
1510                     {
1511                         PlacementTarget = row,
1512                         Placement = PlacementMode.Bottom,
1513                         ContentTemplate = this.toolTipTemplate,
1514                         Content = entry.GetValidationErrors(invalidProperties),
1515                         Effect = new DropShadowEffect() { ShadowDepth = 1 },
1516                     };
1517                     AutomationProperties.SetAutomationId(errorTip, "errorToolTip");
1518
1519                     row.Dispatcher.BeginInvoke(new Action<ToolTip, DataGridRow>((tip, r) =>
1520                     {
1521                         tip.IsOpen = true;
1522                         var dt = new DispatcherTimer(TimeSpan.FromSeconds(6), DispatcherPriority.ApplicationIdle, (sender, e) => { tip.IsOpen = false; }, r.Dispatcher);
1523                     }), DispatcherPriority.ApplicationIdle, errorTip, row);
1524                 }
1525                 else
1526                 {
1527                     row.Dispatcher.BeginInvoke(new Action<string>((error) =>
1528                         {
1529                             //get currently focused element
1530                             var currentFocus = (UIElement)Keyboard.FocusedElement;
1531                             if (null != currentFocus)
1532                             {
1533                                 //if focus was within datagrid's cell, after loosing focus most likely the editing control would be gone, so try to preserve 
1534                                 //reference to the cell itself
1535                                 currentFocus = VisualTreeUtils.FindVisualAncestor<DataGridCell>(currentFocus) ?? currentFocus;
1536                             }
1537                             //show error message (this will result in KeyboardFocus changed
1538                             ErrorReporting.ShowErrorMessage(error);
1539                             //restore keyboard focus to stored element, but only if it is somewhere within DesignerView (i don't want to mess with focus in other windows)
1540                             if (null != currentFocus && null != VisualTreeUtils.FindVisualAncestor<DesignerView>(currentFocus))
1541                             {
1542                                 Keyboard.Focus(currentFocus);
1543                             }
1544                         }), DispatcherPriority.ApplicationIdle, entry.GetValidationErrors(invalidProperties));
1545                 }
1546                 //clear the validation error messages - once the error is raised and displayed, i don't need it anymore in the collection
1547                 entry.ClearValidationErrors(invalidProperties);
1548             }
1549             //in case of property grid edit, the errors would be displayed by model item infrastructure, 
1550             //so just delegate the call to clear errors collection
1551             if (!this.owner.IsEditInProgress && null != entry && entry.HasErrors)
1552             {
1553                 Dispatcher.CurrentDispatcher.BeginInvoke(new Action<DesignObjectWrapper>((instance) =>
1554                     {
1555                         instance.ClearValidationErrors();
1556                     }), DispatcherPriority.ApplicationIdle, entry);
1557             }
1558             return Binding.DoNothing;
1559         }
1560
1561         public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
1562         {
1563             throw FxTrace.Exception.AsError(new NotSupportedException());
1564         }
1565     }
1566 }