WPF学习笔记02-简单美化计算器

<0x00> WPF的界面美化挺方便的

就研究了一小时不到,成功让计算器的UI上升了一个档次

先看成果(因为相较于之前的界面确实帅多了)
全新界面
现代多了
(为了显示效果改了下比例)
咱们一块块慢慢讲

<0x01> 如何实现自定义窗口控件与圆角窗口

首先看这个界面,比较特殊的就是这个窗口有圆角
而且右上角的按钮不是Windows的默认样式
(红色的按钮是关闭,黄色的是最小化窗口)

要做到这一点,我们要让窗口运行在无边框的模式下
但运行在无边框模式下后,原先的窗口控件也没有了
所以要自己写按钮实现窗口关闭和窗口最小化的功能
(窗口最大化也差不多的逻辑,但这里不需要实现)

无边框窗口

首先要设置个无边框的窗口
我们需要在<Window>标签里面加上WindowStyle="None"就可以实现无边框的窗口了

(@ 23-07-19)
其实更正规的做法应该是用WindowChrome,当时不大会,这个只能说是个下策
这个方法本质上是抛弃了非工作区的控件而在工作区重写这些窗口控件,效率有点低

<Window x:Class="WPFTest.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:local="clr-namespace:WPFTest"
        mc:Ignorable="d"
        Height="400" Width="300"
        WindowStartupLocation="CenterScreen" AllowsTransparency="True"
        Background="Transparent" WindowStyle="None">
        <!--...-->
<Window/>

(差不多就这样,没讲的属性后面讲)

这样,我们确实获得了一个无边框的窗口
但这样有几个问题
第一,窗口的圆角无法调整
第二,窗口不能拖动

解决第一个问题

这意味着我们要自己写一个窗口实现
(其实不难)
系统的窗口相当于最下面的图层
现在这个窗口不能满足我们的需求,所以我们要先隐藏自带的窗口
<Window>标签里面加上AllowsTransparency="True"Background="Transparent"
这两条属性表示允许窗口透明并设置窗口背景为透明

下面我们要自己写一个背景来代替原来的窗口背景
我们之前不是拿一个<Grid>包裹所有的控件吗
既然是背景,那层级肯定是比这个Grid更高的
所以只需要在<Grid>外面再套个<Border>就好
(<Border>可以简单理解为绘制一个矩形)

<!--差不多像这样-->
<Window ...>
    <Border ...>
        <Grid ...>
            <!--...-->
        </Grid>
    </Border>
</Window>

<Border>也可以设置不少的属性
为了实现窗口圆角,设置下CornerRadius就好

解决第二个问题

在原生的窗口中,窗口的移动是靠上面的那一条的
相当于窗口移动事件是绑定在那一条控件上的

现在我们隐藏了,所以要自己去绑定这个窗口移动事件
现在刚好写了个<Border>,这个可以绑定窗口移动事件
<Border>绑定MouseMove事件(就是添加个属性)
这个相当于之前给Button绑定Click事件,要在对应.cs里面实现功能

private void Border_MouseMove(object sender, MouseEventArgs e)
{
    if (e.LeftButton == MouseButtonState.Pressed)
    {
        this.DragMove();
    }
}

加上这段代码就好
现在这个窗口就能拖动了

自定义窗口控件按钮

本质上就是自己写个按钮,一个用来关闭程序,一个用来最小化
(当然为了能放下这俩按钮还需要对页面布局做个小改动,这里就不写了)
直接贴代码

//关闭窗口
private void Button_Close_Click(object sender, RoutedEventArgs e)
{
    this.Close();
}
//最小化窗口
private void Button_Minimize_Click(object sender, RoutedEventArgs e)
{
    this.WindowState = WindowState.Minimized;
}

这两个方法绑定到对应的按钮就好

<0x02> 如何实现圆角的按钮

要实现圆角的按钮,我们需要重写按钮的绘制方法
xaml中,这个重写其实也挺简单的,我们仅需更改按钮的模板

更改模板的方法有两种,先讲怎么更改单个按钮的模板
(关闭窗口和最小化窗口的按钮样式就是这么实现的)

<Button ...>
    <Button.Template>
        <ControlTemplate>
            <!--具体的实现-->
        </ControlTemplate>
    </Button.Template>
</Button>

在具体的实现里面写上绘制的样式就行了

如果有很多的按钮需要更改为一个统一的样式,我们需要在<Style>中更改模板

<Style TargetType="Button">
    <!--其他Setter-->
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <!--具体的实现-->
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

注意,如果模板要绑定属性,TargetType="Button"不能省
({TemplateBinding Content}就是个绑定的语法,相当于拷贝Button.Content的内容)
(具体看原代码吧,绑定确实不大好解释)

更改过模板后,原来的触发器也给替换掉了,要我们自己再写一套
(就是比方说鼠标放按钮上,按钮的颜色变深一点之类的)
这个就要在<ControlTemplate>块中加上<ControlTemplate.Triggers>
再在里面写上若干<Trigger>即可
<Trigger>就用来控制前端的显示,不管后端的

<Trigger>中有Property(设置触发器类型)和Value(设置触发值)
常用的PropertyIsMouseOn(鼠标在不在上面)和IsPressed(鼠标有没有按下)
<Trigger>块中通过<Setter>来设置样式
这里的<Setter>需要加上TargetName(因为可能有好几个需要绘制的东西)

<0x03> 如何实现背景的色彩渐变

添加色彩渐变很简单
<Border>块中加上这些代码即可

<Border.Background>
    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
        <GradientStop Color="#392669" Offset="0"/>
        <GradientStop Color="#46204F" Offset="1"/>
    </LinearGradientBrush>
</Border.Background>

其中<LinearGradientBrush>指定了线性渐变
StartPointEndPoint指定了起止点,"0,0"表示左上角,"0,1"表示左下角
<GradientStop>中可以指定渐变的颜色,Offset相当于颜色在渐变条上的位置
(如果用过些图像处理工具应该是能理解Offset的功能的)

<0x04> 绘制窗口阴影

因为我们已经把原生的窗口干掉了嘛,所以窗口阴影也没了
没窗口阴影的话总是看着有些违和

添加窗口阴影也很简单
<Border>块中加上这些代码即可

<Border.Effect>
    <DropShadowEffect Color="Gray" ShadowDepth="0" BlurRadius="10" Opacity=".5" Direction="0"/>
</Border.Effect>

<Border>中也要调整下Margin属性,不然阴影可能显示不出

附录

MainWindow.xaml.cs的代码就不贴了,基本没什么改动

<!--MainWindow.xaml-->
<Window x:Class="WPFTest.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:local="clr-namespace:WPFTest"
        mc:Ignorable="d"
        Height="400" Width="300"
        WindowStartupLocation="CenterScreen" AllowsTransparency="True"
        Background="Transparent" WindowStyle="None">
    <Window.Resources>
        <Style TargetType="Button">
            <Setter Property="Margin" Value="6"/>
            <Setter Property="FontSize" Value="24"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Name="border" Background="#241238" CornerRadius="10">
                            <TextBlock Text="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="border" Property="Background" Value="#190D24"/>
                            </Trigger>
                            <Trigger Property="IsPressed" Value="True">
                                <Setter TargetName="border" Property="Background" Value="Black"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Border CornerRadius="10" MouseMove="Border_MouseMove" Margin="5">
        <Border.Background>
            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                <GradientStop Color="#392669" Offset="0"/>
                <GradientStop Color="#46204F" Offset="1"/>
            </LinearGradientBrush>
        </Border.Background>
        <Border.Effect>
            <DropShadowEffect Color="Gray" ShadowDepth="0" BlurRadius="10" Opacity=".5" Direction="0"/>
        </Border.Effect>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Right">
                <Button Name="Button_Minimize" Click="Button_Minimize_Click">
                    <Button.Template>
                        <ControlTemplate>
                            <Ellipse Width="16" Height="16" Fill="#F0DC4E"/>
                        </ControlTemplate>
                    </Button.Template>
                </Button>
                <Button Name="Button_Close" Click="Button_Close_Click">
                    <Button.Template>
                        <ControlTemplate>
                            <Ellipse Width="16" Height="16" Fill="#F0443E"/>
                        </ControlTemplate>
                    </Button.Template>
                </Button>
            </StackPanel>
            <TextBlock Grid.Row="1" Name="Answer" Text="0" FontSize="28" HorizontalAlignment="Right" Margin="20" Foreground="White"/>
            <Grid Grid.Row="2">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <Button Name="Button_MinusSign" Click="Button_MinusSign_Click"
                    Content="±" Grid.Column="0" Grid.Row="0"/>
                <Button Name="Button_Clear" Click="Button_Clear_Click"
                    Content="CE" Grid.Column="1" Grid.Row="0"/>
                <Button Name="Button_Backspace" Click="Button_Backspace_Click"
                    Content="←" Grid.Column="2" Grid.Row="0"/>
                <Button Name="Button_Num9" Click="Button_Num9_Click"
                    Content="9" Grid.Column="2" Grid.Row="1"/>
                <Button Name="Button_Num8" Click="Button_Num8_Click"
                    Content="8" Grid.Column="1" Grid.Row="1"/>
                <Button Name="Button_Num7" Click="Button_Num7_Click"
                    Content="7" Grid.Column="0" Grid.Row="1"/>
                <Button Name="Button_Num6" Click="Button_Num6_Click"
                    Content="6" Grid.Column="2" Grid.Row="2"/>
                <Button Name="Button_Num5" Click="Button_Num5_Click"
                    Content="5" Grid.Column="1" Grid.Row="2"/>
                <Button Name="Button_Num4" Click="Button_Num4_Click"
                    Content="4" Grid.Column="0" Grid.Row="2"/>
                <Button Name="Button_Num3" Click="Button_Num3_Click"
                    Content="3" Grid.Column="2" Grid.Row="3"/>
                <Button Name="Button_Num2" Click="Button_Num2_Click"
                    Content="2" Grid.Column="1" Grid.Row="3"/>
                <Button Name="Button_Num1" Click="Button_Num1_Click"
                    Content="1" Grid.Column="0" Grid.Row="3"/>
                <Button Name="Button_Num0" Click="Button_Num0_Click"
                    Content="0" Grid.ColumnSpan="2" Grid.Column="0" Grid.Row="4"/>
                <Button Name="Button_Dot" Click="Button_Dot_Click"
                    Content="." Grid.Column="2" Grid.Row="4"/>
                <Button Name="Button_Div" Click="Button_Div_Click"
                    Content="/" Grid.Column="3" Grid.Row="0"/>
                <Button Name="Button_Mul" Click="Button_Mul_Click"
                    Content="*" Grid.Column="3" Grid.Row="1"/>
                <Button Name="Button_Sub" Click="Button_Sub_Click"
                    Content="-" Grid.Column="3" Grid.Row="2"/>
                <Button Name="Button_Add" Click="Button_Add_Click"
                    Content="+" Grid.Column="3" Grid.Row="3"/>
                <Button Name="Button_Equal" Click="Button_Equal_Click"
                    Content="=" Grid.Column="3" Grid.Row="4"/>
            </Grid>
        </Grid>
    </Border>
</Window>

(经典写博客耗时比写代码多😂)

Licensed under CC BY-NC-SA 4.0