继承控件中的 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 绑定到 MVVM 中的 AutoSuggestBox
UWP 数据绑定:如何将按钮命令设置为 DataTemplate 中的父 DataContext