WPF - 从PSD文件创建ProgressBar模板

Mis*_*siu 11 c# wpf wpf-controls

我正在使用WPF开始我的冒险,在创建我的第一个应用程序后,我想要设置它的样式.我找到了UI模板并使用Blend for VS2013我将PSD导入到我的项目中.

以下是ProgressBar在PSD中的外观: 在此输入图像描述

以下是导入Blend时的外观: 在此输入图像描述

这是代码:

    <Canvas x:Name="Progress1" ClipToBounds="True" HorizontalAlignment="Left" Height="52" UseLayoutRounding="False" VerticalAlignment="Top" Width="493" Margin="0,307.5,0,-53.5">
        <Canvas x:Name="Loading" Height="52" Canvas.Left="0" Canvas.Top="0" Width="493">
            <Path x:Name="Base2" Data="F1M22.086,3C22.086,3 63.118,4.562 125.833,3 199.069,1.175 294.072,5.645 370.146,4.333 430.323,3.294 474,3 474,3 479.523,3 487.826,8.208 489.687,15.098 491.864,23.156 491.191,28.867 489.081,37.118 487.415,43.637 479.856,47.999 474.333,47.999 474.333,47.999 368.324,50.176 252.792,47.999 135.568,45.792 42.104,49.541 23.518,47.999 12.306,47.07 6.028,45.811 4.028,37.787 3.199,34.461 1.441,23.222 7.178,11.906 10.179,5.987 16.563,3 22.086,3z" Height="52" Canvas.Left="0" Canvas.Top="0" Width="493" StrokeThickness="2">
                <Path.Stroke>
                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                        <GradientStop Color="#FFC18A13" Offset="1"/>
                        <GradientStop Color="#FFDC9A0C" Offset="0.339"/>
                    </LinearGradientBrush>
                </Path.Stroke>
                <Path.Fill>
                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                        <GradientStop Color="#FFE4882D" Offset="0"/>
                        <GradientStop Color="#FFF5CA09" Offset="1"/>
                    </LinearGradientBrush>
                </Path.Fill>
            </Path>
            <Path x:Name="Bg" Data="F1M16.361,2.603C16.361,2.603 133.014,3.416 247.396,3.478 311.817,3.513 376.242,2.615 416.922,1.936 446.114,1.448 458.772,2.411 458.772,2.411 462.592,2.411 469.449,4.823 471.077,9.484 473.896,17.557 472.201,20.776 471.202,25.468 470.232,30.02 467.977,31.719 459.43,33.25 450.883,34.782 424.628,32.594 376,32.594 298.703,32.594 184.467,31.065 105.75,30.911 54.767,30.812 18.683,32.063 17.185,32.063 9.403,32.063 6.954,28.298 5.436,25.402 4.335,23.303 1.86,15.809 6.797,8.253 9.308,4.41 12.541,2.603 16.361,2.603z" Fill="#FFA77235" Height="36" Canvas.Left="9" Canvas.Top="8" Width="475"/>
            <Path x:Name="Progress" Data="F1M19.986,2.29C19.986,2.29 50.058,4.582 104.021,2.936 154.279,1.403 214.797,4.02 264,4.02 310.844,4.02 341.117,2.457 347.659,2.936 354.201,3.415 356.173,5.804 357.743,10.484 359.313,15.162 360.055,20.568 357.202,26.468 355.175,30.658 353.597,31.417 347.492,33.396 345.484,34.047 309.622,34.937 262.208,34.943 217.536,34.948 162.63,33.886 116.105,33.683 61.905,33.446 19.087,34.063 17.185,34.063 9.403,34.063 6.016,31.048 4.498,28.152 3.397,26.053 1.86,15.809 6.797,8.253 9.308,4.41 16.166,2.29 19.986,2.29z" Height="36" Canvas.Left="8" Canvas.Top="7" Width="362">
                <Path.Fill>
                    <LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
                        <GradientStop Color="#FF5DFF4E" Offset="0.409"/>
                        <GradientStop Color="#FF159308" Offset="1"/>
                    </LinearGradientBrush>
                </Path.Fill>
            </Path>
        </Canvas>
    </Canvas>
Run Code Online (Sandbox Code Playgroud)

我的问题是如何将该画布转换为ProgressBar模板.

根据教程我能够创建模板,但有矩形而不是路径.我尝试过使用路径,但我无法正确对齐它们.

如何创建使用Path作为PART_Track和PART_Indicator的模板以及如何向ProgressBar添加文本?

我想这是3层:组件背景(橙色),进度背景(棕色)和进度条(绿色).也许我应该使用两层:进度条和中风背景?

这是我的第一个模板,所以我想尽我所能.

我在SO上找不到类似的问题(除了蜗牛进度条,但解决方案是基于图像而不是路径).

Evk*_*Evk 1

我将向您介绍如何从该模板制作工作进度条。

首先,我们创建自定义wpf控件并继承ProgressBar:

public class MyProgressBar : ProgressBar {
    static MyProgressBar() {
        DefaultStyleKeyProperty.OverrideMetadata(typeof (MyProgressBar), new FrameworkPropertyMetadata(typeof (MyProgressBar)));
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我们进入 Themes/Generic.xaml 文件(如果不存在,则由 Visual Studio 为我们创建)并创建控件的外观:

<local:MyProgressBarWidthConverter x:Key="width" />
<Style TargetType="{x:Type local:MyProgressBar}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyProgressBar}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <Viewbox Stretch="Fill">
                        <Canvas x:Name="Progress1" ClipToBounds="True" HorizontalAlignment="Left" Height="52" UseLayoutRounding="False" VerticalAlignment="Top" Width="493">
                            <Canvas x:Name="Loading" Height="52" Canvas.Left="0" Canvas.Top="0" Width="493">
                                <Path x:Name="Base2" Data="F1M22.086,3C22.086,3 63.118,4.562 125.833,3 199.069,1.175 294.072,5.645 370.146,4.333 430.323,3.294 474,3 474,3 479.523,3 487.826,8.208 489.687,15.098 491.864,23.156 491.191,28.867 489.081,37.118 487.415,43.637 479.856,47.999 474.333,47.999 474.333,47.999 368.324,50.176 252.792,47.999 135.568,45.792 42.104,49.541 23.518,47.999 12.306,47.07 6.028,45.811 4.028,37.787 3.199,34.461 1.441,23.222 7.178,11.906 10.179,5.987 16.563,3 22.086,3z" Height="52" Canvas.Left="0" Canvas.Top="0" Width="493" StrokeThickness="2">
                                    <Path.Stroke>
                                        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                            <GradientStop Color="#FFC18A13" Offset="1"/>
                                            <GradientStop Color="#FFDC9A0C" Offset="0.339"/>
                                        </LinearGradientBrush>
                                    </Path.Stroke>
                                    <Path.Fill>
                                        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                            <GradientStop Color="#FFE4882D" Offset="0"/>
                                            <GradientStop Color="#FFF5CA09" Offset="1"/>
                                        </LinearGradientBrush>
                                    </Path.Fill>
                                </Path>
                                <Path x:Name="Bg" Data="F1M16.361,2.603C16.361,2.603 133.014,3.416 247.396,3.478 311.817,3.513 376.242,2.615 416.922,1.936 446.114,1.448 458.772,2.411 458.772,2.411 462.592,2.411 469.449,4.823 471.077,9.484 473.896,17.557 472.201,20.776 471.202,25.468 470.232,30.02 467.977,31.719 459.43,33.25 450.883,34.782 424.628,32.594 376,32.594 298.703,32.594 184.467,31.065 105.75,30.911 54.767,30.812 18.683,32.063 17.185,32.063 9.403,32.063 6.954,28.298 5.436,25.402 4.335,23.303 1.86,15.809 6.797,8.253 9.308,4.41 12.541,2.603 16.361,2.603z" Fill="#FFA77235" Height="36" Canvas.Left="9" Canvas.Top="8" Width="475"/>
                                <Viewbox Stretch="UniformToFill" Canvas.Left="8" Canvas.Top="7" Height="36">
                                    <Viewbox.Width>
                                        <MultiBinding Converter="{StaticResource width}">
                                            <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Minimum" />
                                            <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value" />
                                            <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Maximum" />
                                        </MultiBinding>
                                    </Viewbox.Width>
                                    <Path x:Name="Progress" Data="F1M19.986,2.29C19.986,2.29 50.058,4.582 104.021,2.936 154.279,1.403 214.797,4.02 264,4.02 310.844,4.02 341.117,2.457 347.659,2.936 354.201,3.415 356.173,5.804 357.743,10.484 359.313,15.162 360.055,20.568 357.202,26.468 355.175,30.658 353.597,31.417 347.492,33.396 345.484,34.047 309.622,34.937 262.208,34.943 217.536,34.948 162.63,33.886 116.105,33.683 61.905,33.446 19.087,34.063 17.185,34.063 9.403,34.063 6.016,31.048 4.498,28.152 3.397,26.053 1.86,15.809 6.797,8.253 9.308,4.41 16.166,2.29 19.986,2.29z" >
                                        <Path.Fill>
                                            <LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
                                                <GradientStop Color="#FF5DFF4E" Offset="0.409"/>
                                                <GradientStop Color="#FF159308" Offset="1"/>
                                            </LinearGradientBrush>
                                        </Path.Fill>
                                    </Path>
                                </Viewbox>
                            </Canvas>
                        </Canvas>
                    </Viewbox>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
Run Code Online (Sandbox Code Playgroud)

基本上,我们只是使用 Stretch=Fill 将 psd 中的整个画布放入 Viewbox 中(并且还删除了不必要的边距)。请注意,所有尺寸都是相同的并且是硬编码的,但是因为我们将控件放在视图框内,所以它将拉伸到视图框的尺寸。因为这是带有 Stretch=Fill 的视图框,并且没有指定宽度和高度 - 它将拉伸到控件的大小。我们还将对应于绿色填充的 Path 放入其自己的视图框中,因为我们需要根据 ProgressBar.Value 参数调整该 Path 的大小。

现在我们为绿色路径的视图框宽度创建转换器:

 public class MyProgressBarWidthConverter : IMultiValueConverter {
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
        if (values.Any(c => c == null || c == DependencyProperty.UnsetValue))
            return 0.0d;
        var min = (double) values[0];
        var current = (double) values[1];
        var max = (double) values[2];
        const double maxWidth = 475; // that is from template
        return (current/(max - min))*maxWidth;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) {
        throw new NotImplementedException();
    }
}
Run Code Online (Sandbox Code Playgroud)

在那里我们传递进度条的最小值、值、最大值并计算绿条宽度。最大可用宽度始终为 475,但请记住我们将其放入视图框中,因此我们的控件不固定为 475 宽度。

然后我们将控件放在窗口中:

 <wpf:MyProgressBar x:Name="bar" Width="500" Height="50" Value="5" Minimum="0" Maximum="100" />
Run Code Online (Sandbox Code Playgroud)

隐藏代码:

public partial class MainWindow : Window {
    public MainWindow() {
        InitializeComponent();
        var timer = new DispatcherTimer()
        {
            Interval = TimeSpan.FromSeconds(1)
        };
        timer.Tick += (o, e) =>
        {
            if (bar.Value < bar.Maximum)
                bar.Value++;
            else
                timer.Stop();
        };
        timer.Start();
    }
} 
Run Code Online (Sandbox Code Playgroud)

并观察我们的进度条如何工作。一般来说,如果您有繁重的 UI,您可能希望使用简单的表单,因为视图框中的许多路径效率不高。但如果您必须使用 PSD 的固定路径...为什么不呢。

编辑以回答您的评论。当然,不需要创建新的控件,它只是更灵活。如果您不想这样做,只需为进度条创建控件模板并分配给现有的 ProgressBar.ControlTemplate,如下所示:

<Window x:Class="Wpf.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:wpf="clr-namespace:Wpf"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <wpf:MyProgressBarWidthConverter x:Key="width" />
    <ControlTemplate x:Key="myProgressBar" TargetType="{x:Type ProgressBar}">
        <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
            <Viewbox Stretch="Fill">
                <Canvas x:Name="Progress1" ClipToBounds="True" HorizontalAlignment="Left" Height="52" UseLayoutRounding="False" VerticalAlignment="Top" Width="493">
                    <Canvas x:Name="Loading" Height="52" Canvas.Left="0" Canvas.Top="0" Width="493">
                        <Path x:Name="Base2" Data="F1M22.086,3C22.086,3 63.118,4.562 125.833,3 199.069,1.175 294.072,5.645 370.146,4.333 430.323,3.294 474,3 474,3 479.523,3 487.826,8.208 489.687,15.098 491.864,23.156 491.191,28.867 489.081,37.118 487.415,43.637 479.856,47.999 474.333,47.999 474.333,47.999 368.324,50.176 252.792,47.999 135.568,45.792 42.104,49.541 23.518,47.999 12.306,47.07 6.028,45.811 4.028,37.787 3.199,34.461 1.441,23.222 7.178,11.906 10.179,5.987 16.563,3 22.086,3z" Height="52" Canvas.Left="0" Canvas.Top="0" Width="493" StrokeThickness="2">
                            <Path.Stroke>
                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                    <GradientStop Color="#FFC18A13" Offset="1"/>
                                    <GradientStop Color="#FFDC9A0C" Offset="0.339"/>
                                </LinearGradientBrush>
                            </Path.Stroke>
                            <Path.Fill>
                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                    <GradientStop Color="#FFE4882D" Offset="0"/>
                                    <GradientStop Color="#FFF5CA09" Offset="1"/>
                                </LinearGradientBrush>
                            </Path.Fill>
                        </Path>
                        <Path x:Name="Bg" Data="F1M16.361,2.603C16.361,2.603 133.014,3.416 247.396,3.478 311.817,3.513 376.242,2.615 416.922,1.936 446.114,1.448 458.772,2.411 458.772,2.411 462.592,2.411 469.449,4.823 471.077,9.484 473.896,17.557 472.201,20.776 471.202,25.468 470.232,30.02 467.977,31.719 459.43,33.25 450.883,34.782 424.628,32.594 376,32.594 298.703,32.594 184.467,31.065 105.75,30.911 54.767,30.812 18.683,32.063 17.185,32.063 9.403,32.063 6.954,28.298 5.436,25.402 4.335,23.303 1.86,15.809 6.797,8.253 9.308,4.41 12.541,2.603 16.361,2.603z" Fill="#FFA77235" Height="36" Canvas.Left="9" Canvas.Top="8" Width="475"/>
                        <Viewbox Stretch="UniformToFill" Canvas.Left="8" Canvas.Top="7" Height="36">
                            <Viewbox.Width>
                                <MultiBinding Converter="{StaticResource width}">
                                    <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Minimum" />
                                    <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value" />
                                    <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Maximum" />
                                </MultiBinding>
                            </Viewbox.Width>
                            <Path x:Name="Progress" Data="F1M19.986,2.29C19.986,2.29 50.058,4.582 104.021,2.936 154.279,1.403 214.797,4.02 264,4.02 310.844,4.02 341.117,2.457 347.659,2.936 354.201,3.415 356.173,5.804 357.743,10.484 359.313,15.162 360.055,20.568 357.202,26.468 355.175,30.658 353.597,31.417 347.492,33.396 345.484,34.047 309.622,34.937 262.208,34.943 217.536,34.948 162.63,33.886 116.105,33.683 61.905,33.446 19.087,34.063 17.185,34.063 9.403,34.063 6.016,31.048 4.498,28.152 3.397,26.053 1.86,15.809 6.797,8.253 9.308,4.41 16.166,2.29 19.986,2.29z" >
                                <Path.Fill>
                                    <LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
                                        <GradientStop Color="#FF5DFF4E" Offset="0.409"/>
                                        <GradientStop Color="#FF159308" Offset="1"/>
                                    </LinearGradientBrush>
                                </Path.Fill>
                            </Path>
                        </Viewbox>
                    </Canvas>
                </Canvas>
            </Viewbox>
        </Border>
    </ControlTemplate>
</Window.Resources>
<ProgressBar x:Name="bar" Width="500" Height="50" Value="5" Minimum="0" Maximum="100" Template="{StaticResource myProgressBar}"/>
Run Code Online (Sandbox Code Playgroud)