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
相关产品推荐
相关产品推荐

