我正在实施IExtenderProvider,以提供一个属性来winforms控件,以存储每个控件的权限集合,必须授予该控件的可用权。
一切都按预期效果很好, 问题只出现在设计时,只有在 时:
然后,在编译之后,如果我再次尝试编辑控件的权限,权限就会消失,尽管它们仍然存在于表单的desiger.cs文件中。
此外,如果我在此状态下执行任何导致设计器文件重新生成的设计器操作,例如移动按钮,权限将从表单的设计器文件中丢失。
所以,只要我在设计师打开时不进行编译,一切正常,我可以编辑权限,再次保存和重新编辑。
要从此状态恢复: (即,再次从designer.cs文件加载权限)
感谢@Hans Passant的提示,是的,我正在调试设计器而不检查CLR异常,因此,异常被静默抑制。
问题在于序列化程序的Serialize方法(稍微改进一下):
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;
}
更具体地说,在这一行:
PermissionExtenderProvider provider = (PermissionExtenderProvider)value;
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'.
由于问题只发生在编译之后,我认为它从现在开始。
我对设计师流程并不完全确定,但我的解释是该值是从先前编译的程序集传递的,然后我尝试转换为驻留在&#34; active&#34中的相同类型;设计师使用的装配,因此铸件无法完成,因为类型来自两个不同的装配。
为什么它从不同的程序集中发送值?反正有没有手动映射/序列化对象到活动程序集的类型?
我已尝试在设计模式下调试它(在编译后的状态,即问题发生时),但我能想到的是我的扩展程序提供程序中的PermissionExtenderProvider.GetPermissions(...)
方法返回{ {1}},意味着尚未调用null
,因此我的自定义UITypeEditor中的PermissionExtenderProvider.SetPermission(...)
会收到空值并将其传递给PermissionCollectionEditor.EditValue(...)
,这就是权限所在的原因不在那里。
我提供了一个展示问题的小样本项目,它包括:
PermissionCollectionEditorForm
(包含示例权限的文件夹)。Permissions
PermissionCollection
(UITypeEditor实现)。PermissionCollectionEditor
(编辑权限集合属性的表单)。PermissionCollectionEditorForm
(提供PermissionExtenderProvider
属性的IExtenderProvider实现)。Permission
(将扩展属性序列化为设计器文件的序列化程序。PermissionExtenderProviderSerializer
(项目的主要形式,带有一些具有测试权限的按钮)。您可以使用Form1测试扩展器,其中包含一些按钮和Form1
实例。
以下是该项目的一些关键代码:
PermissionCollectionEditor
PermissionExtenderProvider
PermissionExtenderProvider
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;
}
PermissionExtenderProviderSerializer
[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;
}
}
}
我有一个winforms应用程序,中间服务层与数据库交互。
在数据库中,我有表定义和映射用户和用户角色的权限。这些权限在我的应用程序的类中表示,并实现 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);
}
}
}
}
。
在我的应用程序中,我有一个IPermission
类调用该服务并检查用户的权限,以禁用/隐藏用户不必访问的任何控件(当然,我不依赖为了安全起见。)
我想要做的是,通过添加PermissionProvider
属性来扩展Winforms控件,这是一个权限集合,所以我使用了Permissions
。
我已经实现了我的自定义IExtenderProvider
来编辑属性,并自定义CodeDomSerializer以在设计器文件中正确地序列化权限集合。
我还在我的扩展程序提供程序中添加了一个名为UITypeEditor
的属性,该属性存储托管提供程序的表单/用户控件,并且我的自定义序列化程序在设计器文件中将其与权限一起序列化。
然后在我的提供程序中,当设置Host
属性时,我订阅主机表单/用户控件的Host
事件,并在加载主机时验证控件的权限运行时,并设置一个小指示器,以便用户知道由于权限不足而禁用了控件。
我正在使用Update 4运行VS2013社区,Win 8.1 64位,并在x86中进行编译。