|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Drawing;
namespace System.Windows.Forms;
public partial class DataGridViewTextBoxEditingControl : TextBox, IDataGridViewEditingControl
{
private const DataGridViewContentAlignment AnyTop = DataGridViewContentAlignment.TopLeft | DataGridViewContentAlignment.TopCenter | DataGridViewContentAlignment.TopRight;
private const DataGridViewContentAlignment AnyRight = DataGridViewContentAlignment.TopRight | DataGridViewContentAlignment.MiddleRight | DataGridViewContentAlignment.BottomRight;
private const DataGridViewContentAlignment AnyCenter = DataGridViewContentAlignment.TopCenter | DataGridViewContentAlignment.MiddleCenter | DataGridViewContentAlignment.BottomCenter;
private DataGridView? _dataGridView;
private bool _valueChanged;
private bool _repositionOnValueChange;
private int _rowIndex;
public DataGridViewTextBoxEditingControl() : base()
{
TabStop = false;
}
protected override AccessibleObject CreateAccessibilityInstance()
{
DataGridViewTextBoxEditingControlAccessibleObject controlAccessibleObject = new(this);
_dataGridView?.SetAccessibleObjectParent(controlAccessibleObject);
return controlAccessibleObject;
}
public virtual DataGridView? EditingControlDataGridView
{
get
{
return _dataGridView;
}
set
{
_dataGridView = value;
}
}
[AllowNull]
public virtual object EditingControlFormattedValue
{
get
{
return GetEditingControlFormattedValue(DataGridViewDataErrorContexts.Formatting);
}
set
{
Text = (string?)value;
}
}
public virtual int EditingControlRowIndex
{
get
{
return _rowIndex;
}
set
{
_rowIndex = value;
}
}
public virtual bool EditingControlValueChanged
{
get
{
return _valueChanged;
}
set
{
_valueChanged = value;
}
}
public virtual Cursor EditingPanelCursor
{
get
{
return Cursors.Default;
}
}
public virtual bool RepositionEditingControlOnValueChange
{
get
{
return _repositionOnValueChange;
}
}
public virtual void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)
{
ArgumentNullException.ThrowIfNull(dataGridViewCellStyle);
Font = dataGridViewCellStyle.Font;
if (dataGridViewCellStyle.BackColor.A < 255)
{
// Our TextBox does not support transparent back colors
Color opaqueBackColor = Color.FromArgb(255, dataGridViewCellStyle.BackColor);
BackColor = opaqueBackColor;
if (_dataGridView is not null)
{
_dataGridView.EditingPanel.BackColor = opaqueBackColor;
}
}
else
{
BackColor = dataGridViewCellStyle.BackColor;
}
ForeColor = dataGridViewCellStyle.ForeColor;
if (dataGridViewCellStyle.WrapMode == DataGridViewTriState.True)
{
WordWrap = true;
}
TextAlign = TranslateAlignment(dataGridViewCellStyle.Alignment);
_repositionOnValueChange = (dataGridViewCellStyle.WrapMode == DataGridViewTriState.True && (dataGridViewCellStyle.Alignment & AnyTop) == 0);
}
public virtual bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey)
{
switch (keyData & Keys.KeyCode)
{
case Keys.Right:
// If the end of the selection is at the end of the string
// let the DataGridView treat the key message
if ((RightToLeft == RightToLeft.No && !(SelectionLength == 0 && SelectionStart == Text.Length)) ||
(RightToLeft == RightToLeft.Yes && !(SelectionLength == 0 && SelectionStart == 0)))
{
return true;
}
break;
case Keys.Left:
// If the end of the selection is at the beginning of the string
// or if the entire text is selected and we did not start editing
// send this character to the dataGridView, else process the key event
if ((RightToLeft == RightToLeft.No && !(SelectionLength == 0 && SelectionStart == 0)) ||
(RightToLeft == RightToLeft.Yes && !(SelectionLength == 0 && SelectionStart == Text.Length)))
{
return true;
}
break;
case Keys.Down:
// If the end of the selection is on the last line of the text then
// send this character to the dataGridView, else process the key event
int end = SelectionStart + SelectionLength;
if (Text.IndexOf("\r\n", end, StringComparison.Ordinal) != -1)
{
return true;
}
break;
case Keys.Up:
// If the end of the selection is on the first line of the text then
// send this character to the dataGridView, else process the key event
if (!(Text.IndexOf("\r\n", StringComparison.Ordinal) < 0
|| SelectionStart + SelectionLength < Text.IndexOf("\r\n", StringComparison.Ordinal)))
{
return true;
}
break;
case Keys.Home:
case Keys.End:
if (SelectionLength != Text.Length)
{
return true;
}
break;
case Keys.Prior:
case Keys.Next:
if (_valueChanged)
{
return true;
}
break;
case Keys.Delete:
if (SelectionLength > 0 ||
SelectionStart < Text.Length)
{
return true;
}
break;
case Keys.Enter:
if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Shift && Multiline && AcceptsReturn)
{
return true;
}
break;
}
return !dataGridViewWantsInputKey;
}
public virtual object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context)
{
return Text;
}
public virtual void PrepareEditingControlForEdit(bool selectAll)
{
if (selectAll)
{
SelectAll();
}
else
{
// Do not select all the text, but
// position the caret at the end of the text
SelectionStart = Text.Length;
}
}
private void NotifyDataGridViewOfValueChange()
{
_valueChanged = true;
_dataGridView?.NotifyCurrentCellDirty(true);
}
protected override void OnMouseWheel(MouseEventArgs e)
{
// Forwarding to grid control. Can't prevent the TextBox from handling the mouse wheel as expected.
_dataGridView?.OnMouseWheelInternal(e);
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
// Let the DataGridView know about the value change
NotifyDataGridViewOfValueChange();
}
protected override bool ProcessKeyEventArgs(ref Message m)
{
switch ((Keys)(nint)m.WParamInternal)
{
case Keys.Enter:
if (m.MsgInternal == PInvoke.WM_CHAR
&& !(ModifierKeys == Keys.Shift && Multiline && AcceptsReturn))
{
// Ignore the Enter key and don't add it to the textbox content. This happens when failing
// validation brings up a dialog box for example. Shift-Enter for multiline textboxes need to
// be accepted however.
return true;
}
break;
case Keys.LineFeed:
if (m.MsgInternal == PInvoke.WM_CHAR && ModifierKeys == Keys.Control && Multiline && AcceptsReturn)
{
// Ignore linefeed character when user hits Ctrl-Enter to commit the cell.
return true;
}
break;
case Keys.A:
if (m.MsgInternal == PInvoke.WM_KEYDOWN && ModifierKeys == Keys.Control)
{
SelectAll();
return true;
}
break;
}
return base.ProcessKeyEventArgs(ref m);
}
internal override void ReleaseUiaProvider(HWND handle)
{
if (TryGetAccessibilityObject(out AccessibleObject? accessibleObject))
{
((DataGridViewTextBoxEditingControlAccessibleObject)accessibleObject).ClearParent();
}
base.ReleaseUiaProvider(handle);
}
private static HorizontalAlignment TranslateAlignment(DataGridViewContentAlignment align)
{
if ((align & AnyRight) != 0)
{
return HorizontalAlignment.Right;
}
else if ((align & AnyCenter) != 0)
{
return HorizontalAlignment.Center;
}
else
{
return HorizontalAlignment.Left;
}
}
}
|