调整代码
This commit is contained in:
@@ -526,6 +526,7 @@
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="Padding" Value="12,8" />
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:Melskin.Controls"
|
||||
xmlns:decorations="clr-namespace:Melskin.Controls.Decorations">
|
||||
<Style TargetType="{x:Type controls:Card}">
|
||||
<Style x:Key="FloatLightedCardStyle" TargetType="{x:Type controls:Card}">
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="Padding" Value="10,8" />
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
@@ -54,4 +54,39 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type controls:Card}">
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="Padding" Value="10,8" />
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="Background" Value="{DynamicResource BackgroundContainerBrush}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextPrimaryBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource BorderNormalBrush}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type controls:Card}">
|
||||
<Border
|
||||
x:Name="mainBorder"
|
||||
Padding="0"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<ContentPresenter
|
||||
x:Name="contentPresenter"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Focusable="False"
|
||||
RecognizesAccessKey="True"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
|
||||
TextElement.Foreground="{TemplateBinding Foreground}" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -186,7 +186,7 @@
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type CheckBox}">
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="MinWidth" Value="200" />
|
||||
<Setter Property="Padding" Value="3" />
|
||||
<Setter Property="Padding" Value="4" />
|
||||
<Setter Property="ScrollViewer.CanContentScroll" Value="False" />
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
|
||||
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
|
||||
@@ -162,7 +162,7 @@
|
||||
x:Name="ContentBorder"
|
||||
MinWidth="{TemplateBinding MinWidth}"
|
||||
MinHeight="{TemplateBinding MinHeight}"
|
||||
Padding="0"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="{TemplateBinding Background}"
|
||||
@@ -178,6 +178,7 @@
|
||||
Name="PlaceholderTextBlock"
|
||||
Margin="5,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Focusable="False"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
Foreground="{DynamicResource TextPlaceholderBrush}"
|
||||
Text="{TemplateBinding PlaceholderText}"
|
||||
@@ -185,7 +186,6 @@
|
||||
<ScrollViewer
|
||||
x:Name="PART_ContentHost"
|
||||
Grid.Column="0"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
CanContentScroll="{TemplateBinding ScrollViewer.CanContentScroll}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource BorderNormalBrush}" />
|
||||
<Setter Property="Background" Value="{DynamicResource BackgroundLayoutBrush}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextPrimaryBrush}" />
|
||||
<Setter Property="Margin" Value="4" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="Padding" Value="4" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
|
||||
@@ -73,7 +73,8 @@
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextPrimaryBrush}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="Padding" Value="12,8" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="Padding" Value="12,7" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="assists:InputAssist.Clearable" Value="True" />
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
|
||||
|
||||
@@ -286,7 +286,7 @@ public class MultiComboBox : Control
|
||||
/// <remarks>
|
||||
/// 该属性包含了从数据源中筛选出的项,这些项将被实际显示在组合框中。通过设置此属性,可以控制哪些项最终展示给用户。当数据源发生变化或应用了新的筛选条件时,FilteredItems 会相应地更新。
|
||||
/// </remarks>
|
||||
public IEnumerable FilteredItems
|
||||
public IEnumerable? FilteredItems
|
||||
{
|
||||
get => (IEnumerable)GetValue(FilteredItemsProperty);
|
||||
set => SetValue(FilteredItemsProperty, value);
|
||||
|
||||
@@ -201,6 +201,196 @@
|
||||
</Style>
|
||||
<!--#endregion-->
|
||||
|
||||
<!--<ControlTemplate x:Key="HorizontalProgressTemplate" TargetType="{x:Type ProgressBar}">
|
||||
<ControlTemplate.Resources>
|
||||
<Storyboard x:Key="ProgressIndeterminateHorizontal" RepeatBehavior="Forever">
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground.(Brush.RelativeTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">
|
||||
<EasingDoubleKeyFrame KeyTime="0" Value="-1" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</ControlTemplate.Resources>
|
||||
|
||||
<Border
|
||||
x:Name="backgroundBorder"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4">
|
||||
|
||||
-->
|
||||
<!-- 核心改变:命名为 PART_Track,WPF 原生接管容器 -->
|
||||
<!--
|
||||
<Grid x:Name="PART_Track">
|
||||
-->
|
||||
<!-- 正常状态进度条:命名为 PART_Indicator,不绑Width,WPF自动算比例! -->
|
||||
<!--
|
||||
<Border
|
||||
x:Name="PART_Indicator"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{TemplateBinding Foreground}"
|
||||
CornerRadius="3" />
|
||||
|
||||
-->
|
||||
<!-- Indeterminate 动画层:默认隐藏,充满整个宽度 -->
|
||||
<!--
|
||||
<Border
|
||||
x:Name="IndeterminateLayer"
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{TemplateBinding Foreground}"
|
||||
CornerRadius="3"
|
||||
Visibility="Collapsed" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<ControlTemplate.Triggers>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsIndeterminate" Value="True" />
|
||||
<Condition Property="IsEnabled" Value="True" />
|
||||
</MultiTrigger.Conditions>
|
||||
-->
|
||||
<!-- 进入不确定状态时,隐藏原进度条,显示充满的动画层 -->
|
||||
<!--
|
||||
<Setter TargetName="PART_Indicator" Property="Visibility" Value="Collapsed" />
|
||||
<Setter TargetName="IndeterminateLayer" Property="Visibility" Value="Visible" />
|
||||
|
||||
<Setter Property="Foreground" Value="{DynamicResource PrimaryIndeterminateGradientBrush}" />
|
||||
<MultiTrigger.EnterActions>
|
||||
<BeginStoryboard x:Name="beginIndeterminate" Storyboard="{StaticResource ProgressIndeterminateHorizontal}" />
|
||||
</MultiTrigger.EnterActions>
|
||||
<MultiTrigger.ExitActions>
|
||||
<RemoveStoryboard BeginStoryboardName="beginIndeterminate" />
|
||||
</MultiTrigger.ExitActions>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>-->
|
||||
<!--<ControlTemplate x:Key="ProgressVerticalTemplate" TargetType="{x:Type ProgressBar}">
|
||||
<ControlTemplate.Resources>
|
||||
<Storyboard x:Key="ProgressIndeterminateVertical" RepeatBehavior="Forever">
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground.(Brush.RelativeTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
|
||||
<EasingDoubleKeyFrame KeyTime="0" Value="1" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="-1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</ControlTemplate.Resources>
|
||||
<Border
|
||||
x:Name="backgroundBorder"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4">
|
||||
<Grid x:Name="BarGrid" VerticalAlignment="Bottom">-->
|
||||
<!-- 计算进度条宽度 -->
|
||||
<!--
|
||||
<Grid.Height>
|
||||
<MultiBinding Converter="{x:Static internal:ValueToRangeWidthConverter.Instance}" ConverterParameter="1">
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Minimum"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Maximum"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Value"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="ActualHeight"
|
||||
RelativeSource="{RelativeSource AncestorType={x:Type Border}}" />
|
||||
</MultiBinding>
|
||||
</Grid.Height>-->
|
||||
<!-- 进度条 -->
|
||||
<!--
|
||||
<Border Background="{TemplateBinding Foreground}" CornerRadius="3" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsIndeterminate" Value="True" />
|
||||
<Condition Property="IsEnabled" Value="True" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="BarGrid" Property="Height" Value="auto" />
|
||||
<Setter TargetName="BarGrid" Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource PrimaryIndeterminateGradientBrush}" />
|
||||
<MultiTrigger.EnterActions>
|
||||
<BeginStoryboard x:Name="beginIndeterminate" Storyboard="{StaticResource ProgressIndeterminateVertical}" />
|
||||
</MultiTrigger.EnterActions>
|
||||
<MultiTrigger.ExitActions>
|
||||
<RemoveStoryboard BeginStoryboardName="beginIndeterminate" />
|
||||
</MultiTrigger.ExitActions>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>-->
|
||||
|
||||
<!--<ControlTemplate x:Key="ProgressVerticalTemplate" TargetType="{x:Type ProgressBar}">
|
||||
<ControlTemplate.Resources>
|
||||
<Storyboard x:Key="ProgressIndeterminateVertical" RepeatBehavior="Forever">
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground.(Brush.RelativeTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
|
||||
<EasingDoubleKeyFrame KeyTime="0" Value="1" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="-1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</ControlTemplate.Resources>
|
||||
<Border
|
||||
x:Name="backgroundBorder"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4">
|
||||
<Grid x:Name="BarGrid" VerticalAlignment="Bottom">-->
|
||||
<!-- 计算进度条宽度 -->
|
||||
<!--
|
||||
<Grid.Height>
|
||||
<MultiBinding Converter="{x:Static internal:ValueToRangeWidthConverter.Instance}" ConverterParameter="1">
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Minimum"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Maximum"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Value"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="ActualHeight"
|
||||
RelativeSource="{RelativeSource AncestorType={x:Type Border}}" />
|
||||
</MultiBinding>
|
||||
</Grid.Height>-->
|
||||
<!-- 进度条 -->
|
||||
<!--
|
||||
<Border Background="{TemplateBinding Foreground}" CornerRadius="3" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsIndeterminate" Value="True" />
|
||||
<Condition Property="IsEnabled" Value="True" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="BarGrid" Property="Height" Value="auto" />
|
||||
<Setter TargetName="BarGrid" Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource PrimaryIndeterminateGradientBrush}" />
|
||||
<MultiTrigger.EnterActions>
|
||||
<BeginStoryboard x:Name="beginIndeterminate" Storyboard="{StaticResource ProgressIndeterminateVertical}" />
|
||||
</MultiTrigger.EnterActions>
|
||||
<MultiTrigger.ExitActions>
|
||||
<RemoveStoryboard BeginStoryboardName="beginIndeterminate" />
|
||||
</MultiTrigger.ExitActions>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>-->
|
||||
|
||||
<ControlTemplate x:Key="HorizontalProgressTemplate" TargetType="{x:Type ProgressBar}">
|
||||
<ControlTemplate.Resources>
|
||||
<Storyboard x:Key="ProgressIndeterminateHorizontal" RepeatBehavior="Forever">
|
||||
@@ -210,50 +400,43 @@
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</ControlTemplate.Resources>
|
||||
|
||||
<Border
|
||||
x:Name="backgroundBorder"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4">
|
||||
<Grid x:Name="BarGrid" HorizontalAlignment="Left">
|
||||
<!-- 计算进度条宽度 -->
|
||||
<Grid.Width>
|
||||
<MultiBinding Converter="{x:Static internal:ValueToRangeWidthConverter.Instance}" ConverterParameter="7">
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Minimum"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Maximum"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Value"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="ActualWidth"
|
||||
RelativeSource="{RelativeSource AncestorType={x:Type Border}}" />
|
||||
</MultiBinding>
|
||||
</Grid.Width>
|
||||
<!-- 进度条 -->
|
||||
|
||||
<!-- 核心改变:命名为 PART_Track,WPF 原生接管容器 -->
|
||||
<Grid x:Name="PART_Track">
|
||||
<!-- 正常状态进度条:命名为 PART_Indicator,不绑Width,WPF自动算比例! -->
|
||||
<Border
|
||||
x:Name="borderProgress"
|
||||
x:Name="PART_Indicator"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{TemplateBinding Foreground}"
|
||||
CornerRadius="3" />
|
||||
</Grid>
|
||||
|
||||
<!-- Indeterminate 动画层:默认隐藏,充满整个宽度 -->
|
||||
<Border
|
||||
x:Name="IndeterminateLayer"
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{TemplateBinding Foreground}"
|
||||
CornerRadius="3"
|
||||
Visibility="Collapsed" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<ControlTemplate.Triggers>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsIndeterminate" Value="True" />
|
||||
<Condition Property="IsEnabled" Value="True" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="BarGrid" Property="Width" Value="auto" />
|
||||
<Setter TargetName="BarGrid" Property="HorizontalAlignment" Value="Stretch" />
|
||||
<!-- 进入不确定状态时,隐藏原进度条,显示充满的动画层 -->
|
||||
<Setter TargetName="PART_Indicator" Property="Visibility" Value="Collapsed" />
|
||||
<Setter TargetName="IndeterminateLayer" Property="Visibility" Value="Visible" />
|
||||
|
||||
<Setter Property="Foreground" Value="{DynamicResource PrimaryIndeterminateGradientBrush}" />
|
||||
<MultiTrigger.EnterActions>
|
||||
<BeginStoryboard x:Name="beginIndeterminate" Storyboard="{StaticResource ProgressIndeterminateHorizontal}" />
|
||||
@@ -264,6 +447,8 @@
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
|
||||
|
||||
<ControlTemplate x:Key="ProgressVerticalTemplate" TargetType="{x:Type ProgressBar}">
|
||||
<ControlTemplate.Resources>
|
||||
<Storyboard x:Key="ProgressIndeterminateVertical" RepeatBehavior="Forever">
|
||||
@@ -273,47 +458,66 @@
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</ControlTemplate.Resources>
|
||||
|
||||
<Border
|
||||
x:Name="backgroundBorder"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4">
|
||||
<Grid x:Name="BarGrid" VerticalAlignment="Bottom">
|
||||
<!-- 计算进度条宽度 -->
|
||||
<Grid.Height>
|
||||
<MultiBinding Converter="{x:Static internal:ValueToRangeWidthConverter.Instance}" ConverterParameter="7">
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Minimum"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Maximum"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Value"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="ActualHeight"
|
||||
RelativeSource="{RelativeSource AncestorType={x:Type Border}}" />
|
||||
</MultiBinding>
|
||||
</Grid.Height>
|
||||
<!-- 进度条 -->
|
||||
<Border Background="{TemplateBinding Foreground}" CornerRadius="3" />
|
||||
|
||||
<!-- 这里不再使用 PART_Track 命名,避免WPF原生逻辑干预 -->
|
||||
<Grid x:Name="BarGrid">
|
||||
|
||||
<!-- 正常状态进度条:靠底对齐,通过 Converter 计算高度 -->
|
||||
<Border
|
||||
x:Name="borderProgress"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="{TemplateBinding Foreground}"
|
||||
CornerRadius="3">
|
||||
<Border.Height>
|
||||
<!-- 将 ConverterParameter 改为 0,保证 100% 时完美充满 -->
|
||||
<MultiBinding Converter="{x:Static internal:ValueToRangeWidthConverter.Instance}" ConverterParameter="0">
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Minimum"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Maximum"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
Mode="OneWay"
|
||||
Path="Value"
|
||||
RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding
|
||||
ElementName="BarGrid"
|
||||
Mode="OneWay"
|
||||
Path="ActualHeight" />
|
||||
</MultiBinding>
|
||||
</Border.Height>
|
||||
</Border>
|
||||
|
||||
<!-- Indeterminate 动画层:默认隐藏,充满整个高度 -->
|
||||
<Border
|
||||
x:Name="IndeterminateLayer"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="{TemplateBinding Foreground}"
|
||||
CornerRadius="3"
|
||||
Visibility="Collapsed" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
|
||||
<ControlTemplate.Triggers>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsIndeterminate" Value="True" />
|
||||
<Condition Property="IsEnabled" Value="True" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="BarGrid" Property="Height" Value="auto" />
|
||||
<Setter TargetName="BarGrid" Property="VerticalAlignment" Value="Stretch" />
|
||||
<!-- 切换状态时控制 Visibility 隐藏计算好的进度条,显示充满的动画层 -->
|
||||
<Setter TargetName="borderProgress" Property="Visibility" Value="Collapsed" />
|
||||
<Setter TargetName="IndeterminateLayer" Property="Visibility" Value="Visible" />
|
||||
|
||||
<Setter Property="Foreground" Value="{DynamicResource PrimaryIndeterminateGradientBrush}" />
|
||||
<MultiTrigger.EnterActions>
|
||||
<BeginStoryboard x:Name="beginIndeterminate" Storyboard="{StaticResource ProgressIndeterminateVertical}" />
|
||||
@@ -324,6 +528,87 @@
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
<Style x:Key="CircularProgressBarStyle" TargetType="ProgressBar">
|
||||
<Setter Property="Foreground" Value="{DynamicResource PrimaryNormalBrush}" />
|
||||
<Setter Property="Background" Value="{DynamicResource ControlBackgroundNormalBrush}" />
|
||||
<Setter Property="Width" Value="100" />
|
||||
<Setter Property="Height" Value="100" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ProgressBar">
|
||||
<Viewbox>
|
||||
<Grid Width="100" Height="100">
|
||||
<!-- 底色轨道 -->
|
||||
<Ellipse
|
||||
Width="100"
|
||||
Height="100"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Stroke="{TemplateBinding Background}"
|
||||
StrokeThickness="10" />
|
||||
|
||||
<!-- 进度层 -->
|
||||
<Path
|
||||
x:Name="ProgressPath"
|
||||
RenderTransformOrigin="0.5,0.5"
|
||||
Stretch="None"
|
||||
Stroke="{TemplateBinding Foreground}"
|
||||
StrokeEndLineCap="Round"
|
||||
StrokeStartLineCap="Round"
|
||||
StrokeThickness="10">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform x:Name="RotateTransform" Angle="0" />
|
||||
</Path.RenderTransform>
|
||||
<Path.Data>
|
||||
<MultiBinding Converter="{x:Static internal:ProgressToAngleConverter.Instance}">
|
||||
<Binding Path="Value" RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding Path="Maximum" RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
</MultiBinding>
|
||||
</Path.Data>
|
||||
</Path>
|
||||
|
||||
<!-- 文字显示(仅在非不确定模式下显示) -->
|
||||
<TextBlock
|
||||
x:Name="ValueText"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="20"
|
||||
FontWeight="Bold"
|
||||
Text="{Binding Value, RelativeSource={RelativeSource TemplatedParent}, StringFormat={}{0:0}%}" />
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
|
||||
<ControlTemplate.Triggers>
|
||||
<!-- 不确定进度状态(转圈圈) -->
|
||||
<Trigger Property="IsIndeterminate" Value="True">
|
||||
<!-- 1. 隐藏文字 -->
|
||||
<Setter TargetName="ValueText" Property="Visibility" Value="Collapsed" />
|
||||
<!-- 2. 给 Path 一个固定的弧线数据 (大约 25% 长度) -->
|
||||
<!-- M 50,5 代表起点;A 45,45... 代表半径45的圆弧到 95,50 -->
|
||||
<Setter TargetName="ProgressPath" Property="Data" Value="M 50,5 A 45,45 0 0 1 95,50" />
|
||||
<!-- 3. 执行旋转动画 -->
|
||||
<Trigger.EnterActions>
|
||||
<BeginStoryboard Name="SpinAnimation">
|
||||
<Storyboard>
|
||||
<DoubleAnimation
|
||||
RepeatBehavior="Forever"
|
||||
Storyboard.TargetName="ProgressPath"
|
||||
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
|
||||
From="0"
|
||||
To="360"
|
||||
Duration="0:0:1" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</Trigger.EnterActions>
|
||||
<Trigger.ExitActions>
|
||||
<StopStoryboard BeginStoryboardName="SpinAnimation" />
|
||||
</Trigger.ExitActions>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ProgressBar}">
|
||||
<Setter Property="Foreground" Value="{DynamicResource PrimaryGradientBrush}" />
|
||||
|
||||
@@ -375,19 +375,19 @@
|
||||
Height="15"
|
||||
Fill="{DynamicResource ControlBackgroundNormalBrush}"
|
||||
StrokeThickness="1">
|
||||
<!--<Ellipse.Effect>
|
||||
<Ellipse.Effect>
|
||||
<DropShadowEffect
|
||||
BlurRadius="12"
|
||||
Opacity="0.4"
|
||||
Color="{DynamicResource DarkShadowColor}" />
|
||||
</Ellipse.Effect>-->
|
||||
</Ellipse.Effect>
|
||||
</Ellipse>
|
||||
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True" />
|
||||
<Trigger Property="IsDragging" Value="True">
|
||||
<!--<Setter TargetName="grip" Property="Effect">
|
||||
<Setter TargetName="grip" Property="Effect">
|
||||
<Setter.Value>
|
||||
<DropShadowEffect
|
||||
BlurRadius="4"
|
||||
@@ -395,7 +395,7 @@
|
||||
ShadowDepth="3"
|
||||
Color="{DynamicResource DarkShadowColor}" />
|
||||
</Setter.Value>
|
||||
</Setter>-->
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="grip" Property="Effect" Value="{x:Null}" />
|
||||
@@ -436,6 +436,7 @@
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Width="8"
|
||||
Margin="0,5,0,5"
|
||||
HorizontalAlignment="center"
|
||||
BorderBrush="{DynamicResource BorderNormalBrush}"
|
||||
BorderThickness="1"
|
||||
@@ -510,10 +511,9 @@
|
||||
x:Name="ValueDisplay"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource PrimaryNormalBrush}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
Text="{Binding Value, RelativeSource={RelativeSource TemplatedParent}, StringFormat=F1}">
|
||||
<TextBlock.LayoutTransform>
|
||||
<RotateTransform Angle="-90" />
|
||||
@@ -577,6 +577,7 @@
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Height="8"
|
||||
Margin="5,0,5,0"
|
||||
VerticalAlignment="center"
|
||||
BorderBrush="{DynamicResource BorderNormalBrush}"
|
||||
BorderThickness="1"
|
||||
@@ -649,15 +650,13 @@
|
||||
Fill="{TemplateBinding Foreground}"
|
||||
Placement="Bottom"
|
||||
Visibility="Collapsed" />
|
||||
<!-- 新增:简单的值显示TextBlock,放在滑块下方 -->
|
||||
<TextBlock
|
||||
x:Name="ValueDisplay"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="4,0,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource PrimaryNormalBrush}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
Text="{Binding Value, RelativeSource={RelativeSource TemplatedParent}, StringFormat=F1}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
121
Melskin/Controls/StepBar.xaml
Normal file
121
Melskin/Controls/StepBar.xaml
Normal file
@@ -0,0 +1,121 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Melskin.Controls">
|
||||
<Style TargetType="{x:Type local:StepBarItem}">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
<!-- 添加手型光标 -->
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type local:StepBarItem}">
|
||||
<!-- 使用透明背景的 Grid 扩大点击区域 -->
|
||||
<Grid
|
||||
x:Name="Root"
|
||||
Margin="2"
|
||||
Background="Transparent">
|
||||
<StackPanel x:Name="Container" Orientation="{TemplateBinding Orientation}">
|
||||
<Grid>
|
||||
<Ellipse
|
||||
x:Name="Node"
|
||||
Width="28"
|
||||
Height="28"
|
||||
Fill="#222"
|
||||
Stroke="{DynamicResource TextSecondaryBrush}"
|
||||
StrokeThickness="2" />
|
||||
<TextBlock
|
||||
x:Name="IndexText"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Foreground="White"
|
||||
Text="{TemplateBinding Index}" />
|
||||
<Path
|
||||
x:Name="CheckMark"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Data="M2,5 L5,8 L10,2"
|
||||
Stroke="White"
|
||||
StrokeThickness="2"
|
||||
Visibility="Collapsed" />
|
||||
</Grid>
|
||||
<ContentPresenter Margin="8,4" VerticalAlignment="Center" />
|
||||
<Rectangle
|
||||
x:Name="Line"
|
||||
Margin="4"
|
||||
Fill="{DynamicResource TextSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="Status" Value="Completed">
|
||||
<Setter TargetName="Node" Property="Fill" Value="{DynamicResource PrimaryNormalBrush}" />
|
||||
<Setter TargetName="Node" Property="Stroke" Value="{DynamicResource PrimaryNormalBrush}" />
|
||||
<Setter TargetName="IndexText" Property="Visibility" Value="Collapsed" />
|
||||
<Setter TargetName="CheckMark" Property="Visibility" Value="Visible" />
|
||||
<Setter TargetName="Line" Property="Fill" Value="{DynamicResource PrimaryNormalBrush}" />
|
||||
<!-- 添加一个小动画 -->
|
||||
<Trigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation
|
||||
Storyboard.TargetName="CheckMark"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
From="0"
|
||||
To="1"
|
||||
Duration="0:0:0.2" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</Trigger.EnterActions>
|
||||
</Trigger>
|
||||
<Trigger Property="Orientation" Value="Horizontal">
|
||||
<Setter TargetName="Line" Property="Width" Value="40" />
|
||||
<Setter TargetName="Line" Property="Height" Value="2" />
|
||||
<Setter TargetName="Line" Property="VerticalAlignment" Value="Center" />
|
||||
<Setter TargetName="Node" Property="VerticalAlignment" Value="Center" />
|
||||
</Trigger>
|
||||
<Trigger Property="Orientation" Value="Vertical">
|
||||
<Setter TargetName="Line" Property="Width" Value="2" />
|
||||
<Setter TargetName="Line" Property="Height" Value="30" />
|
||||
<Setter TargetName="Line" Property="HorizontalAlignment" Value="Center" />
|
||||
<Setter TargetName="Node" Property="HorizontalAlignment" Value="Center" />
|
||||
</Trigger>
|
||||
<Trigger Property="Status" Value="Active">
|
||||
<Setter TargetName="Node" Property="Stroke" Value="{DynamicResource PrimaryNormalBrush}" />
|
||||
<Setter TargetName="IndexText" Property="Foreground" Value="{DynamicResource PrimaryNormalBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="Status" Value="Completed">
|
||||
<Setter TargetName="Node" Property="Fill" Value="{DynamicResource PrimaryNormalBrush}" />
|
||||
<Setter TargetName="Node" Property="Stroke" Value="{DynamicResource PrimaryNormalBrush}" />
|
||||
<Setter TargetName="IndexText" Property="Visibility" Value="Collapsed" />
|
||||
<Setter TargetName="CheckMark" Property="Visibility" Value="Visible" />
|
||||
<Setter TargetName="Line" Property="Fill" Value="{DynamicResource PrimaryNormalBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsLast" Value="True">
|
||||
<Setter TargetName="Line" Property="Visibility" Value="Collapsed" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- StepBar Style -->
|
||||
<Style TargetType="{x:Type local:StepBar}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type local:StepBar}">
|
||||
<ItemsPresenter />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<!-- 通过绑定方向来切换面板 -->
|
||||
<StackPanel Orientation="{Binding Orientation, RelativeSource={RelativeSource AncestorType=local:StepBar}}" />
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
124
Melskin/Controls/StepBar.xaml.cs
Normal file
124
Melskin/Controls/StepBar.xaml.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Melskin.Controls
|
||||
{
|
||||
public class StepBar : ItemsControl
|
||||
{
|
||||
// 使用 StepProgress 代替 SelectedIndex,允许值范围 0 到 Items.Count
|
||||
public static readonly DependencyProperty StepProgressProperty =
|
||||
DependencyProperty.Register("StepProgress", typeof(int), typeof(StepBar),
|
||||
new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnStepProgressChanged));
|
||||
|
||||
public int StepProgress
|
||||
{
|
||||
get => (int)GetValue(StepProgressProperty);
|
||||
set => SetValue(StepProgressProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty OrientationProperty =
|
||||
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(StepBar),
|
||||
new PropertyMetadata(Orientation.Horizontal));
|
||||
|
||||
public Orientation Orientation
|
||||
{
|
||||
get => (Orientation)GetValue(OrientationProperty);
|
||||
set => SetValue(OrientationProperty, value);
|
||||
}
|
||||
|
||||
static StepBar()
|
||||
{
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(StepBar), new FrameworkPropertyMetadata(typeof(StepBar)));
|
||||
}
|
||||
|
||||
private static void OnStepProgressChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is StepBar bar) bar.UpdateItemsStatus();
|
||||
}
|
||||
|
||||
protected override DependencyObject GetContainerForItemOverride() => new StepBarItem();
|
||||
protected override bool IsItemItsOwnContainerOverride(object item) => item is StepBarItem;
|
||||
|
||||
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
|
||||
{
|
||||
base.PrepareContainerForItemOverride(element, item);
|
||||
UpdateItemsStatus();
|
||||
}
|
||||
|
||||
internal void UpdateItemsStatus()
|
||||
{
|
||||
for (int i = 0; i < Items.Count; i++)
|
||||
{
|
||||
if (ItemContainerGenerator.ContainerFromIndex(i) is StepBarItem container)
|
||||
{
|
||||
container.Index = i + 1;
|
||||
container.IsLast = (i == Items.Count - 1);
|
||||
container.Orientation = this.Orientation;
|
||||
|
||||
// 核心逻辑:
|
||||
// i < Progress -> 已完成 (Completed)
|
||||
// i == Progress -> 进行中 (Active)
|
||||
// i > Progress -> 等待 (Waiting)
|
||||
if (i < StepProgress)
|
||||
container.Status = StepStatus.Completed;
|
||||
else if (i == StepProgress)
|
||||
container.Status = StepStatus.Active;
|
||||
else
|
||||
container.Status = StepStatus.Waiting;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class StepBarItem : ContentControl
|
||||
{
|
||||
public static readonly DependencyProperty StatusProperty = DependencyProperty.Register("Status", typeof(StepStatus), typeof(StepBarItem), new PropertyMetadata(StepStatus.Waiting));
|
||||
public StepStatus Status { get => (StepStatus)GetValue(StatusProperty); set => SetValue(StatusProperty, value); }
|
||||
|
||||
public static readonly DependencyProperty IndexProperty = DependencyProperty.Register("Index", typeof(int), typeof(StepBarItem), new PropertyMetadata(1));
|
||||
public int Index { get => (int)GetValue(IndexProperty); set => SetValue(IndexProperty, value); }
|
||||
|
||||
public static readonly DependencyProperty IsLastProperty = DependencyProperty.Register("IsLast", typeof(bool), typeof(StepBarItem), new PropertyMetadata(false));
|
||||
public bool IsLast { get => (bool)GetValue(IsLastProperty); set => SetValue(IsLastProperty, value); }
|
||||
|
||||
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(StepBarItem), new PropertyMetadata(Orientation.Horizontal));
|
||||
public Orientation Orientation { get => (Orientation)GetValue(OrientationProperty); set => SetValue(OrientationProperty, value); }
|
||||
|
||||
static StepBarItem()
|
||||
{
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(StepBarItem), new FrameworkPropertyMetadata(typeof(StepBarItem)));
|
||||
}
|
||||
|
||||
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
|
||||
{
|
||||
base.OnMouseLeftButtonDown(e);
|
||||
var parent = ItemsControl.ItemsControlFromItemContainer(this) as StepBar;
|
||||
if (parent != null)
|
||||
{
|
||||
int myIndex = parent.ItemContainerGenerator.IndexFromContainer(this);
|
||||
|
||||
// 点击逻辑:
|
||||
// 1. 如果点的是当前正在进行的步 -> 完成这一步,进度+1
|
||||
// 2. 如果点的是已经完成的步 -> 回退到那一步
|
||||
// 3. 如果点的是还没开始的步 -> 直接跳到那一步
|
||||
if (parent.StepProgress == myIndex)
|
||||
{
|
||||
parent.StepProgress = myIndex + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.StepProgress = myIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum StepStatus
|
||||
{
|
||||
Completed,
|
||||
Active,
|
||||
Waiting
|
||||
}
|
||||
@@ -379,6 +379,7 @@
|
||||
<Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
|
||||
<Setter Property="AllowDrop" Value="True" />
|
||||
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
|
||||
|
||||
149
Melskin/Controls/TutorialPrompt.xaml
Normal file
149
Melskin/Controls/TutorialPrompt.xaml
Normal file
@@ -0,0 +1,149 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Melskin.Controls">
|
||||
|
||||
<Style TargetType="{x:Type local:TutorialHighlighter}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type local:TutorialHighlighter}">
|
||||
<Grid>
|
||||
<!-- 呼吸灯效果的边框 -->
|
||||
<Border
|
||||
x:Name="BreathBorder"
|
||||
BorderBrush="{DynamicResource PrimaryNormalBrush}"
|
||||
BorderThickness="2"
|
||||
CornerRadius="4">
|
||||
<Border.Effect>
|
||||
<BlurEffect Radius="8" />
|
||||
</Border.Effect>
|
||||
|
||||
<!-- 把动画触发器放到元素自身,保证名称解析在正确的名称域内 -->
|
||||
<Border.Triggers>
|
||||
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
|
||||
<BeginStoryboard>
|
||||
<Storyboard AutoReverse="True" RepeatBehavior="Forever">
|
||||
<DoubleAnimation
|
||||
Storyboard.TargetName="BreathBorder"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
From="0.3"
|
||||
To="1"
|
||||
Duration="0:0:1" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</EventTrigger>
|
||||
</Border.Triggers>
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
BorderBrush="{DynamicResource PrimaryNormalBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type local:TutorialGuide}">
|
||||
<Setter Property="Background" Value="{DynamicResource BackgroundContainerBrush}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextPrimaryBrush}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type local:TutorialGuide}">
|
||||
<!-- 增加外层 Grid 以容纳箭头 -->
|
||||
<Grid x:Name="LayoutRoot" Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 主体卡片 -->
|
||||
<Border
|
||||
x:Name="MainBorder"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="350"
|
||||
Padding="16"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{DynamicResource BorderSecondaryBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="6">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect
|
||||
BlurRadius="20"
|
||||
Opacity="0.4"
|
||||
ShadowDepth="4" />
|
||||
</Border.Effect>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Header -->
|
||||
<DockPanel Margin="0,0,0,12">
|
||||
<TextBlock
|
||||
FontSize="16"
|
||||
FontWeight="Bold"
|
||||
Foreground="{DynamicResource PrimaryNormalBrush}"
|
||||
Text="{TemplateBinding Title}" />
|
||||
<Button
|
||||
x:Name="PART_CloseBtn"
|
||||
HorizontalAlignment="Right"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Content="✕"
|
||||
Cursor="Hand"
|
||||
DockPanel.Dock="Right"
|
||||
Foreground="{DynamicResource TextSecondaryBrush}" />
|
||||
</DockPanel>
|
||||
|
||||
<!-- Content -->
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,0,0,20"
|
||||
Foreground="{DynamicResource TextPrimaryBrush}"
|
||||
LineHeight="20"
|
||||
Text="{TemplateBinding Description}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<!-- Footer -->
|
||||
<Grid Grid.Row="2">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource TextSecondaryBrush}"
|
||||
Text="{TemplateBinding StepText}" />
|
||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="PART_BackBtn"
|
||||
Margin="0,0,8,0"
|
||||
Padding="12,5"
|
||||
Background="{DynamicResource BackgroundOverlayBrush}"
|
||||
BorderThickness="0"
|
||||
Content="上一步"
|
||||
Foreground="{DynamicResource TextAccentBrush}" />
|
||||
<Button
|
||||
x:Name="PART_NextBtn"
|
||||
Padding="15,5"
|
||||
Background="{DynamicResource PrimaryNormalBrush}"
|
||||
BorderThickness="0"
|
||||
Content="下一步"
|
||||
Foreground="{DynamicResource TextAccentBrush}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
147
Melskin/Controls/TutorialPrompt.xaml.cs
Normal file
147
Melskin/Controls/TutorialPrompt.xaml.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Melskin.Controls
|
||||
{
|
||||
public class TutorialGuide : Control
|
||||
{
|
||||
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(TutorialGuide), new PropertyMetadata("Tutorial"));
|
||||
public string Title { get => (string)GetValue(TitleProperty); set => SetValue(TitleProperty, value); }
|
||||
|
||||
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(nameof(Description), typeof(string), typeof(TutorialGuide), new PropertyMetadata(string.Empty));
|
||||
public string Description { get => (string)GetValue(DescriptionProperty); set => SetValue(DescriptionProperty, value); }
|
||||
|
||||
public static readonly DependencyProperty StepTextProperty = DependencyProperty.Register(nameof(StepText), typeof(string), typeof(TutorialGuide), new PropertyMetadata("1 / 1"));
|
||||
public string StepText { get => (string)GetValue(StepTextProperty); set => SetValue(StepTextProperty, value); }
|
||||
|
||||
// 路由事件
|
||||
public static readonly RoutedEvent NextEvent = EventManager.RegisterRoutedEvent(nameof(Next), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TutorialGuide));
|
||||
public event RoutedEventHandler Next { add => AddHandler(NextEvent, value); remove => RemoveHandler(NextEvent, value); }
|
||||
|
||||
public static readonly RoutedEvent BackEvent = EventManager.RegisterRoutedEvent(nameof(Back), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TutorialGuide));
|
||||
public event RoutedEventHandler Back { add => AddHandler(BackEvent, value); remove => RemoveHandler(BackEvent, value); }
|
||||
|
||||
public static readonly RoutedEvent CloseEvent = EventManager.RegisterRoutedEvent(nameof(Close), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TutorialGuide));
|
||||
public event RoutedEventHandler Close { add => AddHandler(CloseEvent, value); remove => RemoveHandler(CloseEvent, value); }
|
||||
public static readonly DependencyProperty PlacementProperty =
|
||||
DependencyProperty.Register("Placement", typeof(PlacementMode), typeof(TutorialGuide), new PropertyMetadata(PlacementMode.Top));
|
||||
|
||||
public PlacementMode Placement
|
||||
{
|
||||
get => (PlacementMode)GetValue(PlacementProperty);
|
||||
set => SetValue(PlacementProperty, value);
|
||||
}
|
||||
static TutorialGuide()
|
||||
{
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(TutorialGuide), new FrameworkPropertyMetadata(typeof(TutorialGuide)));
|
||||
}
|
||||
|
||||
public override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
if (GetTemplateChild("PART_NextBtn") is Button next) next.Click += (s, e) => RaiseEvent(new RoutedEventArgs(NextEvent));
|
||||
if (GetTemplateChild("PART_BackBtn") is Button back) back.Click += (s, e) => RaiseEvent(new RoutedEventArgs(BackEvent));
|
||||
if (GetTemplateChild("PART_CloseBtn") is Button close) close.Click += (s, e) => RaiseEvent(new RoutedEventArgs(CloseEvent));
|
||||
}
|
||||
}
|
||||
public class TutorialHighlighter : Control
|
||||
{
|
||||
static TutorialHighlighter()
|
||||
{
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(TutorialHighlighter), new FrameworkPropertyMetadata(typeof(TutorialHighlighter)));
|
||||
}
|
||||
|
||||
// 目标控件属性
|
||||
public static readonly DependencyProperty TargetElementProperty =
|
||||
DependencyProperty.Register("TargetElement", typeof(FrameworkElement), typeof(TutorialHighlighter), new PropertyMetadata(null, OnTargetChanged));
|
||||
|
||||
public FrameworkElement TargetElement
|
||||
{
|
||||
get => (FrameworkElement)GetValue(TargetElementProperty);
|
||||
set => SetValue(TargetElementProperty, value);
|
||||
}
|
||||
|
||||
private static void OnTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is TutorialHighlighter helper) helper.UpdatePosition();
|
||||
}
|
||||
|
||||
public void UpdatePosition()
|
||||
{
|
||||
if (TargetElement == null) return;
|
||||
|
||||
// 获取 TutorialLayer (也就是当前的父容器 Canvas)
|
||||
var parentCanvas = VisualTreeHelper.GetParent(this) as UIElement;
|
||||
if (parentCanvas == null) return;
|
||||
|
||||
// 关键修改:相对于 parentCanvas 计算坐标,而不是相对于 Window
|
||||
Point location = TargetElement.TranslatePoint(new Point(0, 0), parentCanvas);
|
||||
|
||||
// 加上偏移量(让边框比目标稍微大一点,包围住目标)
|
||||
double offset = 4;
|
||||
Canvas.SetLeft(this, location.X - offset);
|
||||
Canvas.SetTop(this, location.Y - offset);
|
||||
|
||||
this.Width = TargetElement.ActualWidth + (offset * 2);
|
||||
this.Height = TargetElement.ActualHeight + (offset * 2);
|
||||
}
|
||||
}
|
||||
public class TutorialLayer : Canvas
|
||||
{
|
||||
private TutorialHighlighter _highlighter;
|
||||
private TutorialGuide _guide;
|
||||
|
||||
public TutorialLayer()
|
||||
{
|
||||
// 初始化两个组件
|
||||
_highlighter = new TutorialHighlighter();
|
||||
_guide = new TutorialGuide();
|
||||
|
||||
this.Children.Add(_highlighter);
|
||||
this.Children.Add(_guide);
|
||||
|
||||
// 初始隐藏
|
||||
this.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public void ShowStep(FrameworkElement target, string title, string content, string step)
|
||||
{
|
||||
this.Visibility = Visibility.Visible;
|
||||
|
||||
// 1. 更新文字
|
||||
_guide.Title = title;
|
||||
_guide.Description = content;
|
||||
_guide.StepText = step;
|
||||
|
||||
// 2. 强制刷新布局(确保目标控件的 ActualWidth/Height 已计算)
|
||||
target.UpdateLayout();
|
||||
|
||||
// 3. 定位高亮框 (Highlighter)
|
||||
_highlighter.TargetElement = target;
|
||||
_highlighter.UpdatePosition();
|
||||
|
||||
// 4. 定位提示框 (Guide)
|
||||
// 获取高亮框在当前 Canvas 里的位置
|
||||
double hLeft = Canvas.GetLeft(_highlighter);
|
||||
double hTop = Canvas.GetTop(_highlighter);
|
||||
double hWidth = _highlighter.Width;
|
||||
double hHeight = _highlighter.Height;
|
||||
|
||||
// 默认逻辑:放在目标右侧。如果右侧放不下,放在左侧。
|
||||
double guideLeft = hLeft + hWidth + 10;
|
||||
double guideTop = hTop;
|
||||
|
||||
if (guideLeft + 350 > this.ActualWidth) // 350 是 TutorialGuide 的宽度
|
||||
{
|
||||
guideLeft = hLeft - 350 - 10; // 放左边
|
||||
}
|
||||
|
||||
Canvas.SetLeft(_guide, guideLeft);
|
||||
Canvas.SetTop(_guide, guideTop);
|
||||
}
|
||||
public void Close() => this.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user