Как загрузить пользовательский элемент управления, учитывая его тип
Есть ли способ создать экземпляр и использовать usercontrol (.ascx), если у вас есть его тип (т.е. typeof(MyUserControl)
)?
С помощью обычных элементов управления asp.net, таких как текстовое поле или выпадающий список, вы можете просто создать новый экземпляр и добавить его в коллекцию элементов управления. Это не похоже на работу для пользовательских элементов управления. Хотя вы можете создать новый экземпляр и добавить его в коллекцию, и все его события будут запущены, он фактически не будет отображаться на странице. Как правило, вы бы позвонили Page.LoadControl()
с путем к.ascx
Это создает проблему, если все, что у вас есть, это его тип. Как вы можете получить путь к.ascx, чтобы дать LoadControl
метод. В идеале я также хотел бы, чтобы не было ссылки на объект Page.
2 ответа
Нет, это невозможно; виртуальный путь ASCX должен быть предоставлен для динамической загрузки пользовательского элемента управления с разметкой, при этом отсутствует внутреннее сопоставление типов виртуальных путей.
Однако, поскольку я все еще ленив, вот подход, который я использовал, который все еще "типобезопасен", и изолирует решающую проблему в одном месте в коде. Он по-прежнему требует доступа к "объекту страницы", но в остальном заботится о глупых деталях.
Вот краткое объяснение:
- Используйте тип для поиска относительного (к моему корню) пути ASCX, используя эвристику для отображения типов из пространства имен в виртуальный путь; позволяют указать ручное отображение и использовать его, если указано
- Превратите относительный путь типа в правильный / полный виртуальный путь
- Загрузите элемент управления с виртуальным путем
- Продолжайте, как будто ничего не случилось
Наслаждайтесь (я просто копирую и вставляю отдельные части из моего проекта, 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;
}
}
}
}
}
}
' 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 )
Вам может не повезти здесь.
Этот парень не мог получить помощь, делая это.
И они сказали этому человеку, что это невозможно сделать.