创建一个 ASP.NET 控件对象的副本

3
我有一个在ASP.NET缓存中的用户控件缓存对象。 我想访问我的控件并创建一份副本,以便自由更改它而不会对基础缓存控件产生任何影响。

你能发布一些你的控制器/缓存代码吗? - BrandonZeider
我认为没有克隆命令,因为每个控件都可以有子控件,它们也可以有子控件,依此类推。 - Chuck Savage
你需要多少“保真度”? 一段时间以前,我最终“序列化”了整个(可定制的)UI并稍后恢复它(它是WinForms而不是ASP,但概念相同)。此外,您事先了解控件有多少信息?如果它将具有众所周知的类型,则可以创建一个新实例并复制所需的每个字段/属性。否则,反射可以帮助,但在我指导您完成该方法之前,您应该提供更多详细信息。 - Edurne Pascual
1
你为什么要缓存控件?你是想缓存其中的数据吗?你想达到什么目的? - John Saunders
5个回答

2

你好,我找到了我想要的代码。我把它放在这里,也许对你有帮助。

/// <summary>
/// this method makes a copy of object as a clone function
/// </summary>
/// <param name="o"></param>
/// <returns></returns>
public static object Clone_Control(object o)
{

    Type type = o.GetType();
    PropertyInfo[] properties = type.GetProperties();
    Object retObject = type.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, null, o, null);
    foreach (PropertyInfo propertyInfo in properties)
    {
        if (propertyInfo.CanWrite)
        {
            propertyInfo.SetValue(retObject, propertyInfo.GetValue(o, null), null);
        }
    }
    return retObject;
}

嗨。我已经使用这个“hack”几年了。但最近我发现这似乎在MONO上不起作用。会出现异常:System.Reflection.TargetException:非静态方法需要一个目标。 - Tobias81

1

你想做的肯定是可以做到的。而且非常简单。类似这样的东西可能吗?

private static void ChangeMyControlTextFromCache(string controlName, string newText, Panel containingPanel)
{
    MyControl original = ViewState[controlName] as MyControl;
    if (original == null)
    {
        original = new MyControl();
        ViewState[controlName] = original;
    }    
    MyControl clone = CloneMyControl(original);
    clone.Text = newText;
    if (containingPanel.Children.Contains(original))
        containingPanel.Children.Remove(original);
    containingPanel.Children.Add(clone);
}

private static MyControl CloneMyControl(MyControl original)
{
    MyControl clone = new MyControl();
    clone.Text = original.Text;
    clone.SomeOtherProperty = original.SomeOtherProperty;
    return clone;
}

大家好,我从文件中加载了一个用户控件(Page.LoadControl("ascx 文件名")),这是一个耗时的过程,我想将已加载的用户控件缓存起来,以防止从文件中重新加载它,但是这个缓存的控件在不同的地方使用不同的设置,并且我希望在不同的用途中制作不同的副本,以防止源缓存控件发生任何更改。请告诉我如何复制我的缓存用户控件(这个用户控件有不同的子控件,我想制作一个与源完全相同的副本)。 - Reza.Sorouri

1

这里是另一种克隆方法,也适用于MONO。 它克隆传递的控件及其子控件。 注意:克隆的控件不能添加到不同的页面(或稍后的回发),即使它还没有添加到任何页面。在Windows上似乎可以工作,但在MONO上会崩溃,因此我猜它通常不应该这样做。

public static Control CloneControl(Control c) {
    var clone = Activator.CreateInstance(c.GetType()) as Control;
    if (c is HtmlControl) {
        clone.ID = c.ID;
        foreach (string key in ((HtmlControl)c).Attributes.Keys)
            ((HtmlControl)clone).Attributes.Add(key, (string)((HtmlControl)c).Attributes[key]);
    }
    else {
        foreach (PropertyInfo p in c.GetType().GetProperties()) {
            // "InnerHtml/Text" are generated on the fly, so skip them. "Page" can be ignored, because it will be set when control is added to a Page.
            if (p.CanRead && p.CanWrite && p.Name != "InnerHtml" && p.Name != "InnerText" && p.Name != "Page") {
                try {
                    p.SetValue(clone, p.GetValue(c, p.GetIndexParameters()), p.GetIndexParameters());
                }
                catch {
                }
            }
        }
    }
    for (int i = 0; i < c.Controls.Count; ++i)
        clone.Controls.Add(CloneControl(c.Controls[i]));
    return clone;
}

0

尝试使用包含LiteralControl和ResourceBasedLiteralControl的页面。

public static System.Web.UI.Control CloneControl(Control c)
            {
                Type type = c.GetType();
                var clone = (0 == type.GetConstructors().Length) ? new Control() : Activator.CreateInstance(type) as Control;
                if (c is HtmlControl)
                {
                    clone.ID = c.ID;
                    AttributeCollection attributeCollection = ((HtmlControl)c).Attributes;
                    System.Collections.ICollection keys = attributeCollection.Keys;
                    foreach (string key in keys)
                    {
                        ((HtmlControl)c).Attributes.Add(key, (string)attributeCollection[key]);
                    }
                }else if(c is System.Web.UI.LiteralControl){
                    clone = new System.Web.UI.LiteralControl(((System.Web.UI.LiteralControl)(c)).Text);
                }
                else
                {
                    PropertyInfo[] properties = c.GetType().GetProperties();
                    foreach (PropertyInfo p in properties)
                    {
                        // "InnerHtml/Text" are generated on the fly, so skip them. "Page" can be ignored, because it will be set when control is added to a Page.
                        if (p.Name != "InnerHtml" && p.Name != "InnerText" && p.Name != "Page" && p.CanRead && p.CanWrite)
                        {
                            try
                            {
                                ParameterInfo[] indexParameters = p.GetIndexParameters();
                                p.SetValue(clone, p.GetValue(c, indexParameters), indexParameters);
                            }
                            catch
                            {
                            }
                        }
                    }
                }
                int cControlsCount = c.Controls.Count;
                for (int i = 0; i < cControlsCount; ++i)
                {
                    clone.Controls.Add(CloneControl(c.Controls[i]));
                }
                return clone;
            }

参考: 动态Web控件、回发和视图状态 作者:Scott Mitchell


0

赞同的答案对我没用。这是我创建 ASP.NET 控件对象副本的解决方案。

    // Copies the custom properties on the control. To use it, cast your object to it. 
    public static object CopyUserControl(object obj, object obj2)
    {
        Type type = obj.GetType();
        PropertyInfo[] properties = type.GetProperties();

        Type type2 = obj2.GetType();
        PropertyInfo[] properties2 = type.GetProperties();

        foreach (PropertyInfo property in properties)
        {
            foreach (PropertyInfo property2 in properties2)
            {
                // Match the loops and ensure it's not a non-custom property 
                if (property.Name == property2.Name && property.SetMethod != null && !property.SetMethod.IsSecuritySafeCritical)
                    property2.SetValue(obj2, property.GetValue(obj, null));
            }
        }
        return obj2;
    }

当您需要在同一页上使用不同的HTML ID复制ASCX控件时,这将非常有用。用法:

// Copying myAscxControl1 onto myAscxControl2
myAscxControl2 = (ObjectNameToBeCasted)CopyUserControl(myAscxControl1, myAscxControl2);

它也应该适用于不同类型的对象。但要匹配你想要复制的属性名称。


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