与VS2013 WinForms设计器

时间:2015-04-28 01:26:35

标签: c# winforms iextenderprovider

我正在实施IExtenderProvider,以提供一个属性来winforms控件,以存储每个控件的权限集合,必须授予该控件的可用权。

问题

一切都按预期效果很好, 问题只出现在设计时,只有在 时:

  1. 我打开表单设计器。
  2. 编辑表单上某些控件的权限(扩展属性)。
  3. 在表单设计器打开时编译我的项目。
  4. 然后,在编译之后,如果我再次尝试编辑控件的权限,权限就会消失,尽管它们仍然存在于表单的desiger.cs文件中。

    此外,如果我在此状态下执行任何导致设计器文件重新生成的设计器操作,例如移动按钮,权限将从表单的设计器文件中丢失。

    所以,只要我在设计师打开时不进行编译,一切正常,我可以编辑权限,再次保存和重新编辑。

    要从此状态恢复: (即,再次从designer.cs文件加载权限)

    • 关闭表单设计器并重新打开它。
    • 或切换到designer.cs文件(当设计器仍然打开时),编辑任何内容然后撤消它,然后再次返回到表单设计器。它会刷新并再次加载权限。

    更新1

    感谢@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实例。

    Download the sample project

    以下是该项目的一些关键代码:

    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中进行编译。

0 个答案:

没有答案