HandyControl
HandyControl copied to clipboard
鼠标悬停在带 ShowClearButton 的 TextBox 的边缘时发生闪烁
Describe the bug
移动鼠标指针到 hc:TextBox 的边缘(靠近 Clear 按钮处),发现 ClearButton 快速闪烁。
这个问题有些控件可以通过设置 UseLayoutRounding=False 来解决,但是这个方法对 hc:TextBox 无效。
更新
我发现官方 Demo 也会有这个问题。
Steps to reproduce the bug
<hc:TextBox hc:InfoElement.ShowClearButton="True" Text="1234567890"/>
Expected behavior
No response
Screenshots
No response
NuGet package version
HandyControl 3.4.0
IDE
Visual Studio 2022
Framework type
.Net 6.0
Windows version
Windows 11 (22000)
Additional context
No response
https://github.com/HandyOrg/HandyControl/blob/babf40e59fe9e57eb185e4658ce46036610083f9/src/Shared/HandyControl_Shared/Themes/Styles/Base/TextBoxBaseStyle.xaml#L172-L192
解决方案:此处的 root 元素建议改为:
<hc:SimplePanel x:Name="root"
Grid.Column="1">
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}" />
<TextBlock Margin="{TemplateBinding Padding}"
HorizontalAlignment="Stretch"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Style="{StaticResource TextBlockDefaultThiLight}"
Text="{Binding Path=(hc:InfoElement.Placeholder), RelativeSource={RelativeSource TemplatedParent}}"
Visibility="{TemplateBinding Text,
Converter={StaticResource String2VisibilityReConverter}}" />
<ScrollViewer x:Name="PART_ContentHost"
Margin="-2,0,0,0"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Focusable="false"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden" />
<Button Name="ButtonClear"
Width="Auto"
Height="Auto"
Padding="8,0"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Left"
hc:BorderElement.CornerRadius="4"
hc:IconElement.Geometry="{StaticResource DeleteFillCircleGeometry}"
hc:IconElement.Width="14"
Background="{TemplateBinding Background}"
Command="{x:Static hc:ControlCommands.Clear}"
Foreground="{Binding BorderBrush, ElementName=border}"
Style="{StaticResource ButtonIcon}"
Visibility="Collapsed" />
</hc:SimplePanel>
TextBoxPlusTopTemplate 、SearchBar(带 Clear 按钮) 及有类似实现的地方也需一并更改。
TopTemplate 中需要使用 hc:TitleElement.MarginOnTheTop,但实际没有这个属性(#1498),暂时使用硬编码。
不使用两列的 Grid 还有一个好处,就是如果我使用 WrapPanel,当 TextBox 接近 WrapPanel 一行的宽度时,如果鼠标悬停在 TextBox 上面,那么 TextBox 会因为显示 Clear 按钮而变宽,导致换行,而换行后又失去鼠标悬停使得 Clear 按钮被隐藏折叠,TextBox 宽度减少而回退位置,循环往复,此效果如下:
SearchBar 带 Clear 按钮闪烁问题解决方案:
<ControlTemplate x:Key="SearchBarPlusTopTemplate"
TargetType="hc:SearchBar">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="{Binding Path=(hc:InfoElement.ContentHeight), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource Double2GridLengthConverter}}"
MinHeight="{Binding Path=(hc:InfoElement.MinContentHeight), RelativeSource={RelativeSource TemplatedParent}}" />
</Grid.RowDefinitions>
<DockPanel Margin="6,0,0,1"
HorizontalAlignment="{Binding Path=(hc:TitleElement.HorizontalAlignment), RelativeSource={RelativeSource TemplatedParent}}"
LastChildFill="True"
Visibility="{Binding Path=(hc:InfoElement.Title), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource String2VisibilityConverter}}">
<ContentPresenter Margin="4,0,0,0"
Content="{Binding Path=(hc:InfoElement.Symbol), RelativeSource={RelativeSource TemplatedParent}}"
DockPanel.Dock="Right"
TextElement.Foreground="{DynamicResource DangerBrush}"
Visibility="{Binding Path=(hc:InfoElement.Necessary), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource Boolean2VisibilityConverter}}" />
<TextBlock hc:TextBlockAttach.AutoTooltip="True"
Text="{Binding Path=(hc:InfoElement.Title), RelativeSource={RelativeSource TemplatedParent}}"
TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap" />
</DockPanel>
<Border x:Name="border"
Grid.Row="1"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}" />
<Grid x:Name="root"
Grid.Row="1"
SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Name="ButtonClear"
Width="Auto"
Height="Auto"
Padding="4,0"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Left"
⚠️ 设置 Panel.ZIndex="99" 为了防止代码格式化重排序,手动将 Clear 按钮置于文本框上,不然光标选中的是文本。
Panel.ZIndex="99"
hc:BorderElement.CornerRadius="4"
hc:IconElement.Geometry="{StaticResource DeleteFillCircleGeometry}"
hc:IconElement.Width="14"
Background="{TemplateBinding Background}"
Command="{x:Static hc:ControlCommands.Clear}"
Foreground="{Binding BorderBrush, ElementName=border}"
Style="{StaticResource ButtonIcon}"
Visibility="Collapsed" />
<TextBlock Grid.Row="0"
Grid.Column="0"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="Stretch"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Style="{StaticResource TextBlockDefaultThiLight}"
Text="{Binding Path=(hc:InfoElement.Placeholder), RelativeSource={RelativeSource TemplatedParent}}"
Visibility="{TemplateBinding Text,
Converter={StaticResource String2VisibilityReConverter}}" />
<ScrollViewer x:Name="PART_ContentHost"
Grid.Row="0"
Grid.Column="0"
Margin="-2,0"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Focusable="false"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden" />
<Button Grid.Row="0"
Grid.Column="1"
Width="Auto"
Height="Auto"
Padding="{Binding Padding, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ThicknessSplitConverter}, ConverterParameter='0,0,1,0'}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Left"
hc:IconElement.Geometry="{StaticResource SearchGeometry}"
hc:IconElement.Width="14"
Command="{x:Static hc:ControlCommands.Search}"
Focusable="False"
Foreground="{TemplateBinding BorderBrush}"
Style="{StaticResource ButtonIcon}" />
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="border" Property="Opacity" Value="0.4" />
<Setter TargetName="root" Property="Opacity" Value="0.4" />
</Trigger>
<Trigger SourceName="root" Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="{DynamicResource SecondaryBorderBrush}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition SourceName="root" Property="IsMouseOver" Value="true" />
<Condition Property="hc:InfoElement.ShowClearButton" Value="True" />
</MultiTrigger.Conditions>
<Setter TargetName="ButtonClear" Property="Visibility" Value="Visible" />
</MultiTrigger>
<Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" Value="{DynamicResource PrimaryBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="SearchBarPlusLeftTemplate"
TargetType="hc:SearchBar">
<Grid Height="{Binding Path=(hc:InfoElement.ContentHeight), RelativeSource={RelativeSource TemplatedParent}}"
MinHeight="{Binding Path=(hc:InfoElement.MinContentHeight), RelativeSource={RelativeSource TemplatedParent}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding Path=(hc:InfoElement.TitleWidth), RelativeSource={RelativeSource TemplatedParent}}" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<DockPanel Margin="{Binding Path=(hc:TitleElement.MarginOnTheLeft), RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="{Binding Path=(hc:TitleElement.HorizontalAlignment), RelativeSource={RelativeSource TemplatedParent}}"
VerticalAlignment="{Binding Path=(hc:TitleElement.VerticalAlignment), RelativeSource={RelativeSource TemplatedParent}}"
LastChildFill="True"
Visibility="{Binding Path=(hc:InfoElement.Title), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource String2VisibilityConverter}}">
<ContentPresenter Margin="4,0,0,0"
Content="{Binding Path=(hc:InfoElement.Symbol), RelativeSource={RelativeSource TemplatedParent}}"
DockPanel.Dock="Right"
TextElement.Foreground="{DynamicResource DangerBrush}"
Visibility="{Binding Path=(hc:InfoElement.Necessary), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource Boolean2VisibilityConverter}}" />
<TextBlock hc:TextBlockAttach.AutoTooltip="True"
Text="{Binding Path=(hc:InfoElement.Title), RelativeSource={RelativeSource TemplatedParent}}"
TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap" />
</DockPanel>
<Border x:Name="border"
Grid.Column="1"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}" />
<Grid x:Name="root"
Grid.Column="1"
SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Name="ButtonClear"
Width="Auto"
Height="Auto"
Padding="4,0"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Left"
⚠️ 设置 Panel.ZIndex="99" 为了防止代码格式化重排序,手动将 Clear 按钮置于文本框上,不然光标选中的是文本。
Panel.ZIndex="99"
hc:BorderElement.CornerRadius="4"
hc:IconElement.Geometry="{StaticResource DeleteFillCircleGeometry}"
hc:IconElement.Width="14"
Background="{TemplateBinding Background}"
Command="{x:Static hc:ControlCommands.Clear}"
Foreground="{Binding BorderBrush, ElementName=border}"
Style="{StaticResource ButtonIcon}"
Visibility="Collapsed" />
<TextBlock Grid.Row="0"
Grid.Column="0"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="Stretch"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Style="{StaticResource TextBlockDefaultThiLight}"
Text="{Binding Path=(hc:InfoElement.Placeholder), RelativeSource={RelativeSource TemplatedParent}}"
Visibility="{TemplateBinding Text,
Converter={StaticResource String2VisibilityReConverter}}" />
<ScrollViewer x:Name="PART_ContentHost"
Grid.Row="0"
Grid.Column="0"
Margin="-2,0"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Focusable="false"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden" />
<Button Grid.Row="0"
Grid.Column="1"
Width="Auto"
Height="Auto"
Padding="{Binding Padding, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ThicknessSplitConverter}, ConverterParameter='0,0,1,0'}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Left"
hc:IconElement.Geometry="{StaticResource SearchGeometry}"
hc:IconElement.Width="14"
Command="{x:Static hc:ControlCommands.Search}"
Focusable="False"
Foreground="{TemplateBinding BorderBrush}"
Style="{StaticResource ButtonIcon}" />
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="border" Property="Opacity" Value="0.4" />
<Setter TargetName="root" Property="Opacity" Value="0.4" />
</Trigger>
<Trigger SourceName="root" Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="{DynamicResource SecondaryBorderBrush}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition SourceName="root" Property="IsMouseOver" Value="true" />
<Condition Property="hc:InfoElement.ShowClearButton" Value="True" />
</MultiTrigger.Conditions>
<Setter TargetName="ButtonClear" Property="Visibility" Value="Visible" />
</MultiTrigger>
<Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" Value="{DynamicResource PrimaryBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>