FEATURED · 精选文章

WPF-MarkupExtension实现Binding+校验转换器通知

发布时间 / 2026/8/11 17:58:06
来源 / 创域科博编辑部
栏目 / 资讯中心
WPF-MarkupExtension实现Binding+校验转换器通知 MarkupExtension XAML标记扩展让自己可以写一个XAML对象。此处MarkupExtension实现了添加Validation和Converter的Binding此Binding可以自己通知变更。public class TestBinding : MarkupExtension { public string PathOne{get; set;} // MultiBinding的第一个Binding的Path public string PathTwo{get; set;} // MultiBinding的第二个Binding的Path // 第一个Binding要用到的校验集合 public CollectionValidationRule ValidationRules {get; set;} new CollectionValidationRule(); privateListDependencyObject? TargetObjectList {get; set;} new ListDependencyObject?(); privateListDependencyProperty? TargetPropertyList {get; set;} new ListDependencyProperty?(); private DependencyObject? TargetObject {get; set;} private DependencyProperty? TargetProperty {get; set;} public override object? ProvideValue(IServiceProvider serviceProvider) { // IServiceProvider通用的服务提供器与当前TestBinding无关但是通过GetService获得的IProvideValueTarget确实是与TestBinding调用时刻的对象 // IProviderValueTargetWPF服务接口获得目标对象和目标属性 if(!(serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget provideValueTarget)) return this; if(provideValueTarget.TargetObject.GetType().FullName System.Windows.SharedDp) return this; if(!provideValueTarget.TargetObject is DependencyObject targetObject) return this; if(!(provideValueTarget.TargetProperty is DependencyProperty targetProperty)) return this; // 由于大量控件使用TestBinding时会出现一个TestBinding负责多个依赖对象和属性 // 所以在此保留一个TestBinding负责的所有对象 TargetObjectList.Add(targetObject); TargetPropertyList.Add(targetProperty); TargetObject targetObject; TargetPropery targetProperty; Binding bindingOne SetBinding(PathOne, true); Binding bindingTwo SetBinding(PathOne, false); MultiBinding multiBinding SetMultiBinding(bindingOne, bindingTwo); return multiBinding.ProvideValue(serviceProvider); } private Binding? SetBinding(string path, bool isValidated) { var binding new Binding(path) { Source FindDataContext(TargetObject), // 获取Source的DataContext Mode BindingMode.TwoWay, NotifyOnSourceUpdated true, NotifyOnTargetUpdated true }; if(isValidated) { binding.ValidationRules.AddRange(ValidationRules); // 在此设置Binding的校验 } return Binding; } private MultiBinding? Set MultiBinding(Binding bindingOne, Binding bindingTwo) { var multiBinding new MultiBinding() { Converter new TestConverter(), // 在此添加多值转换器 Mode BindingMode.TwoWay, NotifyOnSourceUpdated true, NotifyOnTargetUpdated true }; multiBinding.Bindings.Add(bindingOne); multiBinding.Bindings.Add(bindingTwo); // 设置Binding对象返回绑定落地后产生的运行时对象BindingExpressionBase表示当前Binding的行为包含Stats\Value\Error BindingExpressionBase localExpression BindingOperations.SetBinding(TargetObject, TargetProperty); return multiBinding; } // 验证逻辑树向上寻找依赖对象的数据上下文 private object? FindDataContext(DependencyObject? dependencyObject) { object? dataContext null; if(dependencyObject is FrameworkElement element) { dataContext element.DataContext; if(dataContext null) { var parent VisualTreeHelper.GetParent(element); while(parent ! null dataContext null) { if(parent is FrameworkElement parentElement) { dataContext parentElement.DataContext; } parent VisualTreeHelper.GetParent(parent); } } } return dataContext; } private void SetExpression(BindingExpressionBase e) { // TargetObject是DependencyObject类型没有BindingGroup需要转类型 // FrameworkElement真正进行布局、渲染的控件 if(TargetObject is FrameworkElement fe) e.BindingGroup?.BindingExpressions.Add(e); // FrameworkContentElement依赖宿主(FrameworkElement)的控件都是没见过的 else if(TargetObject is FrameworkContentElement fce) fce.BindingGroup?.BindingExpressions.Add(e); } // 由TestBinding自身出发的事件处理先省略了事件的事儿 private void OnTestChanged(object obj, EventArg e) { if(obj is TestBinding binding) { for(int i 0; i binding.TargetObjectList.Count; i) { MultibBindingExpression expression BindingOperations.GetMultiBindingExpress(binding.TargetObjectList[i]. binding.TargetPropertyList[i]); expression?.UpdateTarget(); // 如果是单值Binding则 // (binding.TargetObject as FrameWorkElement)?.GetBindingExpression(binding.TargetPropery)?.UpdateTarget(); } } } }TextBox TextBox.Text local:TestBinding PathOneABC PathTwoCDE local:TestBinding.ValidationRules local:WhatevValidationRule/ /local:TestBinding.ValidationRules local:TestBinding/ /TextBox.Text /TextBoxPathOne、PathOne在XAML中给后端TestBinding的属性赋值总之是为了使用多值转换器因此使用了MultiBinding为了避免每次触发事件时都需要通知所有的属性来让属性再过一遍Converter使用了MarkupExtension在MarkupExtension触发事件OnTestChanged中让所有的TestBinding通知自己负责的所有属性再过一遍Converter。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻