Reza.Sorouri
Reza.Sorouri

Reputation: 81

create a copy of an ASP.NET Control object

I have a cached control object form a UserControl in asp.net cache. I like to access my control and make a copy of it to freely change it without having any affect on the base cached control.

Upvotes: 3

Views: 9344

Answers (5)

Baz Guvenkaya
Baz Guvenkaya

Reputation: 1572

Upvoted answers didn't work for me. Here's my solution to creating a copy of an ASP.NET control object.

    // 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;
    }

It's useful when you have to copy an ASCX control on the same page with a different html id. Usage:

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

It should work with different typed objects too. Property names that you want to copy must match though.

Upvotes: 0

Will V King
Will V King

Reputation: 131

try this for page includes LiteralControl and 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;
            }

ref: Dynamic Web Controls, Postbacks, and View State By Scott Mitchell

Upvotes: 0

Tobias81
Tobias81

Reputation: 1850

Here is another clone method that will also work with MONO. It clones the passed control and it's children. Note: A cloned control cannot be added to a different page (or in a later post-back) even it has not been added to any page yet. It might seem to work on Windows but will crash on MONO so I guess it is generally not meant to be done.

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;
}

Upvotes: 1

Reza.Sorouri
Reza.Sorouri

Reputation: 81

Hi I found the code that I was looking for. I put it here maybe it helps you too.

/// <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;
}

Upvotes: 2

Jeffrey L Whitledge
Jeffrey L Whitledge

Reputation: 59543

It is certainly possible to do what you are asking to do. And it is very straightforward. Something like this maybe?

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;
}

Upvotes: 1

Related Questions