C# VS2013 WinForms designer的IExtenderProvider问题 简明的
我正在实现IExtenderProvider,以便为winforms控件提供一个属性,以存储每个控件的权限集合,这些权限必须被授予才能使控件可用 问题 一切都按预期运行良好,问题只出现在设计阶段,并且只有在以下情况下: 我打开表单设计器。 表单上某些控件的编辑权限扩展属性。 在窗体设计器打开时编译我的项目。 然后,在编译之后,如果我再次尝试编辑控件的权限,权限将消失,尽管它们仍然存在于表单的desiger.cs文件中 此外,如果在此状态下执行任何导致重新生成设计器文件的设计器操作(例如,移动按钮),则表单设计器文件中的权限将丢失 因此,只要我在设计器打开时不编译,一切都正常,我可以编辑权限、保存并再次编辑 要从此状态恢复,请执行以下操作:即,再次从designer.cs文件加载权限 关闭表单设计器并重新打开它。 或者在设计器仍处于打开状态时切换到designer.cs文件,编辑任何内容,然后将其撤消,然后再次返回表单设计器。它将被刷新并再次加载权限。 更新1 感谢@Hans Passant的提示,duh,我在调试设计器时没有检查CLR异常,因此,异常被默默地抑制了 该问题存在于序列化程序的序列化方法中,该方法稍微细化了一点:C# VS2013 WinForms designer的IExtenderProvider问题 简明的,c#,winforms,iextenderprovider,C#,Winforms,Iextenderprovider,我正在实现IExtenderProvider,以便为winforms控件提供一个属性,以存储每个控件的权限集合,这些权限必须被授予才能使控件可用 问题 一切都按预期运行良好,问题只出现在设计阶段,并且只有在以下情况下: 我打开表单设计器。 表单上某些控件的编辑权限扩展属性。 在窗体设计器打开时编译我的项目。 然后,在编译之后,如果我再次尝试编辑控件的权限,权限将消失,尽管它们仍然存在于表单的desiger.cs文件中 此外,如果在此状态下执行任何导致重新生成设计器文件的设计器操作(例如,移动按钮
public override object Serialize(IDesignerSerializationManager manager, object value)
{
CodeDomSerializer baseClassSerializer = manager.GetSerializer(typeof(PermissionExtenderProvider).BaseType, typeof(CodeDomSerializer)) as CodeDomSerializer;
CodeStatementCollection statements = baseClassSerializer.Serialize(manager, value) as CodeStatementCollection;
try
{
PermissionExtenderProvider provider = (PermissionExtenderProvider)value;
IDesignerHost host = (IDesignerHost)manager.GetService(typeof(IDesignerHost));
var components = host.Container.Components.Cast<IComponent>().Where(x => provider.CanExtend(x));
this.SerializeExtender(manager, provider, host, components, statements);
}
catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
return statements;
}
value属性的类型相同,它是PermissionExtenderProvider,但来自不同的源程序集,因此引发以下异常:
System.InvalidCastException:
[A]VSDesignerExtenderProviderIssue.PermissionExtenderProvider cannot be cast to
[B]VSDesignerExtenderProviderIssue.PermissionExtenderProvider.
Type A originates from 'VSDesignerExtenderProviderIssue, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadNeither' at location 'C:\Users\Administrator\AppData\Local\Microsoft\VisualStudio\12.0\ProjectAssemblies\elo5dzxu01\VSDesignerExtenderProviderIssue.exe'.
Type B originates from 'VSDesignerExtenderProviderIssue, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadNeither' at location 'C:\Users\Administrator\AppData\Local\Microsoft\VisualStudio\12.0\ProjectAssemblies\5_6og8t_01\VSDesignerExtenderProviderIssue.exe'.
由于这个问题是在编译后才出现的,我想从现在开始就有问题了
我不完全确定设计器进程,但我的解释是,值是从以前编译的程序集中传递的,然后我尝试转换为驻留在设计器使用的活动程序集中的相同类型,因此无法执行转换,因为类型来自两个不同的程序集
为什么它从不同的程序集发送值?在不手动将对象映射/序列化到活动程序集类型的情况下,是否可以解决此问题
代码
我尝试过在编译后的状态下(即问题发生时)在设计模式下调试它,但我能想到的是PermissionExtenderProvider.GetPermissions。。。我的扩展程序提供程序中的方法返回null,这意味着PermissionExtenderProvider.SetPermission。。。尚未调用,因此PermissionCollectionEditor.EditValue。。。在我的自定义UITypeEditor中,接收空值并将其传递给PermissionCollectionEditorForm,这就是权限不存在的原因
我提供了一个演示该问题的小样本项目,它包括:
包含示例权限的权限文件夹。
许可证收集
PermissionCollectionEditor UITypeEditor实现。
PermissionCollectionEditor表单编辑权限集合属性的表单。
PermissionExtenderProvider提供权限属性的IExtenderProvider实现。
PermissionExtenderProviderSerializer将扩展属性序列化为设计器文件的序列化程序。
Form1是项目的主窗体,带有一些具有测试权限的按钮。
您可以使用具有一些按钮的Form1和PermissionExtenderProvider实例测试扩展器
以下是该项目的一些关键代码:
许可收集编辑器
许可证扩展程序提供程序
来自初始帖子的背景信息
我有一个winforms应用程序,具有与数据库交互的中间服务层
在数据库中,我有一些表,这些表定义并映射用户和用户角色的权限。这些权限在我的应用程序的类中表示,并实现IPermission
在我的应用程序中,我有一个PermissionProvider类,它调用服务并检查用户的权限,以禁用/隐藏用户不必访问的任何控件,当然,我不依赖于此来实现安全性
我想做的是,通过添加Permissions属性来扩展Winforms控件,这是一个权限集合,所以我使用了IExtenderProvider
我已经实现了自定义UITypeEditor来编辑属性,并实现了自定义UITypeEditor来在设计器文件中正确序列化权限集合
我还在我的扩展程序提供程序中添加了一个名为Host的属性,该属性存储承载提供程序的表单/用户控件,并由我的自定义序列化程序在设计器文件中序列化它以及权限
然后,在我的提供程序中,当设置host属性时,我订阅host窗体/用户控件的Load事件,并在运行时加载主机时验证控件的权限,并放置一个小指示器,以便用户知道该控件由于
权限不足
我正在使用Update 4运行VS2013社区,Win 8.1 64位,并使用x86进行编译。我没有任何答案,但我可以说祝你好运,我自己也在设计器中努力解决序列化问题,这不是一个有趣的过程。我发现删除自定义序列化代码并让设计器完成它的工作是有帮助的,所以我会开始分解东西,直到它正常工作,然后尝试重新添加东西,即使它序列化到resx文件中。@RonBeyer谢谢,我已经尝试了不同的方法,在我提出这个序列化格式之前,我一直面临着很多问题,我认为设计师很满意,很明显,在某种程度上,它不是。我将不得不休假阅读所有内容。Punting:设计时的异常会导致这种行为。启动另一个VS实例,工具+附加到进程以附加到VS的第一个实例。调试>异常>勾选CLR异常以查看它们。@HansPassant我已尝试缩短它。我在调试时没有检查CLR异常,因此异常被抑制。谢谢你的提示。在调试之后,我提出了一些问题,并更新了这个问题。@HansPassant您知道为什么设计器会从不同的程序集传递值吗?
System.InvalidCastException:
[A]VSDesignerExtenderProviderIssue.PermissionExtenderProvider cannot be cast to
[B]VSDesignerExtenderProviderIssue.PermissionExtenderProvider.
Type A originates from 'VSDesignerExtenderProviderIssue, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadNeither' at location 'C:\Users\Administrator\AppData\Local\Microsoft\VisualStudio\12.0\ProjectAssemblies\elo5dzxu01\VSDesignerExtenderProviderIssue.exe'.
Type B originates from 'VSDesignerExtenderProviderIssue, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadNeither' at location 'C:\Users\Administrator\AppData\Local\Microsoft\VisualStudio\12.0\ProjectAssemblies\5_6og8t_01\VSDesignerExtenderProviderIssue.exe'.
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (context != null && context.Instance != null && provider != null)
{
var editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
if (editorService != null)
{
using (PermissionCollectionEditorForm collEditorFrm = new PermissionCollectionEditorForm(provider, value as PermissionCollection))
{
if (editorService.ShowDialog(collEditorFrm) == DialogResult.OK)
{
value = collEditorFrm.Collection;
}
}
}
}
return value;
}
[ProvideProperty("Permissions", typeof(IComponent))]
[DesignerSerializer(typeof(PermissionExtenderProviderSerializer), typeof(CodeDomSerializer))]
public class PermissionExtenderProvider : Component, IExtenderProvider
{
private Hashtable _Permissions;
private bool _InitialVerification = false;
private List<IComponent> _DisabledComponents;
public PermissionExtenderProvider()
: base()
{
_Permissions = new Hashtable();
_DisabledComponents = new List<IComponent>();
}
//=====================================
// IExtenderProvider
//=====================================
public bool CanExtend(object extendee)
{
return (extendee is Control ||
extendee is TabPage ||
extendee is ToolStripItem) && !(extendee is PermissionExtenderProvider);
}
//=====================================
// ProvidedProperties
//=====================================
[Category("Behavior")]
[Editor(typeof(PermissionCollectionEditor), typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public PermissionCollection GetPermissions(IComponent component)
{
PermissionCollection result = null;
if (_Permissions.ContainsKey(component))
result = (PermissionCollection)_Permissions[component];
return result;
}
public void SetPermissions(IComponent component, PermissionCollection value)
{
if (value == null)
{
value = new PermissionCollection();
}
if (value.Count == 0)
{
_Permissions.Remove(component);
}
else
{
_Permissions[component] = value;
}
}
//=====================================
// Permission verification
//=====================================
public bool CheckPermissions(IComponent component)
{
PermissionCollection permissions = _Permissions.ContainsKey(component) ? (PermissionCollection)_Permissions[component] : null;
if (permissions == null)
return false;
return PermissionProvider.CheckPermissions(permissions.ToArray());
}
public virtual void AssertPermissions(IComponent component)
{
bool hasPermission = CheckPermissions(component);
if(!hasPermission)
{
if(component is TabPage)
{
((TabPage)component).Enabled = false;
((TabControl)((TabPage)component).Parent).Invalidate();
}
else if(component is ToolStripItem)
{
((ToolStripItem)component).Enabled = false;
}
else if(component is Control)
{
((Control)component).Enabled = false;
}
}
// Add/Remove to the list of disabled components
if(hasPermission && _DisabledComponents.Contains(component))
{
_DisabledComponents.Remove(component);
}
else if(!hasPermission && !_DisabledComponents.Contains(component))
{
_DisabledComponents.Add(component);
}
}
/// <summary>
/// Verify the permissions associated with the control provider. It hides/disables the controls
/// associated if the all or any of the permissions are not granted (depending on options specified for the control)
/// </summary>
public virtual void VerifyPermissions()
{
_DisabledComponents.Clear();
// Verify the permissions
foreach (IComponent comp in _Permissions.Keys)
{
AssertPermissions(comp);
}
}
//================================================
// Wiring the host container's load event
//================================================
private ContainerControl m_Host;
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ContainerControl Host
{
get { return m_Host; }
set
{
if (m_Host == null && value as Form != null && !DesignMode)
{
(value as Form).Load += Initialize;
}
else if (m_Host == null && value as UserControl != null && !DesignMode)
{
(value as UserControl).Load += Initialize;
}
m_Host = value;
}
}
private void Initialize(object sender, EventArgs e)
{
if (!DesignMode)
{
VerifyPermissions();
_InitialVerification = true;
}
}
}
public class PermissionExtenderProviderSerializer : CodeDomSerializer
{
public override object Serialize(IDesignerSerializationManager manager, object value)
{
PermissionExtenderProvider provider = value as PermissionExtenderProvider;
CodeDomSerializer baseClassSerializer = manager.GetSerializer(typeof(PermissionExtenderProvider).BaseType, typeof(CodeDomSerializer)) as CodeDomSerializer;
CodeStatementCollection statements = baseClassSerializer.Serialize(manager, value) as CodeStatementCollection;
IDesignerHost host = (IDesignerHost)manager.GetService(typeof(IDesignerHost));
ComponentCollection components = host.Container.Components;
this.SerializeExtender(manager, provider, host, components, statements);
return statements;
}
private void SerializeExtender(IDesignerSerializationManager manager, PermissionExtenderProvider provider, IDesignerHost host, ComponentCollection components, CodeStatementCollection statements)
{
if (components.Count > 0)
{
statements.Add(new CodeCommentStatement(" "));
statements.Add(new CodeCommentStatement(manager.GetName(provider)));
statements.Add(new CodeCommentStatement(" "));
}
// Set the Host property of the provider, this is where we wire the Load event to assert permissions
statements.Add(new CodeSnippetStatement(string.Format("this.{0}.Host = this;", manager.GetName(provider))));
// Serialize permissions for components
foreach (IComponent component in components)
{
if (component is PermissionExtenderProvider
|| component == host.RootComponent)
{
continue;
}
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(component)["Name"];
if (descriptor != null)
{
CodeMethodInvokeExpression methodcall = new CodeMethodInvokeExpression(base.SerializeToExpression(manager, provider), "SetPermissions");
string extendeeName = descriptor.GetValue(component).ToString();
methodcall.Parameters.Add(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), extendeeName));
PermissionCollection permissions = provider.GetPermissions(component);
if (permissions != null && permissions.Count > 0)
{
StringBuilder sb = new StringBuilder();
// Create new PermissionCollection instance and supply permissions array to constructor
sb.Append("new PermissionCollection(");
sb.Append("new ");
sb.Append(typeof(IPermission).FullName);
sb.Append("[] {");
foreach (IPermission perm in permissions)
{
PropertyInfo operationEnumProperty = perm.GetType()
.GetProperty("Operation");
object operationTypeVal = operationEnumProperty.GetValue(perm, null);
string operationTypeEnumValName = Enum.GetName(operationEnumProperty.PropertyType, operationTypeVal);
sb.AppendLine();
sb.Append("new ");
sb.Append(perm.GetType().FullName);
sb.Append("(");
sb.Append(perm.GetType().FullName);
sb.Append(".OperationType.");
sb.Append(operationTypeEnumValName);
sb.Append(")");
sb.Append(", ");
}
// Remove trailing comma from last item
if (permissions.Count > 0)
{
sb.Remove(sb.Length - 2, 2);
}
sb.Append(" })");
methodcall.Parameters.Add(new CodeSnippetExpression(sb.ToString()));
}
else
{
methodcall.Parameters.Add(new CodePrimitiveExpression(null));
}
statements.Add(methodcall);
}
}
}
}