如何根据类型加载用户控件

时间:2011-03-05 17:57:30

标签: c# asp.net dynamic-usercontrols

如果你有类型(即typeof(MyUserControl)),有没有办法实例化和使用usercontrol(.ascx)?

使用常规的asp.net控件(如文本框或下拉列表),您只需创建一个新实例并将其添加到控件集合中。这似乎不适用于用户控件。虽然您可以创建一个新实例并将其添加到集合中,并且触发所有事件,但它实际上不会呈现给页面。通常,您可以使用.ascx

的路径调用Page.LoadControl()

如果您拥有所有类型,则会出现问题。如何获得.ascx的路径以提供LoadControl方法。理想情况下,我还希望不必引用Page对象

3 个答案:

答案 0 :(得分:3)

没有。这可能;必须提供ASCX虚拟路径以动态加载带有标记的用户控件,并且没有虚拟路径类型的内部映射。

但是,因为我仍然是懒惰的,所以我使用的方法仍然是“类型安全”,并将解决问题隔离到代码中的单个位置。它仍然需要访问“页面对象”,否则会处理愚蠢的细节。

以下是简要说明:

  1. 使用类型查找相对(到我的根)ASCX路径,使用启发式将类型从命名空间映射到虚拟路径;允许一种指定手动映射的方法,并在指定时使用
  2. 将类型的相对路径转换为正确/完整的虚拟路径
  3. 使用虚拟路径加载控件
  4. 继续,好像什么也没发生
  5. 享受(我只是从我的项目中复制'n'pasted部分,YMMV):

        /// <summary>
        /// Load the control with the given type.
        /// </summary>
        public object LoadControl(Type t, Page page)
        {
            try
            {
                // The definition for the resolver is in the next code bit
                var partialPath = resolver.ResolvePartialPath(t);
                var fullPath = ResolvePartialControlPath(partialPath);
                // Now we have the Control loaded from the Virtual Path. Easy.
                return page.LoadControl(fullPath);
            } catch (Exception ex)
            {
                throw new Exception("Control mapping failed", ex);
            }
        }
    
        /// <summary>
        /// Loads a control by a particular type.
        /// (Strong-typed wrapper for the previous method).
        /// </summary>
        public T LoadControl<T>(Page page) where T : Control
        {
            try
            {
                return (T)LoadControl(typeof (T), page);
            } catch (Exception ex)
            {
                throw new Exception(string.Format(
                    "Failed to load control for type: {0}",
                    typeof (T).Name), ex);
            }
        }
    
        /// <summary>
        /// Given a partial control path, return the full (relative to root) control path.
        /// </summary>
        string ResolvePartialControlPath(string partialPath)
        {
            return string.Format("{0}{1}.ascx",
                ControlPathRoot, partialPath);
        }
    

    ControlResolver的代码清单:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace FooBar
    {
        class ControlResolver
        {
            const string BaseNamespace = "TheBaseControlNameSpace";
    
            readonly IDictionary<Type, string> resolvedPaths = new Dictionary<Type, string>();
    
            /// <summary>
            /// Maps types to partial paths for controls.
            /// 
            /// This is required for Types which are NOT automatically resolveable
            /// by the simple reflection mapping.
            /// </summary>
            static readonly IDictionary<Type, string> MappedPartialPaths = new Dictionary<Type, string>
            {
                { typeof(MyOddType), "Relative/ControlPath" }, // No virtual ~BASE, no .ASXC
            };
    
            /// <summary>
            /// Given a type, return the partial path to the ASCX.
            /// 
            /// This path is the path UNDER the Control Template root
            /// WITHOUT the ASCX extension.
            /// 
            /// This is required because there is no mapping maintained between the class
            /// and the code-behind path.
            /// 
            /// Does not return null.
            /// </summary>
            public string ResolvePartialPath (Type type)
            {
                if (type == null)
                {
                    throw new ArgumentNullException("type");
                }
    
                string partialPath;
                if (resolvedPaths.TryGetValue(type, out partialPath))
                {
                    return partialPath;
                } else
                {
                    string mappedPath;
                    if (MappedPartialPaths.TryGetValue(type, out mappedPath))
                    {
                        resolvedPaths[type] = mappedPath;
                        return mappedPath;
                    } else
                    {
                        // Since I use well-mapped virtual directory names to namespaces,
                        // this gets around needing to manually specify all the types above.
                        if (!type.FullName.StartsWith(BaseNamespace))
                        {
                            throw new InvalidOperationException("Invalid control type");
                        } else
                        {
                            var reflectionPath = type.FullName
                                .Replace(BaseNamespace, "")
                                .Replace('.', '/');
                            resolvedPaths[type] = reflectionPath;
                            return reflectionPath;
                        }
                    }
                }
            }
        }
    }
    

答案 1 :(得分:0)

  ' Load the control 
  Dim myUC as UserControl = LoadControl("ucHeading.ascx") 

  ' Set the Usercontrol Type 
  Dim ucType as Type = myUC.GetType() 

  ' Get access to the property 
  Dim ucPageHeadingProperty as PropertyInfo = ucType.GetProperty("PageHeading") 

  ' Set the property 
  ucPageHeadingProperty.SetValue(myUC,"Access a Usercontrol from Code Behind",Nothing) 

  pnlHeading.Controls.Add ( myUC ) 

答案 2 :(得分:-1)

你可能在这里运气不好。

This fellow无法获得帮助。

他们告诉this person它无法完成。