/
DataGridTextBox.cs
262 lines (238 loc) · 11.4 KB
/
DataGridTextBox.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace System.Windows.Forms{
using System.Runtime.Remoting;
using System;
using System.Security.Permissions;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using Microsoft.Win32;
using System.Runtime.InteropServices;
/// <include file='doc\DataGridTextBox.uex' path='docs/doc[@for="DataGridTextBox"]/*' />
/// <devdoc>
/// <para>Represents a <see cref='System.Windows.Forms.TextBox'/> control that is hosted in a
/// <see cref='System.Windows.Forms.DataGridTextBoxColumn'/> .</para>
/// </devdoc>
[
ComVisible(true),
ClassInterface(ClassInterfaceType.AutoDispatch),
ToolboxItem(false),
DesignTimeVisible(false),
DefaultProperty("GridEditName")
]
public class DataGridTextBox : TextBox {
private bool isInEditOrNavigateMode = true;
// only needed to signal the dataGrid that an edit
// takes place
private DataGrid dataGrid;
/// <include file='doc\DataGridTextBox.uex' path='docs/doc[@for="DataGridTextBox.DataGridTextBox"]/*' />
public DataGridTextBox() : base () {
TabStop = false;
}
/// <include file='doc\DataGridTextBox.uex' path='docs/doc[@for="DataGridTextBox.SetDataGrid"]/*' />
/// <devdoc>
/// <para>Sets the <see cref='System.Windows.Forms.DataGrid'/> to which this <see cref='System.Windows.Forms.TextBox'/> control belongs.</para>
/// </devdoc>
public void SetDataGrid(DataGrid parentGrid)
{
dataGrid = parentGrid;
}
/// <include file='doc\DataGridTextBox.uex' path='docs/doc[@for="DataGridTextBox.WndProc"]/*' />
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message m) {
// but what if we get a CtrlV?
// what about deleting from the menu?
if (m.Msg == NativeMethods.WM_PASTE || m.Msg == NativeMethods.WM_CUT || m.Msg == NativeMethods.WM_CLEAR) {
IsInEditOrNavigateMode = false;
dataGrid.ColumnStartedEditing(Bounds);
}
base.WndProc(ref m);
}
/// <include file='doc\DataGridTextBox.uex' path='docs/doc[@for="DataGridTextBox.OnMouseWheel"]/*' />
protected override void OnMouseWheel(MouseEventArgs e) {
dataGrid.TextBoxOnMouseWheel(e);
}
/// <include file='doc\DataGridTextBox.uex' path='docs/doc[@for="DataGridTextBox.OnKeyPress"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected override void OnKeyPress(KeyPressEventArgs e)
{
base.OnKeyPress(e);
// Shift-Space should not cause the grid to
// be put in edit mode
if (e.KeyChar == ' ' && (Control.ModifierKeys & Keys.Shift) == Keys.Shift)
return;
// if the edit box is in ReadOnly mode, then do not tell the DataGrid about the
// edit
if (this.ReadOnly)
return;
// Ctrl-* should not put the grid in edit mode
if ((Control.ModifierKeys & Keys.Control) == Keys.Control && ((Control.ModifierKeys & Keys.Alt) == 0))
return;
IsInEditOrNavigateMode = false;
// let the DataGrid know about the edit
dataGrid.ColumnStartedEditing(Bounds);
}
/// <include file='doc\DataGridTextBox.uex' path='docs/doc[@for="DataGridTextBox.ProcessKeyMessage"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
protected internal override bool ProcessKeyMessage(ref Message m)
{
Keys key = (Keys)unchecked((int)(long)m.WParam);
Keys modifierKeys = ModifierKeys;
if ((key | modifierKeys) == Keys.Enter || (key | modifierKeys) == Keys.Escape || ((key | modifierKeys) == (Keys.Enter | Keys.Control))
)
{
// enter and escape keys are sent directly to the DataGrid
// for those keys, eat the WM_CHAR part of the KeyMessage
//
if (m.Msg == NativeMethods.WM_CHAR)
return true;
return ProcessKeyPreview(ref m);
}
if (m.Msg == NativeMethods.WM_CHAR) {
if (key == Keys.LineFeed) // eat the LineFeed we get when the user presses Ctrl-Enter in a gridTextBox
return true;
return ProcessKeyEventArgs(ref m);
}
// now the edit control will be always on top of the grid
// we only want to process the WM_KEYUP message ( the same way the grid was doing when the grid was getting all
// the keys )
if (m.Msg == NativeMethods.WM_KEYUP)
return true;
Keys keyData = key & Keys.KeyCode;
switch (keyData)
{
case Keys.Right:
// here is the deal with Keys.Right:
// if the end of the selection is at the end of the string
// send this character to the dataGrid
// else, process the KeyEvent
//
if (SelectionStart + SelectionLength == Text.Length)
return ProcessKeyPreview(ref m);
return ProcessKeyEventArgs(ref m);
case Keys.Left:
// if the end of the selection is at the begining of the string
// or if the entire text is selected and we did not start editing
// send this character to the dataGrid
// else, process the KeyEvent
//
if (SelectionStart + SelectionLength == 0 ||
(this.IsInEditOrNavigateMode && this.SelectionLength == Text.Length))
return ProcessKeyPreview(ref m);
return ProcessKeyEventArgs(ref m);
case Keys.Down:
// if the end of the selection is on the last line of the text then
// send this character to the dataGrid
// else, process the KeyEvent
//
int end = SelectionStart + SelectionLength;
if (Text.IndexOf("\r\n", end) == -1)
return ProcessKeyPreview(ref m);
return ProcessKeyEventArgs(ref m);
case Keys.Up:
// if the end of the selection is on the first line of the text then
// send this character to the dataGrid
// else, process the KeyEvent
//
if ( Text.IndexOf("\r\n") < 0 || SelectionStart + SelectionLength < Text.IndexOf("\r\n"))
return ProcessKeyPreview(ref m);
return ProcessKeyEventArgs(ref m);
case Keys.Home:
case Keys.End:
if (SelectionLength == Text.Length)
return ProcessKeyPreview(ref m);
else
return ProcessKeyEventArgs(ref m);
case Keys.Prior:
case Keys.Next:
case Keys.Oemplus:
case Keys.Add:
case Keys.OemMinus:
case Keys.Subtract:
if (IsInEditOrNavigateMode)
{
// this will ultimately call parent's ProcessKeyPreview
// in our case, DataGrid's ProcessKeyPreview
return ProcessKeyPreview(ref m);
}
else
{
return ProcessKeyEventArgs(ref m);
}
case Keys.Space:
if (IsInEditOrNavigateMode && (Control.ModifierKeys & Keys.Shift) == Keys.Shift)
{
// when we get a SHIFT-SPACEBAR message, disregard the WM_CHAR part of the message
if (m.Msg == NativeMethods.WM_CHAR) return true;
// if the user pressed the SHIFT key at the same time with
// the space key, send the key message to the DataGrid
return ProcessKeyPreview(ref m);
}
return ProcessKeyEventArgs(ref m);
case Keys.A:
if (IsInEditOrNavigateMode && (Control.ModifierKeys & Keys.Control) == Keys.Control)
{
// when we get a Control-A message, disregard the WM_CHAR part of the message
if (m.Msg == NativeMethods.WM_CHAR) return true;
// if the user pressed the Control key at the same time with
// the space key, send the key message to the DataGrid
return ProcessKeyPreview(ref m);
}
return ProcessKeyEventArgs(ref m);
case Keys.F2:
IsInEditOrNavigateMode = false;
// do not select all the text, but
// position the caret at the end of the text
SelectionStart = Text.Length;
return true;
case Keys.Delete:
if (IsInEditOrNavigateMode) {
// pass the delete to the parent, in our case, the DataGrid
// if the dataGrid used the key, then we aren't gonne
// use it anymore, else we are
if (ProcessKeyPreview(ref m))
return true;
else {
// the edit control will use the
// delete key: we are in Edit mode now:
IsInEditOrNavigateMode = false;
dataGrid.ColumnStartedEditing(Bounds);
return ProcessKeyEventArgs(ref m);
}
}
else
return ProcessKeyEventArgs(ref m);
case Keys.Tab:
// the TextBox gets the Control-Tab messages,
// not the parent
if ((ModifierKeys & Keys.Control) == Keys.Control)
return ProcessKeyPreview(ref m);
else
return ProcessKeyEventArgs(ref m);
default:
return ProcessKeyEventArgs(ref m);
}
}
/// <include file='doc\DataGridTextBox.uex' path='docs/doc[@for="DataGridTextBox.IsInEditOrNavigateMode"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public bool IsInEditOrNavigateMode {
get {
return isInEditOrNavigateMode;
}
set {
isInEditOrNavigateMode = value;
if (value)
SelectAll();
}
}
}
}