继承控件中的 UWP 数据绑定

Posted

技术标签:

【中文标题】继承控件中的 UWP 数据绑定【英文标题】:UWP databinding in an inherited control 【发布时间】:2021-05-19 06:59:18 【问题描述】:

我有一个从默认 TextBox 继承的自定义文本框控件。在样式上,我添加了一些额外的 xaml,其中之一就是这个进度环:

 <toolkit:Loading    x:Name="IsBusyLoader"
                     Grid.Column="1"
                     Grid.ColumnSpan="2"
                     Style="StaticResource DefaultLoader">
        <ProgressRing Style="StaticResource SmallRing" />
 </toolkit:Loading>

我必须通过使用控件的绑定来控制它的“IsLoading”。为此,我在此控件后面的代码中创建了它的绑定,因为它是一种样式,所以我不确定如何使用依赖属性以 xaml 样式创建该绑定。

        public FluentTextBox() => DefaultStyleKey = typeof(FluentTextBox);

    protected override void OnApplyTemplate()
    
        base.OnApplyTemplate();

        //GetTemplateChild
        _isBusyLoader = GetTemplateChild("IsBusyLoader") as Loading;

        //bindings
        _isBusyLoader.SetBinding(Microsoft.Toolkit.Uwp.UI.Controls.Loading.IsLoadingProperty, new Binding  Mode = BindingMode.OneWay, Source = IsBusy );
    

    public bool IsBusy
    
        get => (bool) GetValue(IsBusyProperty);
        set => SetValue(IsBusyProperty, value);
    
    public static readonly DependencyProperty IsBusyProperty =
        DependencyProperty.Register("IsBusy", typeof(bool), typeof(FluentTextBox), new PropertyMetadata(null));

如您所见,IsBusy 是一个依赖属性。我在 UI 上绑定到它,我正在使用这个自定义文本框。

<controls1:FluentTextBox
                Margin="4"
                Header="Field 1"
                IsBusy="x:Bind IsBusy, Mode=OneWay" />

现在这里的 IsBusy 属性实际上在代码中,问题是当我从后面的代码更新 IsBusy 属性时,它不会反映在 UI 中。但是我在该页面的其他地方使用了相同的 IsBusy 属性,并且它在那里正确地向 UI 发出通知,所以我很清楚该部分工作正常。

 public bool IsBusy // code behind is busy property on my xaml page.
    
        get => _isBusy;
        set
        
            _isBusy = value;
            RaisePropertyChanged(nameof(IsBusy));
        
    

请注意,当我在源代码中创建绑定并将其设置为“true”时,我的自定义文本框会按预期显示进度环。我也尝试将源绑定到 IsBusyProperty ,但这也不起作用。

_isBusyLoader.SetBinding(Microsoft.Toolkit.Uwp.UI.Controls.Loading.IsLoadingProperty, new Binding  Mode = BindingMode.OneWay, Source = true );

更新 1

完整样式代码

<Style TargetType="controls:FluentTextBox">
    <Setter Property="Margin" Value="ThemeResource DefaultControlMargin" />
    <Setter Property="local:TextBoxProperties.Initialized" Value="true" />
    <Setter Property="MinWidth" Value="ThemeResource TextControlThemeMinWidth" />
    <Setter Property="MinHeight" Value="ThemeResource TextControlThemeMinHeight" />
    <Setter Property="Foreground" Value="ThemeResource TextControlForeground" />
    <Setter Property="Background" Value="ThemeResource TextControlBackground" />
    <Setter Property="BorderBrush" Value="ThemeResource TextControlBorderBrush" />
    <Setter Property="SelectionHighlightColor" Value="ThemeResource TextControlSelectionHighlightColor" />
    <Setter Property="BorderThickness" Value="ThemeResource TextControlBorderThemeThickness" />
    <Setter Property="FontFamily" Value="ThemeResource ContentControlThemeFontFamily" />
    <Setter Property="FontSize" Value="ThemeResource InputControlFontSize" />
    <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Auto" />
    <Setter Property="ScrollViewer.VerticalScrollMode" Value="Auto" />
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
    <Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
    <Setter Property="HorizontalAlignment" Value="ThemeResource DefaultControlHorizontalAlignment" />
    <Setter Property="VerticalAlignment" Value="ThemeResource DefaultControlVerticalAlignment" />
    <Setter Property="Padding" Value="ThemeResource TextControlThemePadding" />
    <Setter Property="UseSystemFocusVisuals" Value="ThemeResource IsApplicationFocusVisualKindReveal" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="controls:FluentTextBox">
                <Grid>
                    <Grid.Resources>
                        <!--default delete button style here-->
                    </Grid.Resources>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <i:Interaction.Behaviors>
                        <ic:EventTriggerBehavior EventName="PointerEntered">
                            <ic:CallMethodAction MethodName="StartAnimation" TargetObject="Binding ElementName=MoreActionGridFadeIn" />
                        </ic:EventTriggerBehavior>
                        <ic:EventTriggerBehavior EventName="PointerExited">
                            <ic:CallMethodAction MethodName="StartAnimation" TargetObject="Binding ElementName=MoreActionGridFadeOut" />
                        </ic:EventTriggerBehavior>
                    </i:Interaction.Behaviors>
                    <Border
                        x:Name="BorderElement"
                        Grid.Row="0"
                        Grid.RowSpan="3"
                        Grid.ColumnSpan="2"
                        MinWidth="ThemeResource TextControlThemeMinWidth"
                        MinHeight="ThemeResource TextControlThemeMinHeight"
                        Background="TemplateBinding Background"
                        BorderBrush="TemplateBinding BorderBrush"
                        BorderThickness="TemplateBinding BorderThickness"
                        Control.IsTemplateFocusTarget="True"
                        CornerRadius="TemplateBinding CornerRadius" />
                    <Border
                        x:Name="ValidationBorder"
                        Grid.Row="0"
                        Grid.RowSpan="3"
                        Grid.ColumnSpan="2"
                        MinWidth="ThemeResource TextControlThemeMinWidth"
                        MinHeight="ThemeResource TextControlThemeMinHeight"
                        BorderBrush="ThemeResource ValidationBorderBrush"
                        CornerRadius="TemplateBinding CornerRadius" />
                    <ContentPresenter
                        x:Name="ShadowHeaderContentPresenter"
                        Padding="10,0,2,0"
                        Background="x:Null"
                        Content="TemplateBinding Header"
                        ContentTemplate="TemplateBinding HeaderTemplate"
                        FontSize="TemplateBinding FontSize"
                        FontWeight="Normal"
                        Foreground="x:Null"
                        Opacity="0"
                        TextWrapping="NoWrap"
                        Visibility="Visible" />
                    <ContentPresenter
                        x:Name="HeaderContentPresenter"
                        Padding="10,0,2,0"
                        x:DeferLoadStrategy="Lazy"
                        Content="TemplateBinding Header"
                        ContentTemplate="TemplateBinding HeaderTemplate"
                        FontSize="TemplateBinding FontSize"
                        FontWeight="Normal"
                        Foreground="Binding ElementName=PlaceholderTextContentPresenter, Path=Foreground, Mode=OneWay"
                        TextWrapping="NoWrap"
                        Visibility="Collapsed">
                        <ContentPresenter.RenderTransform>
                            <CompositeTransform />
                        </ContentPresenter.RenderTransform>
                    </ContentPresenter>
                    <ScrollViewer
                        x:Name="ContentElement"
                        Grid.Row="1"
                        Margin="TemplateBinding BorderThickness"
                        Padding="TemplateBinding Padding"
                        AutomationProperties.AccessibilityView="Raw"
                        HorizontalScrollBarVisibility="TemplateBinding ScrollViewer.HorizontalScrollBarVisibility"
                        HorizontalScrollMode="TemplateBinding ScrollViewer.HorizontalScrollMode"
                        IsDeferredScrollingEnabled="TemplateBinding ScrollViewer.IsDeferredScrollingEnabled"
                        IsHorizontalRailEnabled="TemplateBinding ScrollViewer.IsHorizontalRailEnabled"
                        IsTabStop="False"
                        IsVerticalRailEnabled="TemplateBinding ScrollViewer.IsVerticalRailEnabled"
                        VerticalScrollBarVisibility="TemplateBinding ScrollViewer.VerticalScrollBarVisibility"
                        VerticalScrollMode="TemplateBinding ScrollViewer.VerticalScrollMode"
                        ZoomMode="Disabled" />
                    <TextBlock
                        x:Name="PlaceholderTextContentPresenter"
                        Grid.Row="1"
                        Grid.ColumnSpan="2"
                        Margin="TemplateBinding BorderThickness"
                        Padding="10,0,0,0"
                        Foreground="Binding PlaceholderForeground, RelativeSource=RelativeSource TemplatedParent, TargetNullValue=ThemeResource TextControlPlaceholderForeground"
                        IsHitTestVisible="False"
                        Text="TemplateBinding PlaceholderText"
                        TextAlignment="TemplateBinding TextAlignment"
                        TextWrapping="TemplateBinding TextWrapping" />
                    <Grid Grid.RowSpan="3" Grid.Column="1">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <Button
                            x:Name="DeleteButton"
                            MinWidth="ThemeResource DeleteButtonMinWidth"
                            MaxWidth="ThemeResource DeleteButtonMaxWidth"
                            Margin="ThemeResource HelperButtonThemePadding"
                            VerticalAlignment="Stretch"
                            AutomationProperties.AccessibilityView="Raw"
                            BorderThickness="TemplateBinding BorderThickness"
                            FontSize="TemplateBinding FontSize"
                            IsTabStop="False"
                            Style="StaticResource DeleteButtonStyle"
                            Visibility="Collapsed" />
                        <Grid x:Name="ActionGrid" Grid.Column="1">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="48" />
                                <ColumnDefinition Width="48" />
                            </Grid.ColumnDefinitions>
                            <Grid>
                                <Grid x:Name="MoreGrid" Opacity="0">
                                    <i:Interaction.Behaviors>
                                        <ToolkitBehaviors:Fade
                                            x:Name="MoreActionGridFadeIn"
                                            AutomaticallyStart="False"
                                            Delay="0"
                                            EasingMode="EaseOut"
                                            EasingType="Cubic"
                                            Value="1"
                                            Duration="300" />
                                        <ToolkitBehaviors:Fade
                                            x:Name="MoreActionGridFadeOut"
                                            AutomaticallyStart="False"
                                            Delay="0"
                                            EasingMode="EaseOut"
                                            EasingType="Cubic"
                                            Value="0"
                                            Duration="300" />
                                    </i:Interaction.Behaviors>
                                    <AppBarButton
                                        x:Name="CopyButton"
                                        Width="48"
                                        Height="48"
                                        HorizontalAlignment="Center"
                                        VerticalAlignment="Center"
                                        Icon="Copy"
                                        ToolTipService.ToolTip="Copy" />
                                </Grid>
                            </Grid>
                            <Grid Grid.Column="1">
                                <toolkit:DropShadowPanel
                                    Width="24"
                                    Height="24"
                                    Margin="8"
                                    HorizontalAlignment="Center"
                                    VerticalAlignment="Center"
                                    Style="StaticResource GridShadowSmall"
                                    Color="Black">
                                    <Ellipse
                                        Width="24"
                                        Height="24"
                                        HorizontalAlignment="Center"
                                        VerticalAlignment="Center"
                                        Fill="TemplateBinding BorderBrush" />
                                </toolkit:DropShadowPanel>
                                <Ellipse
                                    x:Name="CentralElipse"
                                    Width="20"
                                    Height="20"
                                    HorizontalAlignment="Center"
                                    VerticalAlignment="Center"
                                    Canvas.ZIndex="10">
                                    <ToolTipService.ToolTip>
                                        <ToolTip>
                                            <Grid Width="140" MaxHeight="200">
                                                <Grid.RowDefinitions>
                                                    <RowDefinition Height="Auto" />
                                                    <RowDefinition />
                                                </Grid.RowDefinitions>
                                                <Grid.ColumnDefinitions>
                                                    <ColumnDefinition Width="Auto" />
                                                    <ColumnDefinition />
                                                </Grid.ColumnDefinitions>
                                                <TextBlock
                                                    x:Name="MemosCountBlock"
                                                    Grid.Row="1"
                                                    Margin="4"
                                                    FontWeight="SemiBold"
                                                    TextTrimming="CharacterEllipsis"
                                                    TextWrapping="Wrap" />
                                                <TextBlock
                                                    Grid.Row="1"
                                                    Grid.Column="1"
                                                    Margin="4"
                                                    Text="Memos"
                                                    TextTrimming="CharacterEllipsis"
                                                    TextWrapping="WrapWholeWords" />
                                                <TextBlock
                                                    x:Name="MyFirstMemoBlock"
                                                    Grid.ColumnSpan="2"
                                                    Margin="4"
                                                    TextTrimming="CharacterEllipsis"
                                                    TextWrapping="WrapWholeWords" />
                                            </Grid>
                                        </ToolTip>
                                    </ToolTipService.ToolTip>
                                    <FlyoutBase.AttachedFlyout>
                                        <Flyout x:Name="MemosFlyout" FlyoutPresenterStyle="StaticResource NoPaddingFlyoutPresenter">
                                            <local1:MemosControl
                                                x:Name="MyMemosControl"
                                                Width="320"
                                                MaxHeight="440"
                                                Padding="0,8,0,0"
                                                NewMemoButtonVisible="True" />
                                        </Flyout>
                                    </FlyoutBase.AttachedFlyout>
                                </Ellipse>
                            </Grid>
                            <toolkit:Loading
                                x:Name="IsBusyLoader"
                                Grid.ColumnSpan="2"
                                Style="StaticResource DefaultLoader">
                                <ProgressRing Style="StaticResource SmallRing" />
                            </toolkit:Loading>
                        </Grid>
                    </Grid>

                    <ContentPresenter
                        x:Name="DescriptionPresenter"
                        Grid.Row="2"
                        Padding="10,0,4,4"
                        x:Load="False"
                        AutomationProperties.AccessibilityView="Raw"
                        Content="TemplateBinding Description"
                        FontSize="StaticResource InputControlHeaderFontSize"
                        Foreground="Binding ElementName=PlaceholderTextContentPresenter, Path=Foreground, Mode=OneWay" />



                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">

                            <VisualState x:Name="Disabled">
                           <!--  default disabled state  -->
                                
                            </VisualState>
                            <VisualState x:Name="Normal" />

                            <VisualState x:Name="PointerOver">

                                <!--  default pointer over state  -->
                            </VisualState>
                            <VisualState x:Name="Focused">

                                <!--  default focused state  -->
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="ButtonStates">
                            <VisualState x:Name="ButtonVisible">

                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="DeleteButton" Storyboard.TargetProperty="Visibility">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="ButtonCollapsed" />
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="HeaderStates">
                            <VisualStateGroup.Transitions>
                                <VisualTransition GeneratedDuration="0:0:0.25">
                                    <VisualTransition.GeneratedEasingFunction>
                                        <CircleEase EasingMode="EaseInOut" />
                                    </VisualTransition.GeneratedEasingFunction>
                                </VisualTransition>
                            </VisualStateGroup.Transitions>
                            <VisualState x:Name="NotEmpty">
                                <VisualState.Setters>
                                    <Setter Target="HeaderContentPresenter.(FontSize)" Value="StaticResource InputControlHeaderFontSize" />
                                </VisualState.Setters>
                                <VisualState.StateTriggers>
                                    <StateTrigger IsActive="Binding Text, Converter=StaticResource IsNotEmptyConverter, Mode=OneWay, RelativeSource=RelativeSource Mode=TemplatedParent" />
                                </VisualState.StateTriggers>
                            </VisualState>
                            <VisualState x:Name="Empty">
                                <VisualState.Setters>
                                    <Setter Target="HeaderContentPresenter.(UIElement.RenderTransform).(CompositeTransform.TranslateY)" Value="16" />
                                    <Setter Target="PlaceholderTextContentPresenter.(Opacity)" Value="0" />
                                </VisualState.Setters>
                                <VisualState.StateTriggers>
                                    <StateTrigger IsActive="Binding Text, Converter=StaticResource IsEmptyConverter, Mode=OneWay, RelativeSource=RelativeSource Mode=TemplatedParent" />
                                </VisualState.StateTriggers>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="ValidationStates">
                            <VisualStateGroup.Transitions>
                                <VisualTransition GeneratedDuration="0:0:0.25">
                                    <VisualTransition.GeneratedEasingFunction>
                                        <CircleEase EasingMode="EaseInOut" />
                                    </VisualTransition.GeneratedEasingFunction>
                                </VisualTransition>
                            </VisualStateGroup.Transitions>
                            <VisualState x:Name="Valid" />
                            <VisualState x:Name="NotValid">
                                <VisualState.Setters>
                                    <Setter Target="ValidationBorder.(BorderThickness)" Value="ThemeResource ValidationBorderThickness" />

                                </VisualState.Setters>
                                <VisualState.StateTriggers>
                                    <StateTrigger IsActive="Binding (local:Validation.HasError), Mode=OneWay, RelativeSource=RelativeSource Mode=TemplatedParent" />
                                </VisualState.StateTriggers>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

【问题讨论】:

您能否为您的FluentTextBox 控件发布Style,包括模板? @mm8 添加。我已经删除了一些无关紧要的代码,因为它超出了这里的字符限制,并且我已经评论了我删除代码的地方,它的大部分是默认代码,所以不会很重要 【参考方案1】:

您应该能够直接绑定到您的ControlTemplate 中的IsBusy 属性,而不是覆盖OnApplyTemplate()

<toolkit:Loading x:Name="IsBusyLoader" IsLoading="x:Bind IsBusy, Mode=TwoWay" ... />

请注意,您应该设置 Mode 属性,因为编译 (x:Bind) 绑定的默认模式是 OneTime

您还应该在注册依赖属性时指定一个有效的默认值:

public static readonly DependencyProperty IsBusyProperty =
    DependencyProperty.Register(nameof(IsBusy), typeof(bool), typeof(FluentTextBox),
    new PropertyMetadata(false));

【讨论】:

x:Bind 在资源字典中不起作用,并且这种样式在资源字典中,如果它是用户控件,那么我可以将 x:Bind 直接绑定到 xaml 中的 IsBusy 属性,但事实并非如此继承控制 @touseefbsb:什么? x:Bind 确实在资源字典中定义的 ControlTemplates 中工作。你在说什么?

以上是关于继承控件中的 UWP 数据绑定的主要内容,如果未能解决你的问题,请参考以下文章

uwp:以编程方式进行数据绑定问题

UWP SemanticZoom组绑定

UWP 绑定到 MVVM 中的 AutoSuggestBox

UWP 数据绑定:如何将按钮命令设置为 DataTemplate 中的父 DataContext

UWP 从 TemplateSelector 更改为用户控件不再显示绑定

WPF 数据验证中的绑定失败