调整代码

This commit is contained in:
GG Z
2026-02-22 20:03:42 +08:00
parent 2ad3d0fde0
commit 7e2d5be3cd
258 changed files with 2916 additions and 5013 deletions

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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}">

View File

@@ -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}"

View File

@@ -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" />

View File

@@ -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" />

View File

@@ -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);

View File

@@ -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_TrackWPF 原生接管容器 -->
<!--
<Grid x:Name="PART_Track">
-->
<!-- 正常状态进度条:命名为 PART_Indicator不绑WidthWPF自动算比例 -->
<!--
<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_TrackWPF 原生接管容器 -->
<Grid x:Name="PART_Track">
<!-- 正常状态进度条:命名为 PART_Indicator不绑WidthWPF自动算比例 -->
<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}" />

View File

@@ -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>

View 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>

View 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
}

View File

@@ -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" />

View 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>

View 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;
}
}