输入无效数据后,ASP.NET GridView RowDataBound TemplateField FindControl为null

时间:2014-10-28 13:41:21

标签: asp.net gridview data-binding

单击“保存”将数据保存到数据库后,我的GridView RowDataBound事件出现问题。

我有一个包含5列的网格:标记名称,当前时间戳,当前值,新日期,新值。

这个想法是用户将进入新日期/新值来更新数据库中的数据。当前时间戳和当前值是已存储在数据库中的内容。

我使用JQuery编辑器输入日期。

当我点击“保存”时,我会进行服务器端验证以检查输入的值。如果数据有效,则会显示“新值”列中文本下的消息以指示此情况。如果验证失败,则会显示“新值”列中的消息。

新日期和新值列是TemplateField的。新值TemplateField包含一个带有两个标签的面板 - 一个用于OK状态,另一个用于错误。

当我调用任何e.Row.FindControl(...)时,MyDataGrid_RowDataBound中出现错误,当我重新绑定时,这是从Save按钮单击时触发的。

如果输入了有效值,但输入的值无效,则表示错误。为了简化方案,输入100作为有效值,输入任何其他无效值。

这个WebForm一直是我生活中的麻烦。也许我不应该使用GridView。

无论如何,我很感激在此帮助确定问题。也许是一种更好的表单代码方法。

我从我的解决方案中获取了代码并将其转移到一个独立的解决方案中以重现该问题。我删除了数据库访问并填充了数据结构。这只是在页面初始加载时在页面加载中完成(一次)。

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>

    <link rel="stylesheet" href="Styles/jquery-ui-timepicker-addon.css" />
    <link rel="stylesheet" href="Styles/jquery-ui-1.10.1.custom.css" />
    <link rel="stylesheet" href="Styles/jquery-ui-1.10.1.custom.min.css" />

    <script src="Scripts/jquery-1.9.1.js"></script>
    <script src="Scripts/jquery-ui-1.10.1.custom.js"></script>
    <script src="Scripts/jquery-ui-1.10.1.custom.min.js"></script>
    <script src="Scripts/jquery-ui-timepicker-addon.js"></script>

</head>
<body>
    <script type="text/javascript">

        /** Disable backkey */
        $(document).unbind('keydown').bind('keydown', function (event) {
            var doPrevent = false;
            if (event.keyCode === 8) {
                var d = event.srcElement || event.target;
                if ((d.tagName.toUpperCase() === 'INPUT' && (d.type.toUpperCase() === 'TEXT' || d.type.toUpperCase() === 'PASSWORD' || d.type.toUpperCase() === 'FILE'))
                     || d.tagName.toUpperCase() === 'TEXTAREA') {
                    doPrevent = d.readOnly || d.disabled;
                } else {
                    doPrevent = true;
                }
            }

            if (doPrevent) {
                event.preventDefault();
            }
        });

        $(function () {
            $(".datetimepicker").datetimepicker({
                changeMonth: true,
                changeYear: true
            });
        });

        $(function () {
            $(".datepicker").datepicker({
                changeMonth: true,
                changeYear: true
            });
        });

        $(function () {
            $(".timepicker").timepicker({ showTimezone: false });
        });
    </script>


    <form id="form1" runat="server">
        <div>
            <asp:GridView ID="MyDataGrid" runat="server"
                EnableModelValidation="True"
                DataKeyNames="Id"
                AutoGenerateColumns="False"
                AutoGenerateSelectButton="False"
                EmptyDataText="There is no plant data configured."
                OnRowDataBound="MyDataGrid_RowDataBound">
                <Columns>
                    <asp:BoundField DataField="Name" SortExpression="Name" HeaderText="Tag Name"></asp:BoundField>
                    <asp:BoundField DataField="Date" SortExpression="Date" HeaderText="Current Timestamp"></asp:BoundField>
                    <asp:BoundField DataField="Value" SortExpression="Value" HeaderText="Current Value"></asp:BoundField>
                    <asp:TemplateField HeaderText="New Date&lt;BR/&gt;MM/DD/YYYY HH:MM">
                        <ItemTemplate>
                            <asp:TextBox ID="txtNewDate" runat="server" CssClass="datetimepicker"></asp:TextBox>
                        </ItemTemplate>
                    </asp:TemplateField>
                    <asp:TemplateField HeaderText="New Value">
                        <ItemTemplate>
                            <asp:TextBox ID="txtNewValue" runat="server"></asp:TextBox>
                            <asp:Panel ID="ErrorPanel" runat="server">
                                <br />
                                <asp:Label ID="lblError" runat="server" ForeColor="Red"></asp:Label>
                                <asp:Label ID="lblStatus" runat="server" ForeColor="#66cc00"></asp:Label>
                            </asp:Panel>
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
            </asp:GridView>
            <asp:Button ID="SaveButton" runat="server"
                Text="Save Values"
                ToolTip="Save the current changes."
                OnClick="SaveButton_Click" />
        </div>
    </form>
</body>
</html>

代码背后

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page
{
    private const int COL_NEW_DATE = 3;
    private const int COL_NEW_VALUE = 4;
    bool canEdit = true;

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!this.IsPostBack)
        {
            PopulateDataGrid();
        }
    }

    private void PopulateDataGrid()
    {
        // this is where i load from the database
        List<RowData> data = new List<RowData>() {
            new RowData() { Date = new DateTime(2000,1,1), Editable = true, Name = "Data Item 1", Value = 100.0 },
            new RowData() { Date = new DateTime(2000,1,1), Editable = false, Name = "Data Item 2", Value = 120.0 },
            new RowData() { Date = new DateTime(2000,1,1), Editable = true, Name = "Data Item 3", Value = 19.0 }
        };

        this.MyDataGrid.DataSource = data;
        this.MyDataGrid.DataBind();

        ViewState["GridData"] = this.MyDataGrid.DataSource;
    }

    private void SaveData()
    {
        for (int i = Page.Validators.Count - 1; i >= 0; i--)
            Page.Validators.Remove(Page.Validators[i]);

        ValidateData();

        List<RowData> rowDataList = (List<RowData>)ViewState["GridData"];

        if (this.IsValid)
        {
            foreach (GridViewRow row in this.MyDataGrid.Rows)
            {
                if (row.RowType == System.Web.UI.WebControls.DataControlRowType.DataRow && row.Enabled)
                {
                    RowData dataItem = rowDataList[row.DataItemIndex];

                    var txtNewValue = row.Cells[COL_NEW_VALUE].FindControl("txtNewValue") as TextBox;
                    var txtNewDate = row.Cells[COL_NEW_DATE].FindControl("txtNewDate") as TextBox;

                    if (dataItem != null && txtNewDate != null && txtNewValue != null && !string.IsNullOrEmpty(txtNewValue.Text) && !string.IsNullOrEmpty(txtNewDate.Text))
                    {
                        var newValue = double.Parse(txtNewValue.Text);
                        var newDate = DateTime.Parse(txtNewDate.Text);

                        dataItem.InfoText = "Value written successfully for " + txtNewDate.Text;
                        dataItem.ErrorText = string.Empty;
                        dataItem.EnteredDateCache = string.Empty;
                        dataItem.EnteredValueCache = string.Empty;

                        if ((dataItem.Date.HasValue && DateTime.Compare(newDate, dataItem.Date.Value) >= 0) || !dataItem.Date.HasValue)
                        {
                            dataItem.Date = newDate;
                            dataItem.Value = newValue;
                        }
                    }
                }
            }
            // save any outstanding changes if valid removed from demo
        }

        ViewState["GridData"] = rowDataList;
    }

    private void ValidateData()
    {
        List<RowData> rowDataList = (List<RowData>)ViewState["GridData"];

        foreach (GridViewRow row in this.MyDataGrid.Rows)
        {
            if (row.RowType == System.Web.UI.WebControls.DataControlRowType.DataRow && row.Enabled)
                ValidateDataRow(rowDataList, row);
        }
    }

    private void ValidateDataRow(List<RowData> rowDataList, GridViewRow gridViewRow)
    {
        RowData rowData = rowDataList[gridViewRow.DataItemIndex];

        bool valueOK = false;
        var txtNewValue = gridViewRow.Cells[COL_NEW_VALUE].FindControl("txtNewValue") as TextBox;
        var txtNewDate = gridViewRow.Cells[COL_NEW_DATE].FindControl("txtNewDate") as TextBox;
        var labelError = gridViewRow.Cells[COL_NEW_VALUE].FindControl("lblError") as Label;
        var labelInfo = gridViewRow.Cells[COL_NEW_VALUE].FindControl("lblStatus") as Label;

        labelInfo.Text = string.Empty;
        labelError.Text = string.Empty;
        rowData.InfoText = string.Empty;
        rowData.ErrorText = string.Empty;
        rowData.EnteredDateCache = txtNewDate.Text;
        rowData.EnteredValueCache = txtNewValue.Text;

        if (rowData != null && (!string.IsNullOrEmpty(txtNewValue.Text) || !string.IsNullOrEmpty(txtNewDate.Text)))
        {
            if (!txtNewValue.Text.IsNumber())
            {
                rowData.ErrorText = rowData.Name + " must be a number.";
                AddCustomValidatorForCell(rowData.ErrorText, gridViewRow, 4);
            }
            else
            {
                if (txtNewValue.Text != "100")
                {
                    rowData.ErrorText = rowData.Name + " is invalid.";
                    AddCustomValidatorForCell(rowData.ErrorText, gridViewRow, 4);
                }
                else
                {
                    valueOK = true;
                }
            }
        }
    }

    private void AddCustomValidatorForCell(string errorMessage, GridViewRow gridViewRow, int cellIndex)
    {
        var labelError = gridViewRow.Cells[cellIndex].FindControl("lblError") as Label;
        var divInfoError = gridViewRow.Cells[cellIndex].FindControl("ErrorPanel") as Panel;
        labelError.Text = errorMessage;
        labelError.ToolTip = errorMessage;
        labelError.Attributes.Add("style", "color: red;");

        CustomValidator validatePower = new CustomValidator()
        {
            IsValid = false,
            ErrorMessage = errorMessage,
            EnableViewState = false,
        };
        Page.Validators.Add(validatePower);
    }

    protected void SaveButton_Click(object sender, EventArgs e)
    {
        SaveData();

        this.MyDataGrid.DataSource = ViewState["GridData"];
        this.MyDataGrid.DataBind();
    }

    protected void MyDataGrid_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        if (e.Row.RowType == System.Web.UI.WebControls.DataControlRowType.DataRow)
        {
            var rowData = e.Row.DataItem as RowData;
            if (rowData != null)
            {
                DataControlFieldCell txtNewDate = (DataControlFieldCell)e.Row.Cells[COL_NEW_DATE];
                DataControlFieldCell txtNewValue = (DataControlFieldCell)e.Row.Cells[COL_NEW_VALUE];

                e.Row.Cells[1].Text = rowData.Date.HasValue ? rowData.Date.ToString() : string.Empty;
                e.Row.Cells[2].Text = rowData.Value.HasValue ? rowData.Value.Value.ToString() : string.Empty;
                txtNewValue.Enabled = txtNewDate.Enabled = (canEdit & rowData.Editable);

                if (!string.IsNullOrEmpty(rowData.EnteredDateCache))
                    txtNewDate.Text = rowData.EnteredDateCache;

                if (!string.IsNullOrEmpty(rowData.EnteredValueCache))
                    txtNewValue.Text = rowData.EnteredValueCache;

                (e.Row.FindControl("lblStatus") as Label).Text = rowData.InfoText;
                (e.Row.FindControl("lblError") as Label).Text = rowData.ErrorText;
                //(e.Row.FindControl("ErrorPanel") as Panel).Visible = (!string.IsNullOrEmpty(rowData.InfoText) || !string.IsNullOrEmpty(rowData.ErrorText));
            }
        }
    }

    [Serializable()]
    private class RowData
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime? Date { get; set; }
        public double? Value { get; set; }
        public string ValidationRule { get; set; }
        public string ErrorText { get; set; }
        public string InfoText { get; set; }
        public string EnteredDateCache { get; set; }
        public string EnteredValueCache { get; set; }
        public bool Editable { get; set; }
    }
}

public static class StringExtensionMethods
{
    public static bool IsNumber(this String str)
    {
        double Number;
        if (double.TryParse(str, out Number)) return true;
        return false;
    }
}

1 个答案:

答案 0 :(得分:0)

看起来我在RowDataBound事件中错误地设置了控件。这应该是:

if (e.Row.RowType == System.Web.UI.WebControls.DataControlRowType.DataRow)
{
    var rowData = e.Row.DataItem as RowData;
    if (rowData != null)
    {
        DataControlFieldCell txtNewDate = (DataControlFieldCell)e.Row.Cells[COL_NEW_DATE];

        if (!string.IsNullOrEmpty(rowData.EnteredValueCache))
        {
            var txtNewValue = (e.Row.FindControl("txtNewValue") as TextBox);
            txtNewValue.Enabled = txtNewDate.Enabled = (canEdit & rowData.Editable);
            txtNewValue.Text = rowData.EnteredValueCache;
        }

        (e.Row.FindControl("lblStatus") as Label).Text = rowData.InfoText;
        (e.Row.FindControl("lblError") as Label).Text = rowData.ErrorText;
    }
}

从SaveData中删除以下行:

(row.FindControl("lblStatus") as Label).Text = dataItem.InfoText;
(row.FindControl("lblError") as Label).Text = dataItem.ErrorText;

从ValidateDataRow中删除以下内容:

labelInfo.Text = string.Empty;
labelError.Text = string.Empty;

我假设调用以下内容:

DataControlFieldCell txtNewValue = (DataControlFieldCell)e.Row.Cells[COL_NEW_VALUE];
if (!string.IsNullOrEmpty(rowData.EnteredValueCache))
    txtNewValue.Text = rowData.EnteredValueCache;

在设置数据时将从模板列中删除控件(由于未在缓存中清除,这将在无效值上),这就是我在输入无效值时看到错误的原因。