首页 > 解决方案 > 控件模板中的 UWP 双向绑定

问题描述

我有一个带有 Text 属性的自定义控件,它应该双向绑定到数据上下文的属性。
绑定仅在开始时发生(我猜是一次),但不响应任何文本更改。

我的自定义控件:

public sealed class MyTextControl : Control
{
    public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
        "Text", typeof(string), typeof(MyTextControl), new PropertyMetadata(default(string)));

    public string Text
    {
        get => (string)GetValue(TextProperty);
        set => SetValue(TextProperty, value);
    }

    public MyTextControl()
    {
        DefaultStyleKey = typeof(MyTextControl);
    }
}

控制模板:

<Style TargetType="local:MyTextControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:MyTextControl">
                <Border
                    Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}">

                    <TextBox
                        BorderBrush="Black"
                        BorderThickness="1"
                        Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}}" />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

我打电话给控制:

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
    <myTextControl:MyTextControl
        Width="500"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Text="{x:Bind Greeting, Mode=TwoWay}" />

    <Button
        Margin="50"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Content="Submit" />
</StackPanel>

Greeting 是 DP 在代码后面设置为 Hello World!

我希望在“问候语”或“文本更改文本”中达到一个断点,但我没有。
似乎双向绑定效果不佳。与 WPF 有什么不同?我怎样才能使它工作?

标签: wpfuwpbindingwin-universal-appcustom-controls

解决方案


快速回答

首先,a{TemplateBinding}始终是单向绑定

其次,从 Windows 10 版本 1809 开始,可以在{x:Bind}{TemplateBinding}ControlTemplate.

因此,您可以{x:Bind}在 a 内部使用ControlTemplate而不是{TemplateBinding}并具有双向绑定功能( mode=TwoWay)。

额外帮助

话虽如此,{x:Bind}在 a 内部使用ControlTemplate并不像替换{TemplateBinding}with的实例那么简单{x:Bind}。它涉及一些其他的变化。考虑到 Visual Studio 会自动 (1) 创建一个名为的类文件MyControl.cs并 (2) 使用 inside 分配控件的样式ControlTemplateSomeProject\Themes\Generic.xaml如果文件夹和资源文件不存在,则会创建它们),请按照这些说明进行操作您的控件使用 {x:Bind}

  1. 请记住TargetTypeControlTemplate使用{x:Bind}.

  2. 在该文件夹Themes\中,创建一个名为的新类文件Generic.xaml.cs并声明一个公共密封部分类,该类Generic使用一个公共构造函数命名,该构造函数只调用InitializeComponent

    // Themes\Generic.xaml.cs
    namespace SomeSolution.SomeProject.Themes
    {
        public sealed partial class Generic
        {
            public Generic() => InitializeComponent();
        }
    }
    

    (见注 A。)

  3. 将以下内容作为属性添加到 的根标记Generic.xaml

    <!-- Themes\Generic.xaml -->
    <ResourceDictionary x:Class="SomeProject.Themes.Generic" ...>
        ...
    </ResourceDictionary>
    

    (见注 B。)

  4. Generic在 ( ) 中实例化包含控件样式的 ready-for-xBind 类型 ( Application.Resources) .MergedDictionaries

    <!-- App.xaml -->
    <Application ...>
        <Application.Resource>
            <Generic xmlns="using:Whatever.Namespace" />
        </Application.Resources>
        ...
    </Application>
    

    (见注释 C、D 和 E。)


笔记

A.{x:Bind}标记扩展在编译时生成源代码,因此需要对生成的代码进行初始化]。

B.连接标记和代码隐藏之间的x:Class部分类]。

C.通过实例化它的类型(这样InitializeComponent被调用)而不是引用它的文件名来重用带有代码隐藏的资源字典。

D.Application.Resources可以到处重复使用。如果不打算这样做,则可以在 , 等中实例化具有样式的SomePage.ResourcesSomeFrameworkElement.Resources

E.Themes\Generic.xaml不需要Application.Resources为了让它们在任何地方都可用而“合并”其中的资源。它们按约定提供。但是,由于此资源字典确实需要显式实例化,因此无需命名文件Generic.xamlGeneric.xaml.cs(within Themes/)。文件(和类)可以​​命名不同,命名空间和位置也可以不同。只要它们被相应地实例化,控件就会起作用


推荐阅读