【问题标题】:TypeConverter for dependent properties - show list of methods of the selected type in PropertyGrid依赖属性的 TypeConverter - 在 PropertyGrid 中显示所选类型的方法列表
【发布时间】:2021-07-14 04:38:42
【问题描述】:

我正在研究 N 层(实体、数据、业务和表示)的新解决方案,我需要创建一个自定义控件来加快搜索速度(该控件将位于表示层中)。

这里是解决方案的结构:

Solution
Ⱶ Entities
  Ⱶ customer__entity
  Ⱶ user__entity
  Ⱶ product__entity
Ⱶ Data
  Ⱶ customer__data
  Ⱶ user__data
  Ⱶ product__data
Ⱶ Business
  Ⱶ customer__business
  Ⱶ user__business
  Ⱶ product__business
Ⱶ Presentation
  Ⱶ controls (folder)
    Ⱶ TextSearch (custom control)
  Ⱶ forms (folder)
    Ⱶ frm__customer___maintenance
    Ⱶ frm__user___maintenance
    Ⱶ frm__product___maintenance

现在我将详细介绍业务层的类所具有的方法。

public abstract class customer__business
{
    public static IEnumerable<customer__entity> GetAll() { }
    public static customer__entity GetById(int id) { }
}

public abstract class user__business
{
    public static IEnumerable<customer__entity> GetAllByCriteria(string criteria) { }
    public static customer__entity GetById(int id) { }
    public static DataTable GetBy(string criteria) { }
}

public abstract class product__business
{
    public static product__entity GetById(int id) { }
    public static string GetName(string criteria) { }
}

这个想法是创建一个继承自 System.Windows.Forms.TextBox 并具有 2 个自定义属性(1 => BusinessLayer 和 2 => MethodToInvoke)的 TextBox 控件。第一个属性将是 type 类型,并将使用 TypeConverter 加载业务层类。第二个属性将依赖于第一个,它必须列出第一个属性中选择的类型所具有的所有方法。

public class TextSearch : System.Windows.Forms.TextBox 
{
    [TypeConverter(typeof(BusinessLayerTypeConverter))]
    public Type BusinessLayer { get; set; }

    [TypeConverter(typeof(MethodToInvokeTypeConverter))]
    public string MethodToInvoke { get; set; }
}

我已经准备好业务层 TypeConverter 类:

public class BusinessLayerTypeConverter : TypeConverter
{
    public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
    {
        return true;
    }

    public override bool CanConvertTo(ITypeDescriptorContext pContext, Type pDestinationType)
    {
        return base.CanConvertTo(pContext, pDestinationType);
    }

    public override object ConvertTo(ITypeDescriptorContext pContext, CultureInfo pCulture, object pValue, Type pDestinationType)
    {
        return base.ConvertTo(pContext, pCulture, pValue, pDestinationType);
    }

    public override bool CanConvertFrom(ITypeDescriptorContext pContext, Type pSourceType)
    {
        if (pSourceType == typeof(string))
            return true;

        return base.CanConvertFrom(pContext, pSourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext pContext, CultureInfo pCulture, object pValue)
    {
        if (pValue is string)
            return GetTypeFromName(pContext, (string)pValue);

        return base.ConvertFrom(pContext, pCulture, pValue);
    }

    public override bool GetStandardValuesSupported(ITypeDescriptorContext pContext)
    {
        return true;
    }

    public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext pContext)
    {
        List<Type> types = GetProjectTypes(pContext);
        List<string> values = new List<string>();

        foreach (Type type in types)
            values.Add(type.FullName);

        values.Sort();

        return new StandardValuesCollection(values);
    }

    private List<Type> GetProjectTypes(IServiceProvider serviceProvider)
    {
        var typeDiscoverySvc = (ITypeDiscoveryService)serviceProvider.GetService(typeof(ITypeDiscoveryService));
        var types = typeDiscoverySvc.GetTypes(typeof(object), true).Cast<Type>().Where(item => item.IsPublic && !item.FullName.StartsWith("System") && item.FullName.Contains("__business")).ToList();

        return types;
    }

    private Type GetTypeFromName(IServiceProvider serviceProvider, string typeName)
    {
        ITypeResolutionService typeResolutionSvc = (ITypeResolutionService)serviceProvider.GetService(typeof(ITypeResolutionService));

        return typeResolutionSvc.GetType(typeName);
    }
}

这是第一个属性的结果,它可以正常工作。

我的问题是我不知道该怎么做,所以第二个属性 (MethodToInvoke) 列出了在第一个属性 (BusinessLayer) 中选择的类型的方法。

  1. 如果 BusinessLayer 属性是 Business.customer__business,那么 MethodToInvoke 应该允许我选择 GetAll 或 GetById。
  2. 如果 BusinessLayer 属性是 Business.user__business,那么 MethodToInvoke 应该允许我选择 GetAllByCriteria 或 GetById 或 GetBy。
  3. 如果 BusinessLayer 属性是 Business.product__business,那么 MethodToInvoke 应该允许我选择 GetById 或 GetName。

这是 MethodToInvokeTypeConverter 类:

public class MethodToInvokeTypeConverter : TypeConverter
{
    public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
    {
        return true;
    }

    public override bool CanConvertTo(ITypeDescriptorContext pContext, Type pDestinationType)
    {
        return base.CanConvertTo(pContext, pDestinationType);
    }

    public override object ConvertTo(ITypeDescriptorContext pContext, CultureInfo pCulture, object pValue, Type pDestinationType)
    {
        return base.ConvertTo(pContext, pCulture, pValue, pDestinationType);
    }

    public override bool CanConvertFrom(ITypeDescriptorContext pContext, Type pSourceType)
    {
        if (pSourceType == typeof(string))
            return true;

        return base.CanConvertFrom(pContext, pSourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext pContext, CultureInfo pCulture, object pValue)
    {
        if (pValue is string)
            return GetTypeFromName(pContext, (string)pValue);

        return base.ConvertFrom(pContext, pCulture, pValue);
    }

    public override bool GetStandardValuesSupported(ITypeDescriptorContext pContext)
    {
        return true;
    }

    public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext pContext)
    {
        List<Type> types = GetProjectTypes(pContext);
        List<string> values = new List<string>();

        foreach (Type type in types)
            values.Add(type.FullName);

        values.Sort();

        return new StandardValuesCollection(values);
    }

    private List<Type> GetProjectTypes(IServiceProvider serviceProvider)
    {
        var types = // I SUPPOSE THAT I SHOULD CODE WHAT I NEED HERE, HOWEVER I DON'T KNOW WHERE TO START.

        return types;
    }

    private Type GetTypeFromName(IServiceProvider serviceProvider, string typeName)
    {
        ITypeResolutionService typeResolutionSvc = (ITypeResolutionService)serviceProvider.GetService(typeof(ITypeResolutionService));

        return typeResolutionSvc.GetType(typeName);
    }
}

感谢您给予我的所有帮助。

编辑 1:

控件的想法是,当将控件添加到表单时,程序员将设置 BusinessLayer 属性(将自动从业务层中的现有类加载),然后设置 MethodToInvoke 属性(将被加载自动使用在 BusinessLayer 属性中选择的类型的现有方法)。

在TextSearch控件中,我将覆盖KeyDown方法,以通过反射的方式分别调用属性中选择的类型的方法(MethodToInvoke,BusinessLayer)。

public class TextSearch : System.Windows.Forms.TextBox
{
    [TypeConverter(typeof(BusinessLayerTypeConverter))]
    public Type BusinessLayer { get; set; }

    [TypeConverter(typeof(MethodToInvokeTypeConverter))]
    public string MethodToInvoke { get; set; }

    protected override void OnKeyDown(KeyEventArgs e)
    {
        base.OnKeyDown(e);

        if (!DesignMode)
        {
            if (e.KeyCode == Keys.Enter)
            {
                var type = BusinessLayer;
                var method = MethodToInvoke;

                type.GetMethod(method).Invoke(null, null);

                // CODE
            }
        }
    }
}

谢谢。

【问题讨论】:

  • 您要解决的实际问题是什么?只是创建一个通过对象列表搜索的搜索控件? winforms 设计器中是否出现了属性?这些属性实际上应该做什么?
  • MethodToInvoke 是一个字符串,在您的 GetProjectTypes 中,您应该使用反射迭代您需要的类类型并在转换器中返回方法的名称......但只是一个字符串;)
  • 问题出在MethodToInvokeTypeConverter类中,不知道如何根据BusinessLayer属性中选择的类型列出方法。我想在 GetProjectTypes (ITypeDescriptorContext pContext) 方法中,我必须捕获 BusinessLayer 的值,但我不知道该怎么做(并使用反射列出该类型的所有方法。)我编辑了我的帖子(请阅读最终部分)。
  • @pedroPG 这个问题是几个月前的问题(在撰写此评论时),您最喜欢自己找到了解决方案,但主题和问题很有趣,所以我回答了这个问题并分享了一个示例以及一些链接,供未来的读者了解更多关于这个主题的信息。希望您和未来的读者发现它有用。

标签: c# .net winforms windows-forms-designer


【解决方案1】:

你快到了,你需要做的是:

  1. 在您的控件中,在主属性(业务类类型)中,当值发生变化时,将从属属性(方法名称)设置为 null 或任何其他有意义的值。
  2. 在从属属性类型转换器中,在GetStandardValues 中,将context.Instnace 转换为您的控件类型,然后获取主属性的值(业务类类型)并返回可能值列表(方法名称)。

就是这样。以上 cmets 将解决您的问题,但您或未来的读者可能会发现以下示例很有趣。

示例 - 用于选择类及其方法之一的属性编辑器

在这个例子中,我创建了一个自定义控件,它有两个新属性:

  • BusinessClassType:对于这个属性,我使用自定义类型转换器来显示业务类列表。
  • MethodName:对于这个属性,我使用自定义类型转换器来显示业务方法列表。

我没有依赖命名约定来查找业务类,而是创建了一个属性BusinessClass;同样对于方法,我创建了BusinessMethod 属性,所以我不会在下拉列表中显示所有方法,只显示那些您标记为业务方法的方法。它在某种程度上类似于 WFC 服务合同和方法合同中的想法。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Globalization;
using System.Linq;
using System.Windows.Forms;
public class MyCustomControl : TextBox
{
    private Type type;
    [TypeConverter(typeof(BusinessClassTypeConverter))]
    public Type BusinessClassType
    {
        get { return type; }
        set
        {
            if (type != value)
            {
                type = value;
                BusinessMethodName = null;
            }
        }
    }
    [TypeConverter(typeof(BusinessMethodNameConverter))]
    public string BusinessMethodName { get; set; }
}
public class BusinessClassTypeConverter : TypeConverter
{
    public override bool GetStandardValuesExclusive
        (ITypeDescriptorContext context)
    {
        return true;
    }
    public override bool GetStandardValuesSupported
        (ITypeDescriptorContext pContext)
    {
        return true;
    }
    public override StandardValuesCollection GetStandardValues
        (ITypeDescriptorContext pContext)
    {
        return new StandardValuesCollection(
            GetProjectTypes(pContext).OrderBy(x => x.FullName).ToList());
    }
    public override bool CanConvertFrom(ITypeDescriptorContext context,
        Type sourceType)
    {
        if (sourceType == typeof(string))
            return true;
        return base.CanConvertFrom(context, sourceType);
    }
    public override object ConvertFrom
        (ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        if (value is string)
            return GetTypeFromName(context, (string)value);
        return base.ConvertFrom(context, culture, value);
    }
    public override bool CanConvertTo(ITypeDescriptorContext context,
        Type destinationType)
    {
        return base.CanConvertTo(context, destinationType);
    }
    public override object ConvertTo(ITypeDescriptorContext context,
        CultureInfo culture, object value, Type destinationType)
    {
        return base.ConvertTo(context, culture, value, destinationType);
    }
    private List<Type> GetProjectTypes(IServiceProvider serviceProvider)
    {
        var typeDiscoverySvc = (ITypeDiscoveryService)serviceProvider
            .GetService(typeof(ITypeDiscoveryService));
        var types = typeDiscoverySvc.GetTypes(typeof(object), true)
            .Cast<Type>()
            .Where(item =>
                item.IsPublic &&
                item.GetCustomAttributes(true)
                .OfType<BusinessClassAttribute>().Any() &&
                !item.FullName.StartsWith("System")
            ).ToList();
        return types;
    }
    private Type GetTypeFromName(IServiceProvider serviceProvider, string typeName)
    {
        ITypeResolutionService typeResolutionSvc = 
            (ITypeResolutionService)serviceProvider
            .GetService(typeof(ITypeResolutionService));
        return typeResolutionSvc.GetType(typeName);
    }
}
public class BusinessMethodNameConverter : TypeConverter
{
    public override bool GetStandardValuesSupported(
        ITypeDescriptorContext context)
    {
        return true;
    }
    public override bool GetStandardValuesExclusive(
        ITypeDescriptorContext context)
    {
        return true;
    }
    public override StandardValuesCollection GetStandardValues(
        ITypeDescriptorContext context)
    {
        var type = ((MyCustomControl)context.Instance).BusinessClassType;
        if (type != null)
        {
            return new StandardValuesCollection(type.GetMethods()
                .Where(x => x.GetCustomAttributes(true)
                .OfType<BusinessMethodAttribute>().Any())
                .Select(x => x.Name).ToList());
        }
        return new StandardValuesCollection(Array.Empty<object>());
    }
    public override bool CanConvertFrom(ITypeDescriptorContext context,
        Type sourceType)
    {
        if (sourceType == typeof(string))
            return true;
        return base.CanConvertFrom(context, sourceType);
    }
    public override object ConvertFrom(ITypeDescriptorContext context,
        CultureInfo culture, object value)
    {
        if (value is string)
            return (string)value;
        return base.ConvertFrom(context, culture, value);
    }
}
public class BusinessClassAttribute : Attribute { }
public class BusinessMethodAttribute : Attribute { }

[BusinessClass]
public class MyBusinessClass1
{
    [BusinessMethod]
    public void Method11() { }
    public void Method12() { }
    [BusinessMethod]
    public void Method13() { }
}
public class MyBusinessClass2
{
}
[BusinessClass]
public class MyBusinessClass3
{
    [BusinessMethod]
    public void Method31() { }
    public void Method32() { }
    [BusinessMethod]
    public void Method33() { }
}

更多信息

您可能还会发现以下链接很有用:

【讨论】:

  • 我知道这个问题有点老了,但如果你能试一试/验证它会很好。
猜你喜欢
  • 2020-10-28
  • 2017-04-02
  • 1970-01-01
  • 1970-01-01
  • 2021-12-12
  • 1970-01-01
  • 1970-01-01
  • 2012-12-05
  • 1970-01-01
相关资源
最近更新 更多