UWP:Style内绑定UserControl参数的x:Bind报错问题求助
UWP自定义UserControl样式绑定问题解决方案
问题背景
在UWP中开发自定义MenuButton UserControl,内部包含应用了ToggleButton样式的RadioButton,需要将UserControl的Icon、TextCustom等参数绑定到Style模板内的Path和TextBlock元素,要求使用x:Bind绑定,但遇到以下问题:
- 设置Style的
TargetType="local:MenuButton"触发XAML解析错误 - 使用
TemplateBinding触发调试断点错误 - WPF中可用的
ElementName绑定方式在UWP的ControlTemplate内无法生效
解决方案1:利用Tag传递复合属性(快速实现)
UWP的ControlTemplate上下文为目标控件(ToggleButton/RadioButton),无法直接访问UserControl属性,可将需要传递的参数打包成类实例,赋值给RadioButton的Tag属性,再在模板内通过绑定访问Tag的内部属性。
实现步骤
- 创建参数存储类
public class MenuButtonParams { public string Icon { get; set; } public string TextCustom { get; set; } }
- 修改UserControl后台代码,维护Tag参数
public sealed partial class MenuButton : UserControl { public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(string), typeof(MenuButton), new PropertyMetadata(null)); public static readonly DependencyProperty TextCustomProperty = DependencyProperty.Register(nameof(TextCustom), typeof(string), typeof(MenuButton), new PropertyMetadata(null)); public string Icon { get => (string)GetValue(IconProperty); set => SetValue(IconProperty, value); } public string TextCustom { get => (string)GetValue(TextCustomProperty); set => SetValue(TextCustomProperty, value); } // 保留原有GroupName、TogleCommand等属性实现 public MenuButton() { this.InitializeComponent(); UpdateTag(); // 属性变化时同步更新Tag this.RegisterPropertyChangedCallback(IconProperty, (s, e) => UpdateTag()); this.RegisterPropertyChangedCallback(TextCustomProperty, (s, e) => UpdateTag()); } private void UpdateTag() { MyRadioButton.Tag = new MenuButtonParams { Icon = Icon, TextCustom = TextCustom }; } }
- 修改XAML代码
给RadioButton命名,并在模板内绑定Tag属性:
<UserControl x:Class="Organazier.UserControls.MenuButton" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Organazier.UserControls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Background="Transparent"> <UserControl.Resources> <x:String x:Key="DefaultIcon">M4,2L4,22 12.683594,22C12.387594,21.378,12.181078,20.707,12.080078,20L12.074219,20C12.026219,19.673 12,19.34 12,19 12,15.474 14.609,12.566078 18,12.080078 18.327,12.033078 18.66,12 19,12 19.34,12 19.673,12.033078 20,12.080078L20,8 14,2 4,2z M13,3.4550781L18.5,9 13,9 13,3.4550781z M19,14C16.25,14 14,16.25 14,19 14,21.75 16.25,24 19,24 21.75,24 24,21.75 24,19 24,16.25 21.75,14 19,14z M19,16C19.461147,16,19.892436,16.114019,20.283203,16.300781L16.300781,20.285156C16.113553,19.893994 16,19.461758 16,19 16,17.346 17.346,16 19,16z M21.699219,17.716797C21.885981,18.107564 22,18.538853 22,19 22,20.654 20.654,22 19,22 18.538242,22 18.106006,21.886447 17.714844,21.699219L21.699219,17.716797z</x:String> <Style x:Key="MenuButtonStyle" TargetType="ToggleButton"> <Setter Property="Height" Value="100"/> <Setter Property="HorizontalAlignment" Value="Stretch"/> <Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="Background" Value="#CCFFFFFF"/> <Setter Property="BorderThickness" Value="0"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ToggleButton"> <Grid x:Name="RootGrid" CornerRadius="10" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}"> <Grid x:Name="myGrid"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Path Data="{Binding Tag.Icon, RelativeSource={RelativeSource TemplatedParent}, FallbackValue={StaticResource DefaultIcon}, TargetNullValue={StaticResource DefaultIcon}}" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="Uniform" Height="30" Width="31" Fill="#515EC1"/> <TextBlock FontSize="20" Grid.Column="1" Text="{Binding Tag.TextCustom, RelativeSource={RelativeSource TemplatedParent}}"/> </Grid> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </UserControl.Resources> <RadioButton x:Name="MyRadioButton" Style="{StaticResource MenuButtonStyle}" GroupName="{x:Bind GroupName}" Command="{x:Bind TogleCommand}" CommandParameter="{x:Bind ItemName}"/> </UserControl>
解决方案2:自定义附加属性(规范实现)
创建针对ToggleButton的附加属性,将UserControl的参数传递给这些附加属性,再在模板内通过TemplateBinding绑定。
实现步骤
- 创建附加属性类
public static class MenuButtonAttachedProperties { public static readonly DependencyProperty IconProperty = DependencyProperty.RegisterAttached("Icon", typeof(string), typeof(MenuButtonAttachedProperties), new PropertyMetadata(null)); public static readonly DependencyProperty TextCustomProperty = DependencyProperty.RegisterAttached("TextCustom", typeof(string), typeof(MenuButtonAttachedProperties), new PropertyMetadata(null)); public static void SetIcon(DependencyObject obj, string value) { obj.SetValue(IconProperty, value); } public static string GetIcon(DependencyObject obj) { return (string)obj.GetValue(IconProperty); } public static void SetTextCustom(DependencyObject obj, string value) { obj.SetValue(TextCustomProperty, value); } public static string GetTextCustom(DependencyObject obj) { return (string)obj.GetValue(TextCustomProperty); } }
- 修改UserControl的RadioButton绑定
将UserControl属性绑定到附加属性:
<RadioButton Style="{StaticResource MenuButtonStyle}" GroupName="{x:Bind GroupName}" Command="{x:Bind TogleCommand}" CommandParameter="{x:Bind ItemName}" local:MenuButtonAttachedProperties.Icon="{x:Bind Icon, Mode=OneWay}" local:MenuButtonAttachedProperties.TextCustom="{x:Bind TextCustom, Mode=OneWay}"/>
- 修改Style模板内的绑定
直接绑定附加属性:
<Path Data="{TemplateBinding local:MenuButtonAttachedProperties.Icon, FallbackValue={StaticResource DefaultIcon}, TargetNullValue={StaticResource DefaultIcon}}" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="Uniform" Height="30" Width="31" Fill="#515EC1"/> <TextBlock FontSize="20" Grid.Column="1" Text="{TemplateBinding local:MenuButtonAttachedProperties.TextCustom}"/>
关键说明
- UWP的ControlTemplate上下文为目标控件(ToggleButton/RadioButton),无法直接访问UserControl的属性
x:Bind在模板内默认绑定模板父级(TemplatedParent),因此需要将UserControl属性传递到目标控件的属性/附加属性后再绑定- 方案1适合快速验证需求,方案2更符合控件扩展的规范,便于后续维护和复用
内容的提问来源于stack exchange,提问作者Андрей Reyz
相关产品推荐
相关产品推荐

