65.9K
CodeProject 正在变化。 阅读更多。
Home

ASPxGridView Excel 样式 - 扩展功能

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (12投票s)

2013年1月3日

CPOL

14分钟阅读

viewsIcon

47726

downloadIcon

6179

继续上一篇的示例, 探索为 ASPxGridView 添加更多功能的进一步方法。

引言

在本篇文章中,我们将继续扩展我们之前的示例。如果您错过了我之前的文章,您可以在这里找到它:ASPxGridView Excel 风格 – 向网格单元格添加注释。本次的主要目标是为网格添加内联编辑功能,以便能够更新导入并管理信用卡的新增、编辑和删除。然而,我们还将添加一些额外的增强功能,以展示网格的潜力并为最终用户带来好处。我不会详细介绍环境和开发需求,如果您感兴趣,可以跳转到我的其他文章。

目录

  1. 所需内容与结果
  2. 创建数据库架构
  3. 更新我们的 DataService
  4. 内联编辑
  5. 管理信用卡
  6. 标记网格中的值
  7. 导出数据
  8. 下载与源代码
  9. 结语

所需内容与结果

附带的项目是用 Visual Studio 2012 编写的。Express 版本应该足够了。此外,您还需要 Microsoft SQL Server 2012,Express 或更高级的版本都可以。数据库包含在项目中,它将由 VS 自动挂载。您可以通过创建自己的数据库并更改 web.config 中的连接字符串来轻松更改此设置。最后但同样重要的是,您需要在您的机器上安装 DevExpress ASP.NET 控件的 12.2.5 版本。如果您有更新的版本,升级项目应该很容易。请参阅这篇文章,了解如何获取 DevExpress 控件以及如何最终升级项目 https://codeproject.org.cn/Articles/483125/ASPxGridView-Excel-style-Adding-notes-to-grid-cell

最终结果应该类似于此,但还有更多内容,所以请一直阅读本文。

Final Example

您也可以 在线查看。在线版本会将数据保留在会话中,因此每次新会话都会重置您插入的所有数据。此外,因此,它被简化了一些。如果您对代码感兴趣,我可以提供给您,只需在评论中询问。

更新我们的 DataService

首先,我们将在 DataService 类中添加几个新方法。我们将编写允许我们更新、添加和删除信用卡实体的方法。

public static bool AddNewCreditCard(string creditCardName)
{
    string query = @"INSERT INTO tbl_CreditCards ([Name]) VALUES (@Name)";

    using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToString(), cn))
        {
            cmd.Parameters.Add("@Name", SqlDbType.NVarChar).Value = creditCardName;

            cn.Open();
            return cmd.ExecuteNonQuery() > 0;
        }
    }
}

public static bool UpdateCreditCard(int creditCardId, string creditCardName)
{
    string query = @"UPDATE tbl_CreditCards SET [Name] = @Name WHERE Id = @creditCardId";

    using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToString(), cn))
        {
            cmd.Parameters.Add("@creditCardId", SqlDbType.Int).Value = creditCardId;
            cmd.Parameters.Add("@Name", SqlDbType.NVarChar).Value = creditCardName;

            cn.Open();
            return cmd.ExecuteNonQuery() > 0;
        }
    }
}

public static string GetCreditCard(int creditCardId)
{
    string query = @"SELECT [Name] FROM tbl_CreditCards WHERE Id = @creditCardId";

    using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToString(), cn))
        {
            cmd.Parameters.Add("@creditCardId", SqlDbType.Int).Value = creditCardId;

            cn.Open();
            return (string)cmd.ExecuteScalar();
        }
    }
}

public static bool RemoveCreditCard(int creditCardId)
{
    string query = @"UPDATE tbl_CreditCards SET [Active] = 0 WHERE Id = @creditCardId";

    using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToString(), cn))
        {
            cmd.Parameters.Add("@creditCardId", SqlDbType.Int).Value = creditCardId;

            cn.Open();
            return cmd.ExecuteNonQuery() > 0;
        }
    }
}

这段代码非常直接。唯一的例外是 RemoveCreditCard 方法,它实际上不会删除项目并级联删除 tbl_Import 中的相关项目,而是仅将信用卡标记为不活动状态。为了使此功能真正起作用,我们需要修改我们在上一篇文章中定义的存储过程 sp_GetCreditCardImports。我不会在这里浪费篇幅,所以只展示您需要添加的内容。

...
WHERE tbl_CreditCards.Active = 1
...

现在轮到编写添加、更新和删除导入的方法了。

public static bool RemoveImport(int creditCardId, int year, byte month)
{
    string query = @"DELETE FROM tbl_Imports WHERE CreditCardId = @creditCardId AND [Year] = @year AND [Month] = @month";

    using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToString(), cn))
        {
            cmd.Parameters.Add("@creditCardId", SqlDbType.Int).Value = creditCardId;
            cmd.Parameters.Add("@year", SqlDbType.Int).Value = year;
            cmd.Parameters.Add("@month", SqlDbType.SmallInt).Value = month;

            cn.Open();
            return cmd.ExecuteNonQuery() > 0;
        }
    }
}

public static bool AddImport(int creditCardId, int year, byte month, decimal import)
{
    string query = @"INSERT INTO tbl_Imports ([CreditCardId], [Year], [Month], [Import])
                VALUES (@creditCardId, @year, @month, @import)";

    using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToString(), cn))
        {
            cmd.Parameters.Add("@creditCardId", SqlDbType.Int).Value = creditCardId;
            cmd.Parameters.Add("@year", SqlDbType.Int).Value = year;
            cmd.Parameters.Add("@month", SqlDbType.SmallInt).Value = month;
            cmd.Parameters.Add("@import", SqlDbType.Money).Value = import;

            cn.Open();
            return cmd.ExecuteNonQuery() > 0;
        }
    }
}

public static bool UpdateImport(int creditCardId, int year, byte month, decimal import)
{
    string query = @"UPDATE tbl_Imports SET [Import] = @import
                WHERE CreditCardId = @creditCardId AND [Year] = @year AND [Month] = @month";

    using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToString(), cn))
        {
            cmd.Parameters.Add("@creditCardId", SqlDbType.Int).Value = creditCardId;
            cmd.Parameters.Add("@year", SqlDbType.Int).Value = year;
            cmd.Parameters.Add("@month", SqlDbType.SmallInt).Value = month;
            cmd.Parameters.Add("@import", SqlDbType.Money).Value = import;

            cn.Open();
            return cmd.ExecuteNonQuery() > 0;
        }
    }
}

就是这样。管理数据不再需要更多代码,我们可以舒适地开始处理界面了。

内联编辑

编辑 ASPxGridView 非常简单。有几种方法可以达到相同的结果。如果您对此感兴趣,可以查看此 网格编辑 - 编辑模式。一整套针对不同列类型的编辑器可以轻松修改列值。但我们将在接下来的几行中看到一些技巧。

为了开始编辑某个行,我们可以选择开始此操作的方式。例如,您可以在网格本身中添加一个命令按钮,或者简单地开始编辑,比如说,双击一行。还有许多其他模式,您有丰富的客户端和服务器端方法来开始编辑一行或多行。但我会让你自己去发现它们并创造新的。我们将使我们的行在双击时可编辑,为了实现这一点,我们将编写一个函数(JavaScript),它将与网格的客户端事件 RowDblClick 相关联。让我们看看它是如何完成的。

首先,我们将在 ASPX 文件中,在网格定义内添加一个名为 ClientSideEvents 的新部分(就像我们为其他控件所做的那样),它看起来会像这样

<ClientSideEvents RowDblClick="gvPrevisions_RowDblClick" />

然后我们将编写 JavaScript 方法本身

function gvPrevisions_RowDblClick(s, e) { s.StartEditRow(e.visibleIndex); }

就是这么简单!只需调用网格上定义的 StartEditRow JavaScript 方法(在此我使用的是 s 而不是 ClientInstanceName 来访问网格方法,但这里基本相同),并将我们打算进入编辑模式的行的可见索引传递过去。请注意,此事件将通过函数参数提供可见索引,该参数将指示用户双击的行。如果您现在运行示例并双击一行,您应该会看到类似这样的内容

Grid in Edit Mode

即使这看起来不错且简单,您可能已经注意到字段不可编辑。这是因为我们没有将我们的网格绑定到有效且定义良好的数据源。因此,我们的编辑控件处于只读状态。您可以在 Devexpress 支持网站上找到一些关于此行为及其原因的讨论。好消息是我们可以轻松解决这个问题。定义以下服务器端事件

protected void gvImports_CellEditorInitialize(object sender, ASPxGridViewEditorEventArgs e)
{
    if (e.Column.FieldName != "Name" || e.Column.FieldName != "Total")
    {
        e.Editor.ReadOnly = false;

        ASPxTextBox box = e.Editor as ASPxTextBox;
        box.HorizontalAlign = HorizontalAlign.Right;
        box.MaskSettings.Mask = "$ <0..99999g>.<00..99>";
        box.MaskSettings.IncludeLiterals = MaskIncludeLiteralsMode.DecimalSymbol;
        box.ValidationSettings.ErrorDisplayMode = ErrorDisplayMode.ImageWithTooltip;
        box.ValidationSettings.Display = Display.Dynamic;
        box.ClientSideEvents.KeyDown = "gvImports_Cell_KeyDown";

        if (e.Column.FieldName == "1")
            box.Focus();
    }
}

我们在这里定义的内容是检查即将进入编辑模式的单元格是否是我们想要编辑的单元格(不是 Name 和 Total 列),如果是,我们将设置一些属性。首先,我们将单元格指示为只读,然后我们将控件转换为 ASPxTextBox(这是这些列的正确控件),并设置几个参数以使编辑看起来恰当。最重要的事情是,我们为每个编辑单元格分配一个客户端事件 KeyDown,我们将在其中管理持久化。让我们看看这个重要方法是如何定义的。

function gvImports_Cell_KeyDown(s, e) {
    switch (e.htmlEvent.keyCode) {
        case 13:
            ASPxClientUtils.PreventEventAndBubble(e.htmlEvent);
            gvImports.UpdateEdit();
            break;
        case 27:
            gvImports.CancelEdit();
            break;
    }
}

如果在编辑单元格时按下了 Enter (13) 键,我们将阻止此操作的默认行为并调用网格上的 UpdateEdit 方法。如果用户按下 Escape 键 (27),我们将通过调用 CancelEdit 方法来退出编辑模式。一旦调用了 UpdateEdit,将生成一个回发,并执行 RowUpdating 服务器端方法,在其中我们需要处理数据的持久化。我们需要将以下方法附加到 RowUpdating 事件

protected void gvImports_RowUpdating(object sender, ASPxDataUpdatingEventArgs e)
{
    // copy OrderedDictionaries to Hashtables
    // for easier data manipulations
    Hashtable newValues = new Hashtable();
    Hashtable oldValues = new Hashtable();

    foreach (DictionaryEntry item in e.NewValues)
    {
        // if value == 0 then do not insert any value
        newValues.Add(Convert.ToByte(item.Key), (decimal)item.Value == 0 ? null : (decimal?)item.Value);
    }

    foreach (DictionaryEntry item in e.OldValues)
    {
        // transform DbNull to null
        oldValues.Add(Convert.ToByte(item.Key), item.Value == DBNull.Value ? null : (decimal?)item.Value);
    }

    int creditCardID = (int)e.Keys[0];
    int year = (int)cbYears.SelectedItem.Value;

    for (byte i = 1; i <= newValues.Count; i++)
    {
        if ((decimal?)newValues[i] != (decimal?)oldValues[i])
        {
            if (newValues[i] == null)
            {
                // The value was removed
                // so remove the record from table
                DataService.RemoveImport(creditCardID, year, i);
                continue;
            }

            if (oldValues[i] == null)
            {
                // The value was inexistant
                // add record to the table
                DataService.AddImport(creditCardID, year, i, (decimal)newValues[i]);
                continue;
            }

            // it's a change
            // update the record in the table
            DataService.UpdateImport(creditCardID, year, i, (decimal)newValues[i]);
        }
    }

    e.Cancel = true;
}

此事件中发生的是,会发送一个更新行的请求。我们将以 OrderedDictionaries 的形式接收旧值(感兴趣的更新列)和用户选择的新值,这些值由我们的事件参数中的两个属性公开。这两个属性逻辑上称为 NewValues 和 OldValues。

由于 OrderedDictionaries 处理起来不太方便,我将创建两个 Hashtable,并将 OrderedDictionaries 的数据复制到 Hashtable 中。在复制时,我将将任何值转换为可空的 decimal,如果值为零,我将插入 null 而不是该值。接下来是检索其他必要数据,例如信用卡 ID(存储为编辑行的键,并通过事件参数公开)和当前年份,我们将从我们的复选框中获取。

现在我们将逐个处理我们所有的列值。如果新值 Hashtable 中特定列的值与旧值 Hashtable 中的值不匹配,则意味着该列的值已更改,我们需要确定发生了哪种类型的更改,然后才能决定采取什么行动。如果新值为 null,则意味着我们需要从数据库中删除此项,因此我们将调用我们之前创建的相应方法并按要求传递所有参数。如果旧值为 null,则意味着我们的数据库中没有此项的条目,因此我们将通过专用方法添加新项。最后,如果值同时存在于两个 Hashtable 中,我们将需要将值更新为新的。

最后,我们将事件参数的 Cancel 属性设置为 false。e.Cancel = true 允许我们阻止 ASPxGridView 尝试通过控件本身更新数据。还有一个小细节,在持久化数据后,我们应该指示网格编辑已结束,以便它可以恢复到非编辑模式。实现这一点最好的方法是在每次绑定网格时从服务器端调用 CancelEdit 方法(即使有时不需要,它也不是很大的开销,并确保每次更新数据时我们都确保网格不是编辑模式)。因此,我们的 gvImports_DataBinding 事件将更改为如下所示

protected void gvImports_DataBinding(object sender, EventArgs e)
{
    int yearToBind = DateTime.Now.Year;

    if (cbYears.SelectedItem != null)
        yearToBind = Convert.ToInt32(cbYears.SelectedItem.Value);

    gvImports.DataSource = DataService.GetAllByDetail(yearToBind);
    gvImports.CancelEdit();
}

恭喜,您现在可以编辑此网格的值了!很流畅,不是吗?微笑 | :)

管理信用卡

作为下一个要求,我们将添加添加新信用卡、更新现有信用卡名称或删除它们的功能。至于注释,我们将添加一个新的弹出窗口来帮助我们执行这些操作。定义弹出窗口非常直接。

<dx:ASPxPopupControl ID="popupAddCreditCard"  runat="server" AllowDragging="True" ClientInstanceName="popupAddCreditCard"
    HeaderText="Add new Credit Card" Modal="True" PopupHorizontalAlign="Center"
    PopupVerticalAlign="Middle" Width="300px" CloseAction="CloseButton">
    <ContentCollection>
        <dx:PopupControlContentControl ID="PopupControlContentControl4"  runat="server" SupportsDisabledAttribute="True">
            <table border="0" style="width:100%">
                <tr>
                    <td>
                        <dx:ASPxLabel ID="lbltxtAddCreditCardName"  runat="server" Text="Credit Card Name">
                        </dx:ASPxLabel>
                        <dx:ASPxTextBox ID="txtAddCreditCard" ClientInstanceName="txtAddCreditCard"  runat="server" Width="205px">
                            <ClientSideEvents KeyDown="txtAddCreditCard_KeyDown" />
                            <ValidationSettings Display="Dynamic" ValidationGroup="AddCreditCard" ErrorTextPosition="Bottom">
                                <RequiredField ErrorText="Name is a required field!" IsRequired="True" />
                            </ValidationSettings>
                        </dx:ASPxTextBox>
                    </td>
                </tr>
            </table>
            <hr />
            <table border="0">
                <tr>
                    <td>
                        <dx:ASPxButton ID="btnAddNewCreditCard"  runat="server" Text="Add" Width="100px" AutoPostBack="False"
                            UseSubmitBehavior="False" ValidationGroup="AddCreditCard">
                            <ClientSideEvents Click="btnAddNewCreditCard_Click" />
                        </dx:ASPxButton>
                    </td>
                    <td>
                        <dx:ASPxButton ID="btnCancelNewCreditCard"  runat="server" Text="Cancel" Width="100px"
                            AutoPostBack="False" UseSubmitBehavior="False" CausesValidation="False">
                            <ClientSideEvents Click="btnCancelNewCreditCard_Click" />
                        </dx:ASPxButton>
                    </td>
                </tr>
            </table>
        </dx:PopupControlContentControl>
    </ContentCollection>
</dx:ASPxPopupControl>

与添加注释的弹出窗口一样,我们为添加信用卡弹出窗口定义了类似的结构。主要区别在于信用卡名称的验证。我们将该字段定义为必填项,并将执行客户端验证。如果为空,将出现错误消息。现在我将向您展示附加到我们客户端事件的所有三个新脚本的代码。

function PersistCreditCard() {
    var isValid = ASPxClientEdit.ValidateEditorsInContainer(null, 'AddCreditCard');

    if (isValid) {
        var command = 'AddNewCreditCard';

        if (currentIsCreditCardEdit)
            command = 'EditNewCreditCard';

        popupAddCreditCard.Hide();
        gvImports.PerformCallback(command + '|' + currentVisibleIndex);
    }
}

function txtAddCreditCard_KeyDown(s, e) {
    switch (e.htmlEvent.keyCode) {
        case 13:
            ASPxClientUtils.PreventEventAndBubble(e.htmlEvent);
            PersistCreditCard();
            break;
        case 27:
            popupAddCreditCard.Hide(); txtAddCreditCard.SetText('');
            break;
    }
}

function btnAddNewCreditCard_Click(s, e) { PersistCreditCard(); }
function btnCancelNewCreditCard_Click(s, e) { popupAddCreditCard.Hide(); txtAddCreditCard.SetText(''); }

技术与上一个示例中用于编辑和添加导入的技术相同。如果执行了客户端验证并且用户输入了有效条目,我们将对网格执行回调(因此它也可以自动刷新)。在回调中,我们将指定我们正在执行的操作是编辑还是添加,如果是编辑,则传递选定的行索引。我们现在缺少的是用于持久化新添加值的服务器端代码。为了实现这一点,我们将修改之前定义的 gvImports_CustomCallback 事件(它定义在上一篇文章中,您可以在此处找到)。它看起来会像这样

protected void gvImports_CustomCallback(object sender, ASPxGridViewCustomCallbackEventArgs e)
{
    // if no parameter is passed from cliend side, do no process
    if (string.IsNullOrEmpty(e.Parameters))
        return;

    int year = (int)cbYears.SelectedItem.Value;

    string[] values = e.Parameters.Split('|');
    string command = values[0];
    int visibleIndex = 0;
    int creditCardID = 0;

    if (values.Length > 1)
    {
        visibleIndex = int.Parse(values[1]);
        if (command != "ChangeYear")
            creditCardID = (int)gvImports.GetRowValues(visibleIndex, "Id");
    }

    byte month = 0;

    if (values.Length > 2)
        month = byte.Parse(values[2]);

    switch (command)
    {
        case "AddNote":
            DataService.AddNewNote(creditCardID, year, month, txtNote.Text);
            break;
        case "DeleteNote":
            DataService.DeleteNote(creditCardID, year, month);
            break;
        case "EditNote":
            DataService.UpdateNote(creditCardID, year, month, txtNote.Text);
            break;
        case "ChangeYear":
            gvImports.DataSource = DataService.GetAllByDetail(Convert.ToInt32(values[1]));
            break;
        case "AddCreditCard":
            DataService.AddNewCreditCard(txtAddCreditCard.Text);
            break;
        case "EditCreditCard":
            DataService.UpdateCreditCard(creditCardID, txtAddCreditCard.Text);
            break;
        case "DeleteCreditCard":
            DataService.DeleteCreditCard(creditCardID);
            break;
    }

    gvImports.DataBind();
}

根据收到的命令,我们将分析作为参数传递的其他值,并调用与当前操作关联的服务方法。最后,我们将重新绑定网格,以便可以看到新添加的值。简单直接,不是吗?

我将再次提到我用于编辑的相同技巧,它在这里的原因与我在解释注释编辑时解释的原因相同,但这次是在添加/编辑信用卡弹出窗口上进行的。在编辑时,我们将检索选定的信用卡名称,并将文本框填充为该获取的值。

protected void popupAddCreditCard_WindowCallback(object source, PopupWindowCallbackArgs e)
{
    string[] values = e.Parameter.Split('|');

    int visibleIndex = int.Parse(values[0]);
    int creditCardID = (int)gvImports.GetRowValues(visibleIndex, "Id");

    txtAddCreditCard.Text = DataService.GetCreditCard(creditCardID);
    txtAddCreditCard.Focus();
}

标记网格中的值

在我编写上一个需求的代码时,我产生了一个想法。为什么不让用户有机会突出显示小于某个金额的值呢?嗯,这是一个简单的任务,也许可以激发您以其他方式扩展此示例。让我们看看如何实现类似的功能。

首先,我们需要创建必要的界面,为了实现这一点,我们将在 aspx 文件中定义以下控件。在 GridView 定义之后,添加以下内容

<table style="width: 100%;">
    <tr>
        <td style="width: 100%;"></td>
        <td>
            <dx:ASPxLabel ID="lblMark"  runat="server" Text="Mark values lower or equal to:" Width="180px">
            </dx:ASPxLabel>
        </td>
        <td>
            <dx:ASPxTextBox ID="txtMarkValue" ClientInstanceName="txtMarkValue"  runat="server" Width="100px" HorizontalAlign="Right">
                <MaskSettings Mask="$ <0..99999g>.<00..99>" IncludeLiterals="DecimalSymbol" />
                <ValidationSettings Display="Dynamic">
                </ValidationSettings>
            </dx:ASPxTextBox>

        </td>
        <td>
            <dx:ASPxColorEdit ID="ceMark" ClientInstanceName="ceMark"  runat="server" AllowUserInput="False" Width="100px" Color="#C0C0C0"></dx:ASPxColorEdit>
        </td>
        <td>
            <dx:ASPxButton ID="btnMark"  runat="server" Text="Mark" AutoPostBack="False"
                UseSubmitBehavior="False" CausesValidation="False">
                <ClientSideEvents Click="btnMark_Click" />
            </dx:ASPxButton>
        </td>
        <td>
            <dx:ASPxButton ID="btnClear"  runat="server" Text="Clear" AutoPostBack="False"
                UseSubmitBehavior="False" CausesValidation="False">
                <ClientSideEvents Click="btnClear_Click" />
            </dx:ASPxButton>
        </td>
    </tr>
</table>

如您所见,除了我用来实现特定布局的表之外,我还添加了一个文本框,用户将在其中指定我们将用作突出显示单元格的上限的值。我还使用了一个 ASPxColorEdit,以便用户可以选择他喜欢的标记单元格的颜色。一个清除和确认按钮也在此处。然后,我们将以下代码与相关的客户端事件(在本例中仅为按钮单击)关联

function btnMark_Click(s, e) {
    gvImports.PerformCallback('Mark');
}

function btnClear_Click(s, e) {
    txtMarkValue.SetText('');
    ceMark.SetColor('#C0C0C0');
    gvImports.PerformCallback('Mark');
}

在单击清除按钮的情况下,我将把颜色选择器和值文本框的值重置为其默认值,然后执行回调,以便如果之前设置了任何值,它可以恢复到默认值。在单击标记按钮的情况下,我将对网格执行回调并指定 'Mark' 命令。回调执行并且 gvImports_CustomCallback 事件被触发后,您会看到我没有处理 Mark 命令。这是因为此刻没有必要。唯一重要的是重新绑定网格。相反,在 gvImports_HtmlDataCellPrepared 方法的末尾,我将添加以下内容

protected void gvImports_HtmlDataCellPrepared(object sender, ASPxGridViewTableDataCellEventArgs e)
{
    ...

    decimal markValue = 0;
    decimal.TryParse(txtMarkValue.Text, out markValue);

    if (markValue != 0 && (decimal)e.CellValue <= markValue)
    {
        e.Cell.BackColor = ceMark.Color;
    }
}

通过这一点,我将检查是否设置了标记值,如果它是一个有效的数字,我将检查当前正在处理的单元格。如果单元格值小于或等于我们文本框中指定的值,我将更改该单元格的背景颜色并将其设置为我们的颜色选择器中指定的颜色。这表明您可以如何轻松地为网格添加额外功能。

导出数据

这一切都很好,但如果无法检索数据并本地持久化它或/以及编辑它以进行演示或将其导入我们的会计软件,那将是无用的。与 Grid 一起,DevExpress 为我们提供了另一个控件来处理导出过程。它易于设置,并且结果很棒!让我们看看如何做到。

首先,我们需要将此控件添加到我们的页面中。从工具箱中拖放 ASPxGridViewExporter 控件。然后编辑您的 aspx 代码并进行匹配

<dx:ASPxGridViewExporter ID="gridExport" GridViewID="gvImports"
    PaperKind="A4" Landscape="True"  runat="server">
</dx:ASPxGridViewExporter>

您已经注意到我们指定了一些属性。为了指示我们希望从中导出数据的网格,我们需要指定 GridViewID 属性,正如其名称所示,将 id 设置为网格视图 ID。我们还将指定 PaperKind 和 Landscape 属性以获得正确的导出格式。

现在我们需要一些按钮来请求以不同格式导出。ASPxGridViewExporter 可以默认处理多种格式。有关更多详细信息,请查看以下链接 导出数据 - 导出到 PDF、XLS、XLSX 和 RTF。我们将它们添加到与年份选择器相同的行中。最终代码看起来会像这样

<table border="0" style="width: 100%;">
    <tr style="vertical-align: middle;">
        <td style="padding-removed 5px;">
            <dx:ASPxLabel ID="lblSelectedYear"  runat="server" Text="Selected Year:" Width="85px" AssociatedControlID="cbYear"></dx:ASPxLabel>
        </td>
        <td>
            <dx:ASPxComboBox ID="cbYears"  runat="server" ValueType="System.Int32" TextField="Value" ValueField="Key" Width="70px" DataSourceID="odsYears"  önDataBound="cbYears_DataBound" HorizontalAlign="Center">
                <ClientSideEvents SelectedIndexChanged="cbYears_SelectedIndexChanged" />
                <ItemStyle HorizontalAlign="Center" />
            </dx:ASPxComboBox>
        </td>
        <td style="width: 100%;">
            <table style="width: 100%;">
                <tr>
                    <td style="width: 100%;"></td>
                    <td style="padding-removed 170px;">
                        <dx:ASPxButton ID="btnExportExcel"  runat="server" UseSubmitBehavior="False" CausesValidation="False"
                            ImageSpacing="0px" EnableTheming="False" EnableDefaultAppearance="False" ToolTip="Export to Excel"
                             önClick="btnExportExcel_Click" Cursor="pointer">
                            <Image Url="~/images/excel.png">
                            </Image>
                        </dx:ASPxButton>
                    </td>
                    <td>
                        <dx:ASPxButton ID="btnExportPdf"  runat="server" UseSubmitBehavior="False" CausesValidation="False"
                            ImageSpacing="0px" EnableTheming="False" EnableDefaultAppearance="False" ToolTip="Export to Pdf"
                             önClick="btnExportPdf_Click" Cursor="pointer">
                            <Image Url="~/images/pdf.png">
                            </Image>
                        </dx:ASPxButton>
                    </td>
                    <td>
                        <dx:ASPxButton ID="btnExportCsv"  runat="server" UseSubmitBehavior="False" CausesValidation="False"
                            ImageSpacing="0px" EnableTheming="False" EnableDefaultAppearance="False" ToolTip="Export to Csv"
                             önClick="btnExportCsv_Click" Cursor="pointer">
                            <Image Url="~/images/csv.png">
                            </Image>
                        </dx:ASPxButton>
                    </td>
                </tr>
            </table>
        </td>
    </tr>
</table>

我们添加了三个按钮,并设置了一些默认的、大家熟悉的属性,如图像和文本。您应该注意到这里是 UseSubmitBehavior 属性设置为 false。这样,按钮就不会导致浏览器提交机制。但是,仍然会执行回发。因此,对于每个按钮,我们将在服务器端定义事件处理程序,它看起来会像这样

#region Export
protected void btnExportExcel_Click(object sender, EventArgs e)
{
    gridExport.FileName = string.Format("{0} {1}", "Credit card imports for year: ", cbYears.SelectedItem.Value);
    gridExport.WriteXlsToResponse();
}

protected void btnExportPdf_Click(object sender, EventArgs e)
{
    gridExport.FileName = string.Format("{0} {1}", "Credit card imports for year: ", cbYears.SelectedItem.Value);
    gridExport.WritePdfToResponse();
}

protected void btnExportCsv_Click(object sender, EventArgs e)
{
    gridExport.FileName = string.Format("{0} {1}", "Credit card imports for year: ", cbYears.SelectedItem.Value);
    gridExport.WriteCsvToResponse();
}
#endregion

在单击每个定义的按钮时,我们将文件名设置为更有意义的字符串,并调用 ASPxGridViewExporter 上的相应方法。就是这样!检查结果!

Final Example

最终结果令人满意。借助 DevExpress 控件提供的可能性,我们只需付出一点努力就添加了一些出色的功能。

下载与源代码

您可以在此处下载我的项目源代码 此处。您可以在此处找到所需控件的试用版 此处

结语

如果我没有说明清楚,或者我理所当然地解释了一些内容,请随时评论,我将尽我最大的努力提供适当的解释。我没有详细介绍所有代码,因为大部分内容都在上一篇文章中涵盖了,ASPxGridView Excel 风格 – 向网格单元格添加注释。如果有人感兴趣,我将进一步扩展这个例子,并向您介绍我们可以轻松使用 DevExpress 控件实现的其他一些不错的特性。敬请关注,祝您编程愉快!

© . All rights reserved.