使用 CustomButtonCallback 克隆引用类型属性

Clone reference type properties with CustomButtonCallback

我正在尝试关注 the DevExpress documentation example on cloning rows。基础知识很容易掌握,但是当您尝试克隆值类型以外的属性时,事情就变得棘手了。

第一次尝试/设置重现:

我已经将我的真实场景缩小到一个小的(大概)问题重现。以下是新创建的 ASP.NET 2.0 webforms 应用程序的全部内容。首先,假设这些域对象(在这里也可以兼作 DTO):

public class Qualification
{
    public long Id { get; set; }
    public string Title { get; set; }
}

public class Person
{
    public long Id { get; set; }
    public string Name { get; set; }
    public Qualification Qualification { get; set; }
}

然后有两个 quick 'n dirty 数据源 类 像这样:

public class QualificationOds
{
    public static List<Qualification> Qualificiations = new List<Qualification>
    {
        new Qualification { Id = 1, Title = "Doctore" }
    };

    public List<Qualification> GetRecords()
    {
        return Qualificiations;
    }
}

public class PeopleOds
{
    public List<Person> GetRecords()
    {
        return new List<Person> 
        {
            new Person
            {
                Id = 1, 
                Name = "John Doe", 
                Qualification = QualificationOds.Qualificiations[0] 
            }
        };
    }
}

default.aspx 页面将首先 dx 注册到 DevExpress 命名空间:

<%@ Register Assembly="DevExpress.Web.ASPxGridView.v11.1, Version=11.1.11.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" Namespace="DevExpress.Web.ASPxGridView" TagPrefix="dx" %>
<%@ Register Assembly="DevExpress.Web.ASPxEditors.v11.1, Version=11.1.11.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" Namespace="DevExpress.Web.ASPxEditors" TagPrefix="dx" %>

并且有一个包含此代码的单一表格:

<asp:ObjectDataSource runat="server" ID="peopleDataSource" TypeName="DevxTest.PeopleOds" SelectMethod="GetRecords" />
<asp:ObjectDataSource runat="server" ID="qualificationsDataSource" TypeName="DevxTest.QualificationOds" SelectMethod="GetRecords" />

<dx:ASPxGridView 
    runat="server"
    id="grvPeople"
    KeyFieldName="Id"
    DataSourceID="peopleDataSource"
    OnCustomButtonCallback="grid_CustomButtonCallback"
    OnInitNewRow="grid_InitNewRow">
    <Columns>
        <dx:GridViewCommandColumn>
            <EditButton Visible="true" />
            <CustomButtons>
                <dx:GridViewCommandColumnCustomButton ID="Clone" Text="Clone" />
            </CustomButtons>
        </dx:GridViewCommandColumn>
        <dx:GridViewDataTextColumn FieldName="Name" />
        <dx:GridViewDataComboBoxColumn FieldName="Qualification.Id" >
            <PropertiesComboBox 
                DataSourceID="qualificationsDataSource"
                TextField="Title"
                ValueField="Id"
                ValueType="System.Int64" />
        </dx:GridViewDataComboBoxColumn>
    </Columns>
</dx:ASPxGridView>

后面的代码包含将一行克隆到新编辑表单的代码,基于上述 DevExpress 示例,如下所示:

private string[] fieldsToCopy = { "Name", "Id" };
private Hashtable valuesToClone;

protected void grid_CustomButtonCallback(object sender, ASPxGridViewCustomButtonCallbackEventArgs e)
{
    if (e.ButtonID != "Clone") return;

    valuesToClone = new Hashtable();

    foreach (var fieldName in fieldsToCopy)
    {
        valuesToClone[fieldName] = grvPeople.GetRowValues(e.VisibleIndex, fieldName);
    }

    grvPeople.AddNewRow();
}

protected void grid_InitNewRow(object sender, DevExpress.Web.Data.ASPxDataInitNewRowEventArgs e)
{
    if (valuesToClone == null) return;

    foreach (string fieldName in fieldsToCopy)
    {
        e.NewValues[fieldName] = valuesToClone[fieldName];
    }
}

这适用于 Name 属性,但不适用于 Qualification 属性。在页面上是这样显示的:

我也试过 "Qualification.Id""Qualification" 作为要复制的字段名称,但没有成功。无论如何,我很确定它应该像我的示例中那样 "Id",因为调试器仅针对这种情况显示正确的 GetRowValues return 值。

第二次尝试/自定义代码来处理下拉菜单:

所以我想我需要一些自定义代码来为下拉菜单设置 NewValue。首先,我为 Qualification 列创建一个 EditItemTemplate,如下所示:

<EditItemTemplate>
    <dx:ASPxComboBox 
        runat="server" 
        ID="qualificationCombo" 
        DataSourceID="qualificationsDataSource"
        TextField="Title"
        ValueField="Id"
        ValueType="System.Int64" />
</EditItemTemplate>

然后我将此代码添加到 grid_InitNewRow:

// Attempt to set the combo box to a value:
var column = grvPeople.Columns["Qualification"] as GridViewDataColumn;
var comboBox = grvPeople.FindEditRowCellTemplateControl(column, "qualificationCombo") as ASPxComboBox;
var item = comboBox.Items.FindByValue(valuesToClone["Id"]);
item.Selected = true;

而且有效!但是,现在 Name 不再被克隆。它看起来像这样:

我不知道为什么,但是添加的代码有一个 side-effect 导致 NewValues setter 方法失败。

底线:

底线相当简单:如何将 DevExpress 示例更改为也可以克隆引用类型属性的示例?


PS。我也 cross-posted my question on the DevExpress support forum.

您提到 "Qualification.Id" 不起作用,但您很可能在 EditItemTemplate 存在时尝试过。如果没有这样的模板,那么该代码实际上可以工作。

为了完整起见,将 fieldsToCopy 行代码更改为:

private string[] fieldsToCopy = { "Name", "Qualification.Id" };

并且确保没有EditItemTemplate,然后就可以了。

或者,如果您出于某种原因无法使用 EditItemTemplate,可以使用 "solution" 来确保该模板中的 ComboBox 绑定到克隆的值。确保使用以下代码挂接到 ComboBox 上的 OnDataBound

protected void qualificationCombo_DataBound(object sender, EventArgs e)
{
    var column = grvPeople.Columns["Qualification"] as GridViewDataColumn;
    var comboBox = grvPeople.FindEditRowCellTemplateControl(column, "qualificationCombo") as ASPxComboBox;
    var item = comboBox.Items.FindByValue(valuesToClone["Id"]);
    item.Selected = true;
}

这将正确地将 ComboBox 设置为克隆的值,同时还将 e.NewValues 用于普通字段的方法保持不变。