You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinForm自定义Picker控件开发:自动为TextBox添加关联属性

WinForm 自定义Picker控件实现方案与思路

一、基础Picker控件:选中产品填充TextBox

1. 构建Picker核心结构

继承UserControl搭建基础框架,内部用ListBox或DataGridView展示产品列表,同时定义产品实体和选中事件:

// 产品实体类
public class Product
{
    public string ProductName { get; set; }
    public decimal ProductPrice { get; set; }
    // 按需添加其他字段
}

// 自定义Picker控件
public class ProductPicker : UserControl
{
    // 选中产品事件,用于通知外部
    public event EventHandler<Product> ProductSelected;
    private Product _selectedProduct;

    public Product SelectedProduct
    {
        get => _selectedProduct;
        set
        {
            _selectedProduct = value;
            ProductSelected?.Invoke(this, value);
        }
    }

    // 示例:双击ListBox触发选中逻辑
    private void listBoxProducts_DoubleClick(object sender, EventArgs e)
    {
        if (listBoxProducts.SelectedItem is Product selected)
        {
            SelectedProduct = selected;
        }
    }
}

2. 绑定TextBox填充逻辑

在窗体中拖入Picker和目标TextBox,直接订阅ProductSelected事件即可完成填充:

private void productPicker1_ProductSelected(object sender, Product e)
{
    txtProductName.Text = e.ProductName;
    txtProductPrice.Text = e.ProductPrice.ToString("C");
}

二、自动给TextBox添加picker关联属性

要实现类似GcShortCut的动态属性,核心依赖WinForm设计时扩展和TypeDescriptor机制:

1. 定义关联属性标记

先创建属性类,用于标记TextBox要关联的Picker字段:

[AttributeUsage(AttributeTargets.Property)]
public class PickerFieldAttribute : Attribute
{
    public string FieldName { get; set; }
    public PickerFieldAttribute(string fieldName) => FieldName = fieldName;
}

2. 扩展TextBox的TypeDescriptor

通过自定义TypeDescriptor给TextBox动态添加picker属性,让设计器能识别并显示该属性:

// 自定义TypeDescriptor,负责添加picker属性
public class TextBoxPickerTypeDescriptor : CustomTypeDescriptor
{
    private readonly ICustomTypeDescriptor _originalDescriptor;

    public TextBoxPickerTypeDescriptor(ICustomTypeDescriptor originalDescriptor)
    {
        _originalDescriptor = originalDescriptor;
    }

    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        var originalProps = _originalDescriptor.GetProperties(attributes);
        var newProps = new List<PropertyDescriptor>(originalProps.Cast<PropertyDescriptor>());

        // 动态生成picker属性
        var pickerProp = TypeDescriptor.CreateProperty(
            typeof(TextBox),
            "picker",
            typeof(string),
            new DescriptionAttribute("关联Picker的产品字段名")
        );
        newProps.Add(pickerProp);

        return new PropertyDescriptorCollection(newProps.ToArray());
    }
}

// 注册TypeDescriptor提供者
public class TextBoxPickerProvider : TypeDescriptionProvider
{
    private readonly TypeDescriptionProvider _originalProvider;

    public TextBoxPickerProvider(TypeDescriptionProvider originalProvider)
    {
        _originalProvider = originalProvider;
    }

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
    {
        var originalDesc = _originalProvider.GetTypeDescriptor(objectType, instance);
        return new TextBoxPickerTypeDescriptor(originalDesc);
    }
}

3. 启动时注册提供者

在Program.cs的Main方法中注册,确保设计器和运行时都能识别扩展属性:

[STAThread]
static void Main()
{
    // 注册TextBox的属性扩展
    TypeDescriptor.AddProvider(
        new TextBoxPickerProvider(TypeDescriptor.GetProvider(typeof(TextBox))),
        typeof(TextBox)
    );

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}

4. 实现自动填充逻辑

在Picker的选中事件中,遍历窗体上的TextBox,读取picker属性值,通过反射自动填充对应字段:

private void OnProductSelected(Product product)
{
    var form = FindForm();
    if (form == null) return;

    foreach (Control ctrl in form.Controls)
    {
        if (ctrl is TextBox txt)
        {
            // 获取设计时设置的picker属性值
            var fieldName = TypeDescriptor.GetProperties(txt)["picker"]?.GetValue(txt) as string;
            if (string.IsNullOrEmpty(fieldName)) continue;

            // 反射获取产品字段值并填充
            var prop = typeof(Product).GetProperty(fieldName);
            if (prop != null)
            {
                txt.Text = prop.GetValue(product)?.ToString();
            }
        }
    }
}

5. 设计时优化(可选)

如果想让picker属性在设计器中显示为下拉列表(列出Product的所有字段),可以自定义UITypeEditor:

public class PickerFieldEditor : UITypeEditor
{
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.DropDown;
    }

    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
        var editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
        if (editorService == null) return value;

        // 列出Product类的所有公共属性
        var props = typeof(Product).GetProperties().Select(p => p.Name).ToList();
        var listBox = new ListBox();
        listBox.Items.AddRange(props.ToArray());
        listBox.SelectedItem = value;

        editorService.DropDownControl(listBox);
        return listBox.SelectedItem;
    }
}

修改TypeDescriptor.CreateProperty时,给picker属性添加编辑器:

var pickerProp = TypeDescriptor.CreateProperty(
    typeof(TextBox),
    "picker",
    typeof(string),
    new DescriptionAttribute("关联Picker的产品字段名"),
    new EditorAttribute(typeof(PickerFieldEditor), typeof(UITypeEditor))
);

三、关键思路总结

  • 基础填充用事件驱动,解耦Picker与TextBox的直接依赖;
  • 动态属性靠TypeDescriptor扩展,这是WinForm设计器扩展的核心机制;
  • 自动关联用反射读取字段值,避免硬编码,提升控件灵活性。

内容的提问来源于stack exchange,提问作者Dino Thunder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 03:27:36