UWP中复用多控件通用XAML绑定与行为的方法
复用多个TextBox的绑定与行为方案
绝对可以!针对你这种多个TextBox复用绑定和行为的场景,有几种简洁又实用的方案,我给你详细拆解一下:
方案1:自定义TextBox控件(最彻底的封装)
如果你的TextBox复用场景非常多,最彻底的方式是自定义一个封装了所有通用逻辑的TextBox控件,只暴露需要个性化的参数(比如你这里的字典键,也就是原来的ConverterParameter)。
步骤1:创建自定义控件后台代码
using Microsoft.Xaml.Interactivity; using Microsoft.Xaml.Interactions.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; public class ValidatedTextBox : TextBox { // 暴露字典键参数,对应原来的ConverterParameter public static readonly DependencyProperty DictionaryKeyProperty = DependencyProperty.Register(nameof(DictionaryKey), typeof(string), typeof(ValidatedTextBox), new PropertyMetadata(null)); public string DictionaryKey { get => (string)GetValue(DictionaryKeyProperty); set => SetValue(DictionaryKeyProperty, value); } // 暴露验证命令 public static readonly DependencyProperty ValidateCommandProperty = DependencyProperty.Register(nameof(ValidateCommand), typeof(ICommand), typeof(ValidatedTextBox), new PropertyMetadata(null)); public ICommand ValidateCommand { get => (ICommand)GetValue(ValidateCommandProperty); set => SetValue(ValidateCommandProperty, value); } public ValidatedTextBox() { DefaultStyleKey = typeof(ValidatedTextBox); // 添加LostFocus触发验证的行为 var lostFocusBehavior = new EventTriggerBehavior { EventName = "LostFocus" }; var invokeAction = new InvokeCommandAction(); // 绑定到自身的ValidateCommand属性 invokeAction.SetBinding(InvokeCommandAction.CommandProperty, new Binding(nameof(ValidateCommand)) { Source = this }); lostFocusBehavior.Actions.Add(invokeAction); Interaction.GetBehaviors(this).Add(lostFocusBehavior); } }
步骤2:定义控件模板(Generic.xaml)
在项目的Themes/Generic.xaml中添加控件模板,把通用的Text绑定逻辑封装进去:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:YourNamespace"> <Style TargetType="local:ValidatedTextBox"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:ValidatedTextBox"> <TextBox x:Name="InnerTextBox" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, Mode=TwoWay, Converter={StaticResource DictionaryConverter}, ConverterParameter={Binding RelativeSource={RelativeSource TemplatedParent}, Path=DictionaryKey}, UpdateSourceTrigger=PropertyChanged}" AllowFocusOnInteraction="True"> </TextBox> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
步骤3:使用自定义控件
现在你的XAML会变得异常简洁:
<local:ValidatedTextBox x:Name="Control1" DictionaryKey="Control1" ValidateCommand="{x:Bind NewItem.CommandValidate}" Style="{StaticResource FormTextBox}" Grid.Row="1"/> <local:ValidatedTextBox x:Name="Control2" DictionaryKey="Control2" ValidateCommand="{x:Bind NewItem.CommandValidate}" Style="{StaticResource FormTextBoxNumber}" Grid.Row="4"/>
方案2:附加属性+样式(灵活无侵入)
如果不想自定义控件,可以用附加属性来封装通用的绑定和行为逻辑,配合样式使用,完全不修改原有控件类型。
步骤1:创建附加属性类
using Microsoft.Xaml.Interactivity; using Microsoft.Xaml.Interactions.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; public static class TextBoxValidationHelper { // 附加属性:字典键(对应ConverterParameter) public static readonly DependencyProperty DictionaryKeyProperty = DependencyProperty.RegisterAttached("DictionaryKey", typeof(string), typeof(TextBoxValidationHelper), new PropertyMetadata(null, OnDictionaryKeyChanged)); public static void SetDictionaryKey(DependencyObject obj, string value) => obj.SetValue(DictionaryKeyProperty, value); public static string GetDictionaryKey(DependencyObject obj) => (string)obj.GetValue(DictionaryKeyProperty); // 附加属性:验证命令 public static readonly DependencyProperty ValidateCommandProperty = DependencyProperty.RegisterAttached("ValidateCommand", typeof(ICommand), typeof(TextBoxValidationHelper), new PropertyMetadata(null, OnValidateCommandChanged)); public static void SetValidateCommand(DependencyObject obj, ICommand value) => obj.SetValue(ValidateCommandProperty, value); public static ICommand GetValidateCommand(DependencyObject obj) => (ICommand)obj.GetValue(ValidateCommandProperty); // 当字典键变化时,设置Text绑定 private static void OnDictionaryKeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is TextBox textBox) { textBox.SetBinding(TextBox.TextProperty, new Binding { Path = new PropertyPath("NewItem.Params"), Mode = BindingMode.TwoWay, Converter = Application.Current.Resources["DictionaryConverter"] as IValueConverter, ConverterParameter = e.NewValue, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); } } // 当验证命令变化时,添加/更新LostFocus行为 private static void OnValidateCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is TextBox textBox) { // 移除已有的LostFocus行为 var existingBehavior = Interaction.GetBehaviors(textBox) .OfType<EventTriggerBehavior>() .FirstOrDefault(b => b.EventName == "LostFocus"); if (existingBehavior != null) Interaction.GetBehaviors(textBox).Remove(existingBehavior); // 添加新的行为 if (e.NewValue is ICommand command) { var behavior = new EventTriggerBehavior { EventName = "LostFocus" }; behavior.Actions.Add(new InvokeCommandAction { Command = command }); Interaction.GetBehaviors(textBox).Add(behavior); } } } }
步骤2:在原有TextBox上使用附加属性
现在只需要给TextBox附加两个属性即可,完全保留原有样式:
<TextBox x:Name="Control1" local:TextBoxValidationHelper.DictionaryKey="Control1" local:TextBoxValidationHelper.ValidateCommand="{x:Bind NewItem.CommandValidate}" AllowFocusOnInteraction="True" Style="{StaticResource FormTextBox}" Grid.Row="1"/> <TextBox x:Name="Control2" local:TextBoxValidationHelper.DictionaryKey="Control2" local:TextBoxValidationHelper.ValidateCommand="{x:Bind NewItem.CommandValidate}" AllowFocusOnInteraction="True" Style="{StaticResource FormTextBoxNumber}" Grid.Row="4"/>
方案3:自定义行为+样式(轻量复用)
如果主要是行为重复,而绑定只需要复用模板(仅ConverterParameter不同),可以自定义一个行为类,配合样式来复用。
步骤1:自定义验证行为
using Microsoft.Xaml.Interactivity; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; public class ValidateOnLostFocusBehavior : Behavior<TextBox> { public ICommand ValidateCommand { get => (ICommand)GetValue(ValidateCommandProperty); set => SetValue(ValidateCommandProperty, value); } public static readonly DependencyProperty ValidateCommandProperty = DependencyProperty.Register(nameof(ValidateCommand), typeof(ICommand), typeof(ValidateOnLostFocusBehavior), new PropertyMetadata(null)); protected override void OnAttached() { base.OnAttached(); AssociatedObject.LostFocus += OnLostFocus; } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.LostFocus -= OnLostFocus; } private void OnLostFocus(object sender, RoutedEventArgs e) { ValidateCommand?.Execute(null); } }
步骤2:创建通用样式
在样式中设置通用的Text绑定(用Tag属性传递ConverterParameter),并添加自定义行为:
<Style x:Key="ValidatedTextBoxBaseStyle" TargetType="TextBox"> <Setter Property="AllowFocusOnInteraction" Value="True"/> <!-- 用Tag属性传递ConverterParameter --> <Setter Property="Text" Value="{x:Bind NewItem.Params, Mode=TwoWay, Converter={StaticResource DictionaryConverter}, ConverterParameter={Binding Tag, RelativeSource={RelativeSource Self}}, UpdateSourceTrigger=PropertyChanged}"/> <Setter Property="i:Interaction.Behaviors"> <Setter.Value> <i:BehaviorCollection> <local:ValidateOnLostFocusBehavior ValidateCommand="{x:Bind NewItem.CommandValidate}"/> </i:BehaviorCollection> </Setter.Value> </Setter> </Style>
步骤3:使用样式
基于通用样式扩展你的原有样式,设置Tag即可:
<TextBox x:Name="Control1" Tag="Control1" Style="{StaticResource ValidatedTextBoxBaseStyle}" BasedOn="{StaticResource FormTextBox}" Grid.Row="1"/> <TextBox x:Name="Control2" Tag="Control2" Style="{StaticResource ValidatedTextBoxBaseStyle}" BasedOn="{StaticResource FormTextBoxNumber}" Grid.Row="4"/>
方案选型建议
- 如果你有大量TextBox需要复用,优先选自定义控件,封装最彻底,后续维护成本最低。
- 如果需要灵活适配现有控件,不想修改控件类型,选附加属性方案,侵入性最低。
- 如果只是复用行为+部分绑定,选自定义行为+样式,实现最简单。
内容的提问来源于stack exchange,提问作者beatnikthedan
相关产品推荐
相关产品推荐

