150 lines
5.4 KiB
C#
150 lines
5.4 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Linq.Expressions;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using System.Windows.Forms;
|
||
|
||
namespace TemuKJD.Extension;
|
||
|
||
public class DataContext<TSource> : BindingSource
|
||
{
|
||
public DataContext() { }
|
||
public DataContext(TSource dataSource) : base(dataSource, null) { }
|
||
|
||
// 强类型的 DataSource,避免调用方还要自己转型
|
||
public new TSource DataSource
|
||
{
|
||
get => (TSource)base.DataSource;
|
||
set => base.DataSource = value;
|
||
}
|
||
}
|
||
public static class BindingExtensions
|
||
{
|
||
|
||
|
||
/// <summary>
|
||
/// TSource 必须显式指定(因为无法从参数推断),TControl 自动从调用方控件推断
|
||
/// </summary>
|
||
public static Binding Bind<TSource, TControl>(
|
||
this TControl ctrl,
|
||
DataContext<TSource> dataSource,
|
||
Expression<Func<TControl, object>> controlPropertyExpr,
|
||
Expression<Func<TSource, object>> dataPropertyExpr,
|
||
DataSourceUpdateMode mode = DataSourceUpdateMode.OnPropertyChanged,
|
||
string format = null,
|
||
ConvertEventHandler parseHandler = null)
|
||
where TControl : Control
|
||
=> BindCore(ctrl, controlPropertyExpr.GetPropertyName(), dataSource, dataPropertyExpr, mode, format, parseHandler);
|
||
|
||
public static Binding Bind<TControl>(
|
||
this TControl ctrl,
|
||
BindingSource dataSource,
|
||
Expression<Func<TControl, object>> controlPropertyExpr,
|
||
string dataPropertyName,
|
||
DataSourceUpdateMode mode = DataSourceUpdateMode.OnPropertyChanged,
|
||
string format = null,
|
||
ConvertEventHandler parseHandler = null) where TControl : Control
|
||
=> BindCore(ctrl, controlPropertyExpr.GetPropertyName(), dataSource, dataPropertyName, mode, format, parseHandler);
|
||
|
||
// ---------- 共享的核心实现,不对外暴露 ----------
|
||
private static Binding BindCore<TSource>(
|
||
Control ctrl,
|
||
string controlPropertyName,
|
||
BindingSource bs,
|
||
Expression<Func<TSource, object>> dataPropertyExpr,
|
||
DataSourceUpdateMode mode,
|
||
string format,
|
||
ConvertEventHandler parseHandler) => BindCore(ctrl, controlPropertyName, bs, dataPropertyExpr.GetPropertyName(), mode, format, parseHandler);
|
||
|
||
private static Binding BindCore(
|
||
Control ctrl,
|
||
string controlPropertyName,
|
||
BindingSource bs,
|
||
string dataPropertyName,
|
||
DataSourceUpdateMode mode,
|
||
string format,
|
||
ConvertEventHandler parseHandler)
|
||
{
|
||
var binding = new Binding(controlPropertyName, bs, dataPropertyName, true, mode);
|
||
|
||
if (!string.IsNullOrEmpty(format))
|
||
binding.Format += (s, e) => e.Value = string.Format($"{{0:{format}}}", e.Value);
|
||
|
||
if (parseHandler != null)
|
||
binding.Parse += parseHandler;
|
||
|
||
ctrl.DataBindings.Add(binding);
|
||
return binding;
|
||
}
|
||
// ---------- TextBox ----------
|
||
public static Binding Bind<TSource>(
|
||
this TextBox ctrl,
|
||
BindingSource bs,
|
||
Expression<Func<TextBox, object>> controlPropertyExpr,
|
||
Expression<Func<TSource, object>> dataPropertyExpr,
|
||
DataSourceUpdateMode mode = DataSourceUpdateMode.OnPropertyChanged,
|
||
string format = null,
|
||
ConvertEventHandler parseHandler = null)
|
||
=> BindCore(ctrl, controlPropertyExpr.GetPropertyName(), bs, dataPropertyExpr, mode, format, parseHandler);
|
||
|
||
// ---------- CheckBox ----------
|
||
public static Binding Bind<TSource>(
|
||
this CheckBox ctrl,
|
||
BindingSource bs,
|
||
Expression<Func<CheckBox, object>> controlPropertyExpr,
|
||
Expression<Func<TSource, object>> dataPropertyExpr,
|
||
DataSourceUpdateMode mode = DataSourceUpdateMode.OnPropertyChanged,
|
||
string format = null,
|
||
ConvertEventHandler parseHandler = null)
|
||
=> BindCore(ctrl, controlPropertyExpr.GetPropertyName(), bs, dataPropertyExpr, mode, format, parseHandler);
|
||
|
||
// ---------- DateTimePicker ----------
|
||
public static Binding Bind<TSource>(
|
||
this DateTimePicker ctrl,
|
||
BindingSource bs,
|
||
Expression<Func<DateTimePicker, object>> controlPropertyExpr,
|
||
Expression<Func<TSource, object>> dataPropertyExpr,
|
||
DataSourceUpdateMode mode = DataSourceUpdateMode.OnPropertyChanged,
|
||
string format = null,
|
||
ConvertEventHandler parseHandler = null)
|
||
=> BindCore(ctrl, controlPropertyExpr.GetPropertyName(), bs, dataPropertyExpr, mode, format, parseHandler);
|
||
|
||
// ---------- ComboBox ----------
|
||
public static Binding Bind<TSource>(
|
||
this ComboBox ctrl,
|
||
BindingSource bs,
|
||
Expression<Func<ComboBox, object>> controlPropertyExpr,
|
||
Expression<Func<TSource, object>> dataPropertyExpr,
|
||
DataSourceUpdateMode mode = DataSourceUpdateMode.OnPropertyChanged,
|
||
string format = null,
|
||
ConvertEventHandler parseHandler = null)
|
||
=> BindCore(ctrl, controlPropertyExpr.GetPropertyName(), bs, dataPropertyExpr, mode, format, parseHandler);
|
||
|
||
// ---------- NumericUpDown ----------
|
||
public static Binding Bind<TSource>(
|
||
this NumericUpDown ctrl,
|
||
BindingSource bs,
|
||
Expression<Func<NumericUpDown, object>> controlPropertyExpr,
|
||
Expression<Func<TSource, object>> dataPropertyExpr,
|
||
DataSourceUpdateMode mode = DataSourceUpdateMode.OnPropertyChanged,
|
||
string format = null,
|
||
ConvertEventHandler parseHandler = null)
|
||
=> BindCore(ctrl, controlPropertyExpr.GetPropertyName(), bs, dataPropertyExpr, mode, format, parseHandler);
|
||
|
||
|
||
public static string GetPropertyName<TSource, TProperty>(
|
||
this Expression<Func<TSource, TProperty>> propertyExpr)
|
||
{
|
||
if (propertyExpr.Body is MemberExpression member)
|
||
return member.Member.Name;
|
||
|
||
// 处理值类型属性被隐式转换为 object 的情况(如 decimal -> object)
|
||
if (propertyExpr.Body is UnaryExpression unary &&
|
||
unary.Operand is MemberExpression unaryMember)
|
||
return unaryMember.Member.Name;
|
||
|
||
throw new ArgumentException("表达式必须是形如 x => x.PropertyName 的属性访问", nameof(propertyExpr));
|
||
}
|
||
} |