在网格视图中操作当前编辑的行

4

我试图让gridview的编辑功能更加智能。

首先,当我按下某一行的编辑按钮时,所有包含DateTime数据的列都应该显示日期选择器而不是文本框。但是我的尝试似乎只能编辑后面的行而不能编辑当前显示的编辑项。

我的目标是将我正在编辑的行中的日期列改为日期选择器。

protected void GridView1_RowEditing(object sender, GridViewEditEventArgs e)
{
    var row = ((GridView)sender).Rows[e.NewEditIndex].Cells;

    for (int i = 0; i < row.Count; i++)
    {
        DateTime dtDate;
        var res = DateTime.TryParse(row[i].Text, out dtDate);
        if (res)
        {
            DatePickerControl.DatePicker text = new DatePickerControl.DatePicker();
            text.CalendarDate = dtDate;
            row[i].Controls.Clear();
            row[i].Controls.Add(text);
        }
    }
}

但是它似乎编辑的是后面的行,我该如何让它编辑我当前正在编辑的内容?

你难道不需要对GridView1控件的当前行/单元格进行操作吗? 像以下代码那样:int i = GridView1.CurrentRow.Index + 1; Gridview1.CurrentCell = GridView1.Rows[i].Cells[0]; - MethodMan
4个回答

2
我认为这是错误的方法,你不应该在运行时瞎弄控件类型。当 datagrid 构建时,你的 datagrid 列应该是一个日期选择器类型。

不幸的是,微软没有提供这个功能,但幸运的是他们提供了框架来允许你这样做:

你需要 3 个东西:一个新的 1) DataGridViewColumn,一个新的 2) DataGridViewTextBoxCell,最后是实际的 3) DateTimePicker 编辑控件。

1:

public class DataGridViewCalendarColumn : DataGridViewColumn
{
    public DataGridViewCalendarColumn()
        : base(new DataGridViewCalendarCell())
    { }

    public override DataGridViewCell CellTemplate
    {
        get { return base.CellTemplate; }
        set
        {
            // Ensure that the cell used for the template is a CalendarCell.
            if (value != null &&
                !value.GetType().IsAssignableFrom(typeof(DataGridViewCalendarCell)))
                throw new InvalidCastException("Must be a DataGridViewCalendarCell");

            base.CellTemplate = value;

        }
    }
}

2:

public class DataGridViewCalendarCell : DataGridViewTextBoxCell
{
    public DataGridViewCalendarCell()
        : base()
    {
        this.Style.Format = "d";    //short date style
    }

    public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
    {
        //set the value of the editing control to the current cell value
        base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);

        DataGridViewCalendarControl ctl = DataGridView.EditingControl as DataGridViewCalendarControl;

        //use default value if Value is null
        if (this.Value == null || this.Value == DBNull.Value)
            ctl.Value = (DateTime) this.DefaultNewRowValue;
        else
            ctl.Value = (DateTime) this.Value;
    }

    public override Type EditType
    {
        get
        {
            //return the type of control this cell uses
            return typeof(DataGridViewCalendarControl);
        }
    }

    public override Type ValueType
    {
        get
        {
            //return the type of the value that this cell contains
            return typeof(DateTime);
        }
    }

    public override object DefaultNewRowValue
    {
        get
        {
            //use today's date as the default value
            return DateTime.Now;
        }
    }
}

3:

public class DataGridViewCalendarControl : DateTimePicker, IDataGridViewEditingControl
    {
        private DataGridView dataGridView;
        private bool hasValueChanged = false;
        int rowIndex;

        public DataGridViewCalendarControl()
        {
            this.Format = DateTimePickerFormat.Short;
        }

        protected override void OnValueChanged(EventArgs eventargs)
        {
            //Notify the DataGridView that the value has changed
            hasValueChanged = true;
            this.EditingControlDataGridView.NotifyCurrentCellDirty(true);

            base.OnValueChanged(eventargs);
        }

        #region IDataGridViewEditingControl Members

        public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)
        {
            this.Font = dataGridViewCellStyle.Font;
            this.CalendarForeColor = dataGridViewCellStyle.ForeColor;
            this.CalendarMonthBackground = dataGridViewCellStyle.BackColor;
        }

        public DataGridView EditingControlDataGridView
        {
            get { return dataGridView; }
            set { dataGridView = value; }
        }

        public object EditingControlFormattedValue
        {
            get { return this.Value.ToShortDateString(); }
            set
            {
                if (value is String)
                    try
                    {
                        this.Value = DateTime.Parse((String) value);
                    }
                    catch
                    {
                        this.Value = DateTime.Now;
                    }
            }
        }

        public int EditingControlRowIndex
        {
            get { return rowIndex; }
            set { rowIndex = value; }
        }

        public bool EditingControlValueChanged
        {
            get { return hasValueChanged; }
            set { hasValueChanged = value; }
        }

        public bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey)
        {
            //the DateTimePicker needs to handle the keys
            switch (keyData & Keys.KeyCode)
            {
                case Keys.Left:
                case Keys.Right:
                case Keys.Up:
                case Keys.Down:
                case Keys.Home:
                case Keys.End:
                case Keys.PageUp:
                case Keys.PageDown:
                    return true;

                default:
                    return !dataGridViewWantsInputKey;
            }
        }

        public Cursor EditingPanelCursor
        {
            get { return base.Cursor; }
        }

        public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context)
        {
            return EditingControlFormattedValue;
        }

        public void PrepareEditingControlForEdit(bool selectAll)
        {
            //nowt needs doing...
        }

        public bool RepositionEditingControlOnValueChange
        {
            get { return false; }
        }

然后在窗体的“Load”事件中:

eventDateDataGridViewCalendarColumn = new DataGridViewCalendarColumn();
eventDateDataGridViewCalendarColumn.DataPropertyName = "EventDate";
eventDateDataGridViewCalendarColumn.HeaderText = "Date";
//we've created the column, now insert it into the right location (after the ID and UserID)
this.tSEventsDataGridView.Columns.Insert(2, eventDateDataGridViewCalendarColumn);

//remove the original TextBox EventDate column added via VS
this.tSEventsDataGridView.Columns.RemoveAt(3);

这里的eventDateDataGridViewCalendarColumn是一个私有的DataGridViewCalendarColumn。这样我们就在任何编辑事件中都没有数据了。

虽然这对我们有效,但像网上随意找到的示例一样,使用时请自行承担风险!


忘了提到,你应该能够看到这允许网格包含任何类型的控件,内置或者是你创建的。 - cjb110
你有没有一个小的示例项目可以提供吗? - Nahum
我同意@cjb110的观点,并且我已经做了类似的解决方案,但我肯定是在vc++中完成的。.NET控件可能存在一些错误,长路径总是需要时间来负责。 - saeed

2

即使方法不同,可能对于您的场景来说有点过于简单化,我建议您使用<asp:TemplateField />来自定义编辑项模板(如下所示)。当然,您也可以将我使用的<asp:Calendar />控件替换为您自定义的DatePickerControl.DatePicker控件。

<asp:GridView ID="GridView1" runat="server" OnRowEditing="GridView1_RowEditing">
    <Columns>
        <asp:TemplateField>
            <EditItemTemplate>
                <asp:Calendar runat="server" SelectedDate='<%# Eval("Date") %>'></asp:Calendar>
            </EditItemTemplate>
            <ItemTemplate>
                <%# Eval("Date") %>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

[更新]

您还可以动态创建列。请查看下面的代码:

public partial class WebForm1 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        GridView1.AutoGenerateColumns = false;
        GridView1.AutoGenerateEditButton = true;

        DataTable dataSource = new DataTable();
        dataSource.Columns.Add("Id", typeof(int));
        dataSource.Columns.Add("Date1", typeof(DateTime));
        dataSource.Columns.Add("Date2", typeof(DateTime));
        dataSource.Rows.Add(1, DateTime.Now, DateTime.Now.AddMonths(1));
        dataSource.Rows.Add(2, DateTime.Now.AddMonths(2), DateTime.Now.AddMonths(3));

        GridView1.Columns.Clear();
        foreach (DataColumn column in dataSource.Columns)
        {
            if (column.DataType == typeof(DateTime))
            {
                var templateColumn = new TemplateField();
                templateColumn.EditItemTemplate = new AddTemplateToGridView(ListItemType.EditItem, column.ColumnName);
                templateColumn.ItemTemplate = new AddTemplateToGridView(ListItemType.Item, column.ColumnName);
                templateColumn.HeaderText = column.ColumnName;
                GridView1.Columns.Add(templateColumn);
            }
            else
            {
                var dataBoundColumn = new BoundField();
                dataBoundColumn.DataField = column.ColumnName;
                dataBoundColumn.HeaderText = column.ColumnName;
                GridView1.Columns.Add(dataBoundColumn);
            }
        }

        GridView1.DataSource = dataSource;
        GridView1.DataBind();

    }

    public class AddTemplateToGridView : ITemplate
    {

        ListItemType _type;
        string _colName;

        public AddTemplateToGridView(ListItemType type, string colname)
        {
            _type = type;
            _colName = colname;
        }

        public void InstantiateIn(Control container)
        {
            switch (_type)
            {
                case ListItemType.Item:

                    Label l = new Label();
                    l.DataBinding += l_DataBinding;
                    container.Controls.Add(l);

                    break;
                case ListItemType.EditItem:

                    Calendar calendar = new Calendar();
                    calendar.DataBinding += l_DataBinding;
                    container.Controls.Add(calendar);

                    break;
            }
        }

        void l_DataBinding(object sender, EventArgs e)
        {
            GridViewRow container;
            object dataValue;
            switch (sender.GetType().ToString())
            {
                case "System.Web.UI.WebControls.Label":
                    Label label = (Label)sender;
                    container = (GridViewRow)label.NamingContainer;
                    dataValue = DataBinder.Eval(container.DataItem, _colName);
                    if (dataValue != DBNull.Value)
                    {
                        label.Text = dataValue.ToString();
                    }
                    break;
                //use the DatePickerControl.DatePicker type instead of calendar
                case "System.Web.UI.WebControls.Calendar":
                    Calendar calendar = (Calendar)sender;
                    container = (GridViewRow)calendar.NamingContainer;
                    dataValue = DataBinder.Eval(container.DataItem, _colName);
                    if (dataValue != DBNull.Value)
                    {
                        calendar.SelectedDate = (DateTime)dataValue;
                        calendar.VisibleDate = (DateTime)dataValue;
                    }
                    break;
            }
        }
    }

    protected void GridView1_RowEditing(object sender, GridViewEditEventArgs e)
    {
        GridView1.EditIndex = e.NewEditIndex;
        GridView1.DataBind();
    }

}

Alex,我的问题是网格应该与任何表一起使用。我不知道我将提供什么表。 - Nahum
好的,那么在代码后台动态声明并添加您的TemplateField吧。但是也许如果这样做,它就不再是“简单化”的了 :) - Alex Filipovici
这是一个很好的例子:动态创建GridView的TemplateField - Alex Filipovici

0
 protected void GridView1_RowEditing(object sender, GridViewEditEventArgs e)
{
    bindgrid();
    var row = ((GridView)sender).Rows[e.NewEditIndex].Cells;

    for (int i = 0; i < row.Count; i++)
    {
        DateTime dtDate;
        var res = DateTime.TryParse(row[i].Text, out dtDate);
        if (res)
        {
            Calendar cal = new Calendar();
            cal.SelectedDate = dtDate;
            cal.VisibleDate = dtDate;
            cal.DataBind();
            //DatePickerControl.DatePicker text = new DatePickerControl.DatePicker();
            //text.CalendarDate = dtDate;
            row[i].Controls.Clear();
            row[i].Controls.Add(cal);
        }
    }

}

试一下这个


这只是我编写的一个简单函数,用于绑定网格,在你的情况下,你可能已经有了它。 - Kiran1016

0

看起来您的问题在于e.NewEditIndex不是正确的值,因为RowEditing事件在行实际进入编辑模式之前就已被触发。这意味着e.NewEditIndex的值将是您上次编辑的任何一行的索引。如果我猜测的话,您正在按顺序遍历行,所以纯属巧合您总是修改“后面的行”。

从文档中可以看出,您可以通过访问GridView.SelectedIndex属性获得当前选定行的索引。如果在编辑之前将选择该行:

您可以将:var row = ((GridView)sender).Rows[e.NewEditIndex].Cells;

更改为:var row = ((GridView)sender).Rows[(GridView)sender.SelectedIndex].Cells;

您也可以尝试使用SetEditRow方法手动将行放入编辑模式:http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.seteditrow.aspx

无论哪种方式,您在调用RowEditing事件之前都需要知道当前行的索引,或者使用SetEditRow方法将行强制进入编辑模式,这将传播正确的索引到RowEditing事件中。

祝好运。


"e.NewEditIndex" 是当前正在编辑的行的值。这可以通过简单的试错来确认。所选索引是使用“select”命令选择的行的索引。你完全走错了方向。虽然感谢你的尝试。 - Nahum
好的,我猜我误解了文档。如果是这样的话,你不能只将索引加1来获取正确的行吗? - B L

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接