【问题标题】:Generics and User Controls泛型和用户控件
【发布时间】:2013-12-13 20:00:14
【问题描述】:

我是泛型的新手,所以我可能只是这样做完全错误,但这里是: 我有一小部分控件,我将它们组合成一个用户控件。看起来它会按预期工作,但 uc 需要是通用的。在 uc 中,我有几个列表框(和一些按钮)。那些列表框和 uc 必须能够接受任意数量的通用列表。 (即List<user>List<group>List<platform> 等,然后将更改返回到该通用列表。我想我可以接受通用列表,但不知道如何取回通用列表。

控制:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="AddRemoveList.ascx.cs" Inherits="NSDC_Supply.UserControls.AddRemoveList" %>
<table>
    <tr>
        <td><asp:ListBox ID="ListBox1" Width="150px" Height="100px" SelectionMode="Single" runat="server" /></td>
        <td><asp:Button ID="AddUser" runat="server" Text="<<" CausesValidation="false" onclick="AddUser_Click" /></td>
        <td><asp:Button ID="RemoveUser" runat="server" Text=">>" CausesValidation="false" onclick="RemoveUser_Click" /></td>
        <td><asp:ListBox ID="ListBox2" Width="150px" Height="100px" SelectionMode="Single" runat="server" /></td>
    </tr>
</table>

代码背后:

public partial class AddRemoveList : System.Web.UI.UserControl 
{
    public T EditedList { get; set; }  //Obviously this won't work

    protected void Page_Load(object sender, EventArgs e)
    {}

    public void FillListBox<T>(T collection1, T collection2, string val, string text)
    {
        ListBox1.DataSource = collection1;
        ListBox1.DataTextField = text;
        ListBox1.DataValueField = val;
        ListBox1.DataBind();

        ListBox2.DataSource = collection2;
        ListBox2.DataTextField = text;
        ListBox2.DataValueField = val;
        ListBox2.DataBind();
    }

    protected void AddItem_Click(object sender, EventArgs e)
    {
        if (ListBox2.SelectedIndex > -1)
        {
            ListBox1.Items.Add(ListBox2.SelectedItem);
            EditedList.Add(ListBox2.SelectedItem);
            ListBox2.Items.RemoveAt(ListBox2.SelectedIndex);
            ListBox1.ClearSelection();
        }
    }

    protected void RemoveItem_Click(object sender, EventArgs e)
    {
        if (ListBox1.SelectedIndex > -1)
        {
            ListBox2.Items.Add(ListBox1.SelectedItem);
            EditedList.Remove(ListBox2.SelectedItem);
            ListBox1.Items.RemoveAt(ListBox1.SelectedIndex);
            ListBox2.ClearSelection();
        }
    }

}

所以首先我填充 uc:

private void GridView1_SelectedIndexChanged(...)
{
    List<user> myUsers = selectedGroup.users.ToList();
    List<user> masterUserList = uc.GetAllUsers().OrderBy(g => g.full_name).ToList();

    UserControls.AddRemoveList ucarl = ((UserControls.AddRemoveList)FormView1.Controls[0].FindControl("ucAddRemoveList"));
    ucarl.FillListBox(myUsers, masterUserList, "id", "full_name");
}

然后在 uc 中编辑列表框后,我尝试更新实体:

protected void FormView1_ItemUpdate(Object sender, FormViewUpdateEventArgs e)
{
    network_group editedGroup = new network_group();
    editedGroup.id = Convert.ToInt32(((Label)selectedControls.FindControl("lblid")).Text);
    editedGroup.full_name = ((TextBox)selectedControls.FindControl("txtfull_name")).Text;
    editedGroup.distribution_list_email = ((TextBox)selectedControls.FindControl("txtemail")).Text;
    UserControls.AddRemoveList ucarl = ((UserControls.AddRemoveList)FormView1.Controls[0].FindControl("ucAddRemoveList"));

    editedGroup.users.Add(ucarl);   //psudocode here
    ctx.SaveChanges();
}

如果我删除对 EditedList 的所有调用,它可以工作,但我如何从 uc 中获取通用 EditedList?

更新: 我得到的答案似乎是他们试图解决一个不同的问题……我想。所以这是我正在尝试做的更新:

我目前在一个页面上有两个 ListBox。我将从 EntityFramework 实体填充的对象列表绑定到 ListBoxes。 ListBox1 是我当前正在查看的实体对象的实体对象的子集合。 ListBox2 是子实体对象中所有可能条目的集合。因此,对于这个示例,我正在查看 Group 实体的一个实例。 ListBox1 是与该 Group 关联的所有用户,ListBox2 是公司中的所有用户。

在列表框之间是添加/删除按钮,用于在两者之间来回移动项目。 (所以我可以使用 AddItem_Click 和 RemoveItem_Click 事件将用户添加到组/从组、设备到/从站点等中添加或删除。然后在 FormView1_ItemUpdate 事件中,我获取 ListBox1 中的集合(已编辑的用户列表)并将其保存到数据库。

但是因为我将执行几乎完全相同的功能 9 次以上,所以我试图将 4 个控件抽象为一个 uc。通过这种方式,我可以传入任何两个 List 集合,我想为任何给定对象绑定的文本和值字段,进行添加/删除,然后返回修改后的 List。

所以我的问题不是关于从一个 ListBox 中获取项目到另一个,而是关于在我完成后从 ListBox1 中获取已编辑的 List 集合。这有帮助吗?它会改变任何答案吗?对不起,如果我太含糊了。

【问题讨论】:

  • 这也不起作用,因为EditedList 将在每个页面请求上被清除。请记住,网络是无状态的。
  • 嗯,我没想到。除了单个回发之外,我不需要跟踪 EditedList。一旦 FormView1_ItemUpdate(...) 被触发,我就会记录更改并转到另一个页面。

标签: c# asp.net generics user-controls generic-list


【解决方案1】:

我能想到的最简单的方法是添加一个为您执行此操作的扩展方法。如果您的所有对象都实现了一个具有idfullname 属性的接口,例如:

public interface IListableObject
{
    string id { get; set; }
    string full_name { get; set; }
}

然后你可以为ListBox控件写一个扩展方法:

public static List<T> GetListFromListBoxControl<T>(this ListBox source) where T: IListableObject, new()
{
    var returnValue = new List<T>();

    foreach(var item in source.Items)
    {
        returnValue.Add(new T() {
            id = item.Value,
            full_name = item.Text    
        });
    }

    return returnValue;
}

将此视为伪代码(我尚未对其进行测试),但这个想法是合理的。然后您就可以直接从 ListBox 中获取您的列表:

var newList_of_users = ListBox.GetListFromListBoxControl<user>();

如果接口和扩展方法不适合您,那么您总是可以在某处编写一个静态方法并在一对一的基础上进行解析。

public static List<user> GetUsersFromListBox(ListBox source)
{
    var returnValue = new List<user>();

    foreach(var item in source.Items)
    {
        returnValue.Add(new user() {
            id = item.Value,
            full_name = item.Text    
        });
    }

    return returnValue;
}

【讨论】:

  • 不幸的是,并非所有对象都有“全名”。 List 有一个“主机名”,List 将使用一个“mail_code”......因此我参数化了 uc 列表框的文本和值。在给定的 uc 实例中,两个列表框将始终包含相同类型的集合,但集合将具有不同的字段,具体取决于 uc 的实例。
  • @Jeremy:这就是接口和抽象基类可以真正帮助你的地方。即使没有接口,这仍然可以通过扩展方法来完成,您只需要为每个单独的对象提供一个扩展方法。泛型的重点是减少相似对象的代码膨胀。如果您的对象不相似,那么泛型对您没有任何用处。
  • 我想知道我的问题是我措辞不当还是我们不在同一页面上。我已经有了控件和功能,它们基本上可以在 5 个不同的页面上完成您在第二个示例中所描述的内容。我有在两个 ListBox 之间来回切换的功能。我编辑了 OP,希望这更有意义。
  • @Jeremy:如果你想使用泛型,你的对象必须有这些共同的字段,并且它们必须实现一个接口或一个基类。仅使用 5 个随机对象是行不通的,除非您想使用类型名称作为表达式抛出一个 switch 语句(不要这样做)。不一定是这些字段,但至少是一个相关的文本/值字段,并且可能是一种将这些字段转换为正常字段的方法。
  • 好吧,我不喜欢这个答案,但它仍然是一个答案。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-12
  • 2016-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多