|
//------------------------------------------------------------------------------
// <copyright file="DataGridTextBoxColumn.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Windows.Forms {
using System.Runtime.Remoting;
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms.ComponentModel;
using System.Drawing;
using Microsoft.Win32;
using System.Diagnostics;
using System.Globalization;
using System.Diagnostics.CodeAnalysis;
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn"]/*' />
/// <devdoc>
/// <para>Hosts a System.Windows.Forms.TextBox control in a cell of a System.Windows.Forms.DataGridColumnStyle for editing strings.</para>
/// </devdoc>
public class DataGridTextBoxColumn : DataGridColumnStyle {
// ui State
private int xMargin = 2;
private int yMargin = 1;
// private int fontHandle = 0;
private string format = null;
private TypeConverter typeConverter;
private IFormatProvider formatInfo = null;
private System.Reflection.MethodInfo parseMethod;
// hosted control
private DataGridTextBox edit;
// editing state
private string oldValue = null;
private int editRow = -1;
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.DataGridTextBoxColumn"]/*' />
/// <devdoc>
/// <para>Initializes a new instance of the System.Windows.Forms.DataGridTextBoxColumn
/// class.</para>
/// </devdoc>
[
SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set format
// it would be a breaking change.
]
public DataGridTextBoxColumn() : this(null, null) {
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.DataGridTextBoxColumn1"]/*' />
/// <devdoc>
/// <para>Initializes a new instance of a System.Windows.Forms.DataGridTextBoxColumn with
/// a specified System.Data.DataColumn.</para>
/// </devdoc>
[
SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set format
// it would be a breaking change.
]
public DataGridTextBoxColumn(PropertyDescriptor prop)
: this(prop, null, false) {
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.DataGridTextBoxColumn2"]/*' />
/// <devdoc>
/// <para>Initializes a new instance of a System.Windows.Forms.DataGridTextBoxColumn. with
/// the specified System.Data.DataColumn and System.Windows.Forms.ComponentModel.Format.</para>
/// </devdoc>
[
SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set format
// it would be a breaking change.
]
public DataGridTextBoxColumn(PropertyDescriptor prop, string format) : this(prop, format, false){}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.DataGridTextBoxColumn3"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set format
// it would be a breaking change.
]
public DataGridTextBoxColumn(PropertyDescriptor prop, string format, bool isDefault) : base(prop, isDefault) {
edit = new DataGridTextBox();
edit.BorderStyle = BorderStyle.None;
edit.Multiline = true;
edit.AcceptsReturn = true;
edit.Visible = false;
this.Format = format;
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.DataGridTextBoxColumn4"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set format
// it would be a breaking change.
]
public DataGridTextBoxColumn(PropertyDescriptor prop, bool isDefault) : this(prop, null, isDefault) {}
// =------------------------------------------------------------------
// = Properties
// =------------------------------------------------------------------
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.TextBox"]/*' />
/// <devdoc>
/// <para>Gets the hosted System.Windows.Forms.TextBox control.</para>
/// </devdoc>
[Browsable(false)]
public virtual TextBox TextBox {
get {
return edit;
}
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.KeyPress"]/*' />
internal override bool KeyPress(int rowNum, Keys keyData) {
if (edit.IsInEditOrNavigateMode)
return base.KeyPress(rowNum, keyData);
// if the edit box is editing, then
// pass this keystroke to the edit box
//
return false;
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.SetDataGridInColumn"]/*' />
/// <devdoc>
/// <para>
/// Adds a System.Windows.Forms.TextBox control to the System.Windows.Forms.DataGrid control's System.Windows.Forms.Control.ControlCollection
/// .
/// </para>
/// </devdoc>
protected override void SetDataGridInColumn(DataGrid value) {
base.SetDataGridInColumn(value);
if (edit.ParentInternal != null) {
edit.ParentInternal.Controls.Remove(edit);
}
if (value != null) {
value.Controls.Add(edit);
}
// we have to tell the edit control about its dataGrid
edit.SetDataGrid(value);
}
/* CUT as part of the new DataGridTableStyleSheet thing
public override Font Font {
set {
base.Font = value;
Font f = base.Font;
edit.Font = f;
// if (f != null) {
// fontHandle = f.Handle;
// }
}
}
*/
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.PropertyDescriptor"]/*' />
/// <devdoc>
/// <para>Gets or sets the System.Windows.Forms.ComponentModel.Format for the System.Windows.Forms.DataGridTextBoxColumn
/// .</para>
/// </devdoc>
[
SRDescription(SR.FormatControlFormatDescr),
DefaultValue(null)
]
public override PropertyDescriptor PropertyDescriptor {
set {
base.PropertyDescriptor = value;
if (this.PropertyDescriptor != null) {
if (this.PropertyDescriptor.PropertyType != typeof(object)) {
this.typeConverter = TypeDescriptor.GetConverter(this.PropertyDescriptor.PropertyType);
this.parseMethod = this.PropertyDescriptor.PropertyType.GetMethod("Parse", new Type[]{typeof(string), typeof(IFormatProvider)});
}
}
}
}
// add the corresponding value Editor: rip one from the valueEditor for the DisplayMember in the
// format object
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.Format"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[DefaultValue(null), Editor("System.Windows.Forms.Design.DataGridColumnStyleFormatEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor))]
public string Format {
get {
return format;
}
set {
if (value == null)
value = "";
if (format == null || !format.Equals(value)) {
this.format = value;
// if the associated typeConverter cannot convert from string,
// then we can't modify the column value. hence, make it readOnly
//
if (format.Length == 0) {
if (this.typeConverter != null && !typeConverter.CanConvertFrom(typeof(string)))
this.ReadOnly = true;
}
Invalidate();
}
}
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.FormatInfo"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
public IFormatProvider FormatInfo {
get {
return this.formatInfo;
}
set {
if (this.formatInfo == null || !this.formatInfo.Equals(value))
this.formatInfo = value;
}
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.ReadOnly"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public override bool ReadOnly {
get {
return base.ReadOnly;
}
set {
// if the gridColumn is can't convert the string to
// the backGround propertyDescriptor, then make the column ReadOnly
if (!value && (format == null || format.Length == 0)) {
if (this.typeConverter != null && !this.typeConverter.CanConvertFrom(typeof(string)))
return;
}
base.ReadOnly = value;
}
}
// =------------------------------------------------------------------
// = Methods
// =------------------------------------------------------------------
private void DebugOut(string s) {
Debug.WriteLineIf(CompModSwitches.DGEditColumnEditing.TraceVerbose, "DGEditColumnEditing: " + s);
}
// will hide the edit control
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.ConcedeFocus"]/*' />
/// <devdoc>
/// <para>Informs the column the focus is being conceded.</para>
/// </devdoc>
protected internal override void ConcedeFocus() {
edit.Bounds = Rectangle.Empty;
// edit.Visible = false;
// HideEditBox();
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.HideEditBox"]/*' />
/// <devdoc>
/// <para>
/// Hides the System.Windows.Forms.TextBox
/// control and moves the focus to the System.Windows.Forms.DataGrid
/// control.
/// </para>
/// </devdoc>
protected void HideEditBox() {
bool wasFocused = edit.Focused;
edit.Visible = false;
// it seems that edit.Visible = false will take away the focus from
// the edit control. And this means that we will not give the focus to the grid
// If all the columns would have an edit control this would not be bad
// ( or if the grid is the only control on the form ),
// but when we have a DataGridBoolColumn then the focus will be taken away
// by the next control in the form.
//
// if (edit.Focused && this.DataGridTableStyle.DataGrid.CanFocus) {
// when the user deletes the current ( ie active ) column from the
// grid, the grid should still call EndEdit ( so that the changes that the user made
// before deleting the column will go to the backEnd)
// however, in that situation, we are left w/ the editColumn which is not parented.
// the grid will call Edit to reset the EditColumn
if (wasFocused && this.DataGridTableStyle != null && this.DataGridTableStyle.DataGrid != null && this.DataGridTableStyle.DataGrid.CanFocus) {
this.DataGridTableStyle.DataGrid.FocusInternal();
Debug.Assert(!edit.Focused, "the edit control just conceeded focus to the dataGrid");
}
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.UpdateUI"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected internal override void UpdateUI(CurrencyManager source, int rowNum, string displayText)
{
edit.Text = GetText(GetColumnValueAtRow(source, rowNum));
if (!edit.ReadOnly && displayText != null)
edit.Text = displayText;
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.EndEdit"]/*' />
/// <devdoc>
/// <para>Ends an edit operation on the System.Windows.Forms.DataGridColumnStyle
/// .</para>
/// </devdoc>
protected void EndEdit() {
edit.IsInEditOrNavigateMode = true;
DebugOut("Ending Edit");
Invalidate();
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.GetPreferredSize"]/*' />
/// <devdoc>
/// <para>Returns the optimum width and
/// height of the cell in a specified row relative
/// to the specified value.</para>
/// </devdoc>
protected internal override Size GetPreferredSize(Graphics g, object value) {
Size extents = Size.Ceiling(g.MeasureString(GetText(value), DataGridTableStyle.DataGrid.Font));
extents.Width += xMargin*2 + this.DataGridTableStyle.GridLineWidth;
extents.Height += yMargin;
return extents;
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.GetMinimumHeight"]/*' />
/// <devdoc>
/// <para>
/// Gets the height of a cell in a System.Windows.Forms.DataGridColumnStyle
/// .
/// </para>
/// </devdoc>
protected internal override int GetMinimumHeight() {
// why + 3? cause we have to give some way to the edit box.
return FontHeight + yMargin + 3;
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.GetPreferredHeight"]/*' />
/// <devdoc>
/// <para>Gets the height to be used in for automatically resizing columns.</para>
/// </devdoc>
protected internal override int GetPreferredHeight(Graphics g, object value) {
int newLineIndex = 0;
int newLines = 0;
string valueString = GetText(value);
while (newLineIndex != -1 && newLineIndex < valueString.Length)
{
newLineIndex = valueString.IndexOf("\r\n", newLineIndex + 1);
newLines ++;
}
return FontHeight * newLines + yMargin;
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.Abort"]/*' />
/// <devdoc>
/// <para>
/// Initiates a request to interrupt an edit procedure.
/// </para>
/// </devdoc>
protected internal override void Abort(int rowNum) {
RollBack();
HideEditBox();
EndEdit();
}
// used for Alt0 functionality
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.EnterNullValue"]/*' />
/// <devdoc>
/// <para>
/// Enters a <see langword='null '/>in the column.
/// </para>
/// </devdoc>
protected internal override void EnterNullValue() {
if (this.ReadOnly)
return;
// if the edit box is not visible, then
// do not put the edit text in it
if (!edit.Visible)
return;
// if we are editing, then we should be able to enter alt-0 in a cell.
//
if (!edit.IsInEditOrNavigateMode)
return;
edit.Text = NullText;
// edit.Visible = true;
edit.IsInEditOrNavigateMode = false;
// tell the dataGrid that there is an edit:
if (this.DataGridTableStyle != null && this.DataGridTableStyle.DataGrid != null)
this.DataGridTableStyle.DataGrid.ColumnStartedEditing(edit.Bounds);
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.Commit"]/*' />
/// <devdoc>
/// <para>
/// Inititates a request to complete an editing procedure.
/// </para>
/// </devdoc>
protected internal override bool Commit(CurrencyManager dataSource, int rowNum) {
// always hide the edit box
// HideEditBox();
edit.Bounds = Rectangle.Empty;
if (edit.IsInEditOrNavigateMode)
return true;
try {
object value = edit.Text;
if (NullText.Equals(value)) {
value = Convert.DBNull;
edit.Text = NullText;
} else if (format != null && format.Length != 0 && this.parseMethod != null && this.FormatInfo != null) {
// use reflection to get the Parse method on the
// type of the propertyDescriptor.
value = (object) parseMethod.Invoke(null, new object[] {edit.Text, this.FormatInfo});
if (value is IFormattable) {
edit.Text = ((IFormattable)value).ToString(format, formatInfo);
}else
edit.Text = value.ToString();
} else if (typeConverter != null && typeConverter.CanConvertFrom(typeof(string))) {
value = typeConverter.ConvertFromString(edit.Text);
edit.Text = typeConverter.ConvertToString(value);
}
SetColumnValueAtRow(dataSource, rowNum, value);
}
catch {
// MessageBox.Show("There was an error caught setting field \""
// + this.PropertyDescriptor.Name + "\" to the value \"" + edit.Text + "\"\n"
// + "The value is being rolled back to the original.\n"
// + "The error was a '" + e.Message + "' " + e.StackTrace
// , "Error commiting changes...", MessageBox.IconError);
// Debug.WriteLine(e.GetType().Name);
RollBack();
return false;
}
DebugOut("OnCommit completed without Exception.");
EndEdit();
return true;
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.Edit"]/*' />
/// <devdoc>
/// <para>Prepares a cell for editing.</para>
/// </devdoc>
protected internal override void Edit(CurrencyManager source,
int rowNum,
Rectangle bounds,
bool readOnly,
string displayText,
bool cellIsVisible) {
DebugOut("Begining Edit, rowNum :" + rowNum.ToString(CultureInfo.InvariantCulture));
Rectangle originalBounds = bounds;
edit.ReadOnly = readOnly || ReadOnly || this.DataGridTableStyle.ReadOnly;
edit.Text = GetText(GetColumnValueAtRow(source, rowNum));
if (!edit.ReadOnly && displayText != null) {
// tell the grid that we are changing stuff
this.DataGridTableStyle.DataGrid.ColumnStartedEditing(bounds);
// tell the edit control that the user changed it
this.edit.IsInEditOrNavigateMode = false;
edit.Text = displayText;
}
if (cellIsVisible) {
bounds.Offset(xMargin, 2 * yMargin);
bounds.Width -= xMargin;
bounds.Height -= 2 * yMargin;
DebugOut("edit bounds: " + bounds.ToString());
edit.Bounds = bounds;
edit.Visible = true;
edit.TextAlign = this.Alignment;
}
else {
edit.Bounds = Rectangle.Empty;
// edit.Bounds = originalBounds;
// edit.Visible = false;
}
edit.RightToLeft = this.DataGridTableStyle.DataGrid.RightToLeft;
edit.FocusInternal();
editRow = rowNum;
if (!edit.ReadOnly) {
oldValue = edit.Text;
}
// select the text even if the text box is read only
// because the navigation code in the DataGridTextBox::ProcessKeyMessage
// uses the SelectedText property
if (displayText == null)
edit.SelectAll();
else {
int end = edit.Text.Length;
edit.Select(end, 0);
}
if (edit.Visible)
DataGridTableStyle.DataGrid.Invalidate(originalBounds);
}
internal override string GetDisplayText(object value) {
return GetText(value);
}
private string GetText(object value) {
if (value is System.DBNull)
return NullText;
else if (format != null && format.Length != 0 && (value is IFormattable)) {
try {
return ((IFormattable)value).ToString(format, this.formatInfo);
}
catch {
//
}
} else {
// use the typeConverter:
if (this.typeConverter != null && this.typeConverter.CanConvertTo(typeof(string)))
return (string)this.typeConverter.ConvertTo(value, typeof(string));
}
return(value != null ? value.ToString() : "");
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.Paint"]/*' />
/// <devdoc>
/// <para>Paints the a System.Windows.Forms.DataGridColumnStyle with the specified System.Drawing.Graphics,
/// System.Drawing.Rectangle, DataView.Rectangle, and row number. </para>
/// </devdoc>
protected internal override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum) {
Paint(g, bounds, source, rowNum, false);
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.Paint1"]/*' />
/// <devdoc>
/// <para>
/// Paints a System.Windows.Forms.DataGridColumnStyle with the specified System.Drawing.Graphics, System.Drawing.Rectangle, DataView, row number, and alignment.
/// </para>
/// </devdoc>
protected internal override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, bool alignToRight) {
string text = GetText(GetColumnValueAtRow(source, rowNum));
PaintText(g, bounds, text, alignToRight);
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.Paint2"]/*' />
/// <devdoc>
/// <para>Paints a System.Windows.Forms.DataGridColumnStyle with the specified System.Drawing.Graphics,
/// System.Drawing.Rectangle, DataView.Rectangle, row number, background color,
/// and foreground color..</para>
/// </devdoc>
protected internal override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum,
Brush backBrush, Brush foreBrush, bool alignToRight) {
string text = GetText(GetColumnValueAtRow(source, rowNum));
PaintText(g, bounds, text, backBrush, foreBrush, alignToRight);
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.PaintText"]/*' />
/// <devdoc>
/// <para>Draws the text and
/// rectangle at the given location with the specified alignment.</para>
/// </devdoc>
protected void PaintText(Graphics g, Rectangle bounds, string text, bool alignToRight) {
PaintText(g, bounds, text, this.DataGridTableStyle.BackBrush, this.DataGridTableStyle.ForeBrush, alignToRight);
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.PaintText1"]/*' />
/// <devdoc>
/// <para>Draws the text and rectangle at the specified location with the
/// specified colors and alignment.</para>
/// </devdoc>
protected void PaintText(Graphics g, Rectangle textBounds, string text, Brush backBrush, Brush foreBrush, bool alignToRight) {
/*
if (edit.Visible)
g.BackColor = BackColor;
*/
Rectangle rect = textBounds;
StringFormat format = new StringFormat();
if (alignToRight) {
format.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
}
format.Alignment = this.Alignment == HorizontalAlignment.Left ? StringAlignment.Near : this.Alignment == HorizontalAlignment.Center ? StringAlignment.Center : StringAlignment.Far;
// do not wrap the text
//
format.FormatFlags |= StringFormatFlags.NoWrap;
g.FillRectangle(backBrush, rect);
// by design, painting leaves a little padding around the rectangle.
// so do not deflate the rectangle.
rect.Offset(0,2 * yMargin);
rect.Height -= 2 * yMargin;
g.DrawString(text, this.DataGridTableStyle.DataGrid.Font, foreBrush, rect, format);
format.Dispose();
}
private void RollBack() {
Debug.Assert(!edit.IsInEditOrNavigateMode, "Must be editing to rollback changes...");
edit.Text = oldValue;
}
/// <include file='doc\DataGridTextBoxColumn.uex' path='docs/doc[@for="DataGridTextBoxColumn.ReleaseHostedControl"]/*' />
protected internal override void ReleaseHostedControl() {
if (edit.ParentInternal != null) {
edit.ParentInternal.Controls.Remove(edit);
}
}
}
}
|