更新內(nèi)容:
1. Tab/Enter跳轉(zhuǎn)到下一列;
2. 最后一行最后一列,Tab/Enter自動增加新行;
3. 增加新行后,自動跳轉(zhuǎn)到新增行的第一列;
4. 刪除行后,自動選中上一行;
5. Up/Down/Left/Right自動編輯;
實現(xiàn)方式:
1. Tab/Enter跳轉(zhuǎn)到下一列
從google搜到的一篇文章《Datagrid cell navigation with enter key》,文中最開始提到在DataGrid的KeyDown事件中進行處理。但是DataGrid里面做了一些封裝,我們在自己注冊(通過+=操作符來登記、或者XAML中定義)的KeyDown事件中,截獲不到Enter/Tab等鍵。所以最后原作者有提出一個思路:繼承DataGrid,重新OnKeyDown方法。
但我之前的思路,是通過擴展方法來擴展DataGrid,再來重寫就有點兒郁悶了。于是找出Reflector,翻了下DataGrid的實現(xiàn)(reflector中copy出來的):
1: // happyhippy.cnblogs.com
2: // 構(gòu)造函數(shù)
3: public DataGrid()
4: {5: //省略
6: base.KeyDown += new KeyEventHandler(this.DataGrid_KeyDown);
7: base.KeyUp += new KeyEventHandler(this.DataGrid_KeyUp);
8: //省略
9: } 10: 11: private void DataGrid_KeyDown(object sender, KeyEventArgs e)
12: {13: if (!e.Handled)
14: {15: e.Handled = this.ProcessDataGridKey(e);
16: } 17: } 18: 19: private bool ProcessDataGridKey(KeyEventArgs e)
20: {21: bool flag = false;
22: switch (e.Key)
23: {24: case Key.Tab:
25: return this.ProcessTabKey(e);
26: 27: case Key.Enter:
28: flag = this.ProcessEnterKey();
29: break;
30: 31: case Key.Escape:
32: return this.ProcessEscapeKey();
33: 34: case Key.PageUp:
35: flag = this.ProcessPriorKey();
36: break;
37: 38: case Key.PageDown:
39: flag = this.ProcessNextKey();
40: break;
41: 42: case Key.End:
43: flag = this.ProcessEndKey();
44: break;
45: 46: case Key.Home:
47: flag = this.ProcessHomeKey();
48: break;
49: 50: case Key.Left:
51: flag = (base.FlowDirection == FlowDirection.LeftToRight) ? this.ProcessLeftKey() : this.ProcessRightKey();
52: break;
53: 54: case Key.Up:
55: flag = this.ProcessUpKey();
56: break;
57: 58: case Key.Right:
59: flag = (base.FlowDirection == FlowDirection.LeftToRight) ? this.ProcessRightKey() : this.ProcessLeftKey();
60: break;
61: 62: case Key.Down:
63: flag = this.ProcessDownKey();
64: break;
65: 66: case Key.Insert:
67: return this.ProcessCopyKey();
68: 69: case Key.A:
70: return this.ProcessAKey();
71: 72: case Key.C:
73: return this.ProcessCopyKey();
74: 75: case Key.F2:
76: return this.ProcessF2Key(e);
77: }78: if (flag && base.IsTabStop)
79: {80: base.Focus();
81: }82: return flag;
83: }我們可以發(fā)現(xiàn),KeyDown中已經(jīng)定義一部分按鍵的處理,并更新了KeyEventArgs.Handled屬性。MSDN對該屬性的解釋是:
|
將事件標記為已處理將會限制路由事件在事件路由過程中對于偵聽器的可見性。事件將仍然進行路由過程的其余部分,但只會在響應(yīng)中調(diào)用在 AddHandler(RoutedEvent, Delegate, Boolean) 方法調(diào)用中明確添加的其 HandledEventsToo 為 true 的處理程序。 將不會調(diào)用實例偵聽器上的默認處理程序(比如用可擴展應(yīng)用程序標記語言 (XAML) 表示的那些處理程序)。處理標記為已處理的事件這種情形并不常見。 如果您是定義您自己的事件的控件作者,則您在類級別所做的有關(guān)事件處理的決策將影響您的控件的用戶,以及派生控件的任何用戶,并可能會影響您的控件所包含或包含您的控件的其他元素。有關(guān)更多信息,請參見 將路由事件標記為“已處理”和“類處理”。 在極少數(shù)情況下,適合處理其中 Handled 標記為 true 的事件,并通過將 Handled 更改為 false 來修改事件參數(shù)。 在控件輸入事件的某些區(qū)域(比如 KeyDown 與 TextInput 鍵處理)中,低級別和高級別的輸入事件會爭用處理,并且每個事件都會嘗試使用不同的路由策略,因此,可能必須要這樣做。 |
也就是說,在將KeyEventArgs.Handled,我們通過+=來自己注冊的處理程序,就無法執(zhí)行了。一種替代思路就是在不要用+=來注冊處理程序,而是調(diào)用AddHandler(RoutedEvent, Delegate, Boolean) 方法來注冊處理程序:
1: //之前的處理方式,將監(jiān)聽不到已經(jīng)處理過的Tab/Enter等鍵
2: //dataGrid.KeyDown += KeyDownHandler<T>; //注冊按鍵事件
3: //只能通過下列方式來注冊
4: dataGrid.AddHandler(DataGrid.KeyDownEvent, new KeyEventHandler(KeyDownHandler<T>), true);
但在使用的時候發(fā)現(xiàn),如果當(dāng)前Cell(TextBlock)處于編輯狀態(tài),要按兩次Tab/Enter才能跳轉(zhuǎn)到下一列:第一次提交更改,第二次才執(zhí)行跳轉(zhuǎn)操作。
回過頭來看,上面的DataGrid的部分源碼,跟進去看了下KeyUp事件的處理,發(fā)現(xiàn)KeyUp并沒有去更改KeyEventArgs.Handled屬性。因此,我們可以考慮在DataGrid上注冊自定義KeyUp事件,來做一些額外處理:
1: dataGrid.KeyUp += KeyUpHandler<T>; 2: 3: private static void KeyUpHandler<T>(object sender, KeyEventArgs e) where T : class, new()
4: {5: DataGrid dataGrid = sender as DataGrid;
6: switch (e.Key)
7: {8: case Key.Tab:
9: goto case Key.Enter;
10: case Key.Enter:
11: if (dataGrid.CurrentColumn != null)
12: {13: if (dataGrid.IsLastRow() & dataGrid.IsLastCell()) // Last Row And Last Cell (End of the grid)
14: { 15: dataGrid.AddEntity<T>(); 16: }17: else if (dataGrid.IsLastCell()) // Last Cell
18: { 19: dataGrid.SelectedIndex = dataGrid.SelectedIndex + 1; 20: dataGrid.NextCellToEdit(); 21: }22: else // Normal navigation
23: { 24: dataGrid.NextCellToEdit(); 25: } 26: }27: break;
28: default:
29: break;
30: }
2. 最后一行最后一列,Tab/Enter自動增加新行
能捕捉到Tab/Enter后,處理就比較簡單了;只需判斷時候在最后一行、最后一列,如果是,則調(diào)用上一篇文章中的增加新行方法:
1: if (dataGrid.IsLastRow() & dataGrid.IsLastCell())
2: { 3: dataGrid.AddEntity<T>(); 4: }
3. 增加新行后,自動跳轉(zhuǎn)到新增行的第一列
可以嘗試在后臺代碼中,將DataGrid聚焦在新增行的第一列上:
1: dataGrid.SelectedItem = entity; 2: dataGrid.CurrentColumn = dataGrid.Columns[0]; 3: dataGrid.NextCellToEdit();但是在實際使用過程中,可能會遇到如下異常:
這是因為,新增加的行,可能(有時能成功,有時可能失敗)還沒有來得及在UI顯示出來,如果在這時設(shè)置SelectedItem/CurrentColumn,并調(diào)用ScrollIntoView,就會出現(xiàn)上面的異常。一種代替思路,就是用一個計時器來進行處理,如果執(zhí)行失敗,則下次接著執(zhí)行,直到成功為止:
1: public static void ScrolToFirstColumn(this DataGrid dataGrid, object entity)
2: {3: DispatcherTimer timer = new DispatcherTimer();
4: timer.Interval = TimeSpan.FromTicks(100);5: object sucess = false;
6: timer.Tick += (o, e) => 7: {8: if (!(bool)sucess)
9: { 10: dataGrid.SelectedItem = entity; 11: dataGrid.CurrentColumn = dataGrid.Columns[0]; 12: dataGrid.NextCellToEdit();13: sucess = true;
14: timer.Stop(); //執(zhí)行完畢后,就不再執(zhí)行。
15: } 16: }; 17: timer.Start(); 18: }
4. 刪除行后,自動選中上一行
默認情況下,行被刪除行后,DataGrid沒有選中任何行。如果想連續(xù)刪除、或者用Up/Down/Left/Right跳轉(zhuǎn)的話,就需要每次用鼠標點一下,著實不方便。于是提供了此功能:
1: public static void DeleteCurrentRow(this DataGrid dataGrid)
2: {3: object entity = dataGrid.SelectedItem;
4: IList dataSource = dataGrid.ItemsSource as IList;
5: if (entity == null || dataSource == null || dataSource.Count == 0)
6: {7: return;
8: } 9: 10: int selectedIndex = dataGrid.SelectedIndex; //刪除前,記錄當(dāng)前行的位置
11: if (ExtenderInfo[dataGrid].Deleteable)
12: { 13: dataSource.Remove(entity); 14: dataGrid.SelectedIndex = selectedIndex - 1;//刪除后,重置當(dāng)前行 15: } 16: dataGrid.BeginEdit(); 17: ExtenderInfo[dataGrid].RaiseRowChangedEvent(dataGrid,18: new EntityChangedEventArgs() { ChangedEntities = new List<object> { entity }, Type = "Delete" });
19: }
5. Up/Down/Left/Right自動編輯
上一篇文章中,通過在DataGrid的KeyDown事件中,見按鍵記錄到DataGrid.Tag中,然后進入編輯狀態(tài)時再賦值給單元格中的文本框(TextBox),來實現(xiàn)即時編輯功能。但在使用的時候,發(fā)現(xiàn)如果使用中文輸入法、或者列使用的是自定義模板列(不是TextBox),就會遇到問題:
如果是中文輸入法+默認的TextBox:DataGrid會將第一個字符賦給TextBox,這樣就多出一個字符,每次得多按一次BackSpace鍵。
如果是使用自定義模板列,而不是TextBox,就會丟失第一次字符按鍵。
一種替代的思路,就是在捕捉Up/Down/Left/Right后,讓DataGrid立即進入編輯狀態(tài)。如上面第1點所述,還是要在KeyUp事件中進行處理。
![clip_image001[4] clip_image001[4]](https://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101122032386042.png)

浙公網(wǎng)安備 33010602011771號