深入浅出wpf(wpf入门教程)

背景

最近做4K屏幕高DPI支持,看到UWP概念和WPF UI框架,2者都会轻松支持4K屏幕高DPI,所以此时做些笔记研究下。

WPF的界面处理是描述性的,用的语言是XAML,XAML是标记语言的一种,其实Winform或较早的VC++里的RC,也是要用文本描述界面,但是习惯了使用VS的所见即所得界面设计方式,用XAML或HTML+CSS这类,写描述自己想界面结果很不适应。说白了,标记语言、手写脚本、分离的特征和非百分百所见即所得真是不适应,现在有软件在图片上拖一下就能出界面效果,还要先让适应手写,真是很不适应,特别是对初学者,加大了学习难度。

另外对WPF的趋势,本人不太看好,Web前端发展很快,写普通的界面Web前端形式更有优势,除非要求和Win平台无缝对接要求性能强点的界面才有可能用上WPF。总结就是WPF有机会用,学了就学了,反正它的思想在Web前端和小程序员这类开发上,也有可借鉴之处。

WPF

WPF代表Windows Presentation Foundation,是Microsoft对GUI框架的最新方法,与.NET框架一起使用。

GUI代表图形用户界面,您现在可能正在看一个。Windows具有用于与计算机配合使用的GUI,您很可能在其中阅读本文档的浏览器具有允许您上网冲浪的GUI。

GUI框架允许您使用各种GUI元素(例如标签,文本框和其他众所周知的元素)创建应用程序。如果没有GUI框架,则必须手动绘制这些元素并处理所有用户交互方案,例如文本和鼠标输入。这是一项繁重的工作,因此,大多数开发人员将使用GUI框架来完成所有基本工作,并允许开发人员专注于开发出色的应用程序。

目前有许多GUI框架,但是对于.NET开发人员而言,最有趣的框架是WinForms和WPF。WPF是最新的,但Microsoft仍在维护和支持WinForms,两个框架之间有很多差异,但是它们的目的是相同的:轻松使用出色的GUI创建应用程序。

WPF和WinForms比较

WinForms和WPF之间最重要的区别是,虽然WinForms只是标准Windows控件(例如TextBox)之上的一层,但WPF是从头开始构建的,并且在几乎所有情况下都不依赖于标准Windows控件。这看似细微的差别,但实际上并非如此,如果您曾经使用过依赖Win32/WinAPI的框架,则一定会注意到。

一个很好的例子是带有图像和文字的按钮。这不是标准的Windows控件,因此WinForms不会立即为您提供这种可能性。取而代之的是,您将必须自己绘制图像,实现支持图像的自己的按钮或使用第三方控件。使用WPF,按钮可以包含任何内容,因为它本质上是带有内容和各种状态(例如,未触摸,悬停,按下)的边框。WPF按钮与大多数其他WPF控件一样,都是“无外观”按钮,这意味着它可以在其中包含一系列其他控件。您想要一个带有图像和一些文本的按钮吗?只需将Image和TextBlock控件放在按钮内部,即可完成!您根本无法从标准WinForms控件中获得这种灵活性

这种灵活性的缺点在于,有时您需要付出更多的努力才能实现WinForms非常容易的事情,因为它是针对您所需的场景而创建的。至少从一开始就是这种感觉,您会发现自己创建模板来制作具有图像和一些对齐好的文本的ListView,这是WinForms ListViewItem在单行代码中所做的事情。

这只是一个差异,但是当您使用WPF时,您会意识到,这实际上是许多其他差异的根本原因-WPF只是以自己的方式做事,无论好坏。您不再受制于以Windows方式进行操作,而是要获得这种灵活性,而您实际上只是想以Windows方式进行操作时,您需要付出更多的工作。

以下是WPF和WinForms的主要优点的完整主观清单。它应该使您对要进行的工作有了更好的了解。

WPF的优势
它较新,因此与当前标准更加一致
Microsoft正在将其用于许多新应用程序,例如Visual Studio
它更加灵活,因此您无需编写或购买新控件就可以做更多的事情
当您确实需要使用第三方控件时,这些控件的开发人员可能会更专注于WPF,因为它较新
XAML使得创建和编辑GUI变得容易,并且允许在设计人员(XAML)和程序员(C#,VB.NET等)之间拆分工作。
数据绑定,使您可以更清晰地分离数据和布局
使用硬件加速来绘制GUI,以获得更好的性能
它允许您为Windows应用程序和Web应用程序(Silverlight/XBAP)制作用户界面
WinForms的优势
它比较老,因此经过更多的尝试和测试
您已经可以免费购买或购买许多第三方控件
在撰写本文时,Visual Studio中的设计器对于WinForms而言仍然比对WPF更好,在WPF中,您必须自己使用WPF进行更多工作

深入浅出wpf(wpf入门教程)

《计算机图形学原理及实践》一书选择WPF平台进行教学,书中给出了选择WPF平台的原因:

我们之所以选择WPF,是因为它是可以同时支持2D和3D应用的少数现代图形平台之一。它提供了与程序员编程相一致的用户界面和绘图功能。此外,它还是一个极好的可对2D和3D图形学原理进行实验的快速验证平台。其可扩展的应用标记语言(XAML)是一种采用简介方式构建场景的描述性语言(按HTML风格)。由于XAML解释器可以支持虚拟的即时实验和调试,这使得我们能够快速地引入大量的2D和3D图形基本概念,读者无需经历耗时的学习过程就能立即进行试验。

WPF开发环境准备

开发WPF应用,首先到微软官网下载 Visual Studio Tools – 免费安装 Windows、Mac、Linux,安装过程中只需要勾选.NET桌面开发进行安装即可。

深入浅出wpf(wpf入门教程)

新建项目选择WPF application类型

深入浅出wpf(wpf入门教程)

场景变换

练习目标:将整个canvas场景进行缩放,绘制在canvas上的图形都会全部进行缩放

缩放变换前代码及绘制图形:

<Window x:Class="_2d_demo.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:_2d_demo"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Canvas>
          	<!--在以canvas坐标原点为中心绘制一个圆-->
            <Ellipse Width="200" Height="200" Canvas.Left="-100" Canvas.Top="-100" 
                     Fill="Red"></Ellipse>
						<!--在以canvas坐标(150,50)为矩形左上角绘制一个矩形-->
            <Rectangle Width="200" Height="100" Canvas.Left="150" Canvas.Top="50"
                       Fill="Blue"></Rectangle>
        </Canvas>
    </Grid>
</Window>
深入浅出wpf(wpf入门教程)

缩放变换后的代码及绘制图形:

<!-- 仅展示canvas之间的代码 -->
<Canvas>
  <!--在以canvas坐标原点为中心绘制一个圆-->
	<Ellipse Width="200" Height="200" Canvas.Left="-100" Canvas.Top="-100" 
			 Fill="Red"></Ellipse>
	<!--在以canvas坐标(150,50)为矩形左上角绘制一个矩形-->
	<Rectangle Width="200" Height="100" Canvas.Left="150" Canvas.Top="50"
			   Fill="Blue"></Rectangle>
	
	<!-- 整个canvas场景x、y轴方向放大两倍,CenterX、CenterY为放大的中心点,
	也就是canvas坐标的原点 -->
	<Canvas.RenderTransform>
		<TransformGroup>
			<ScaleTransform ScaleX="2" ScaleY="2" CenterX="0" CenterY="0">
			</ScaleTransform>
		</TransformGroup>
	</Canvas.RenderTransform>
</Canvas>
深入浅出wpf(wpf入门教程)

绿色边框的矩形为放大后的尺寸

图元变换

练习目标:将绘制的图元以图元中心为原点放大

<Canvas>
	<!-- 以圆心为中心点进行放大 -->
	<Ellipse Width="100" Height="100" Canvas.Left="-50" Canvas.Top="-50" 
			 Fill="LightPink">
		<Ellipse.RenderTransform>
			<ScaleTransform ScaleX="2" ScaleY="2" CenterX="50" CenterY="50"></ScaleTransform>
		</Ellipse.RenderTransform>
	</Ellipse>
	<!-- 以矩形为中心点进行放大 -->
	<Rectangle Width="100" Height="50" Canvas.Left="150" Canvas.Top="50"
			   Fill="LightBlue">
		<Rectangle.RenderTransform>
			<ScaleTransform ScaleX="2" ScaleY="2" CenterX="50" CenterY="25"></ScaleTransform>
		</Rectangle.RenderTransform>
	</Rectangle>
	
	<!-- 绘制未放大前的图元,用于比对效果 -->
	<!-- 原始图元:圆 -->
	<Ellipse Width="100" Height="100" Canvas.Left="-50" Canvas.Top="-50" 
			 Fill="Red"></Ellipse>
	<!-- 原始图元:矩形 -->
	<Rectangle Width="100" Height="50" Canvas.Left="150" Canvas.Top="50"
			   Fill="Blue"></Rectangle>
</Canvas>

运行效果如下,其中深色的图形展示的是放大前的图元,浅色的图形是沿各自图元的中心进行放大2倍的效果。

这里需要特别说明ScaleTransform变换中心点CenterX和CenterY的坐标值怎么来的,每个图元都有各自独立的坐标系,图元的左上角就是图元坐标系的原点,而变换的中心点也就是图元图形的中心点是以图元的坐标系为参照的,如下图中的圆的圆心坐标是(50,50)。

深入浅出wpf(wpf入门教程)

简单动画

不用写一行C#代码就可以使用XAML标记语言实现简单的动画,下面的XAML代码是《计算机图形学原理及实践》书中的第二章的练习,实现了一个简单的时钟,其中时针、分针、秒针按照真实的速度运行,不过如果要实现时钟运行和真实的时间同步运行,就得借助C#代码来实现了。

深入浅出wpf(wpf入门教程)

<Canvas
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Name="ClockCanvas">

	<!-- 定义控制模板,可以方便重用 -->
	<Canvas.Resources>
		<!-- 多边形,用于时针、分针 -->
		<ControlTemplate x:Key="ClockHandTemplate">
			<Canvas>
				<Polygon
				   Points="-0.3,-1   -0.2,8   0,9   0.2,8     0.3,-1"
				   Fill="Navy"/>
			</Canvas>
		</ControlTemplate>
		<!-- 多边形,用于秒针 -->
		<ControlTemplate x:Key="SecondHandTemplate">
			<Polygon
				Points="-0.05,-1 -0.05,8 0,9 0.05,8 0.05,-1"
				Fill="red"/>
		</ControlTemplate>
	</Canvas.Resources>
	
	<!-- 圆形表盘 -->
	<Ellipse
	   Width="20.0" Height="20.0"
	   Canvas.Left="-10.0" Canvas.Top="-10.0"
	   Fill="lightgray"/>
	
	<!-- 使用定义的模板绘制秒针 -->
	<Control Name="SecondHand" Template="{StaticResource SecondHandTemplate}">
		<Control.RenderTransform>
			<TransformGroup>
				<RotateTransform Angle="180" CenterX="0" CenterY="0"></RotateTransform>
				<RotateTransform x:Name="ActualTimeSecond" Angle="0"></RotateTransform>
			</TransformGroup>
		</Control.RenderTransform>
	</Control>
	<!-- 使用定义的模板绘制分针 -->
	<Control Name="MinuteHand" Template="{StaticResource ClockHandTemplate}">
		<Control.RenderTransform>
			<TransformGroup>
				<RotateTransform Angle="180" CenterX="0" CenterY="0"></RotateTransform>
				<RotateTransform x:Name="ActualTimeMinute" Angle="0"></RotateTransform>
			</TransformGroup>
		</Control.RenderTransform>
	</Control>
	<!-- 使用定义的模板绘制时针 -->
	<Control Name="HourHand" Template="{StaticResource ClockHandTemplate}">
		<Control.RenderTransform>
			<TransformGroup>
				<ScaleTransform ScaleX="1.7" ScaleY="0.7" CenterX="0" CenterY="0"></ScaleTransform>
				<RotateTransform Angle="180" CenterX="0" CenterY="0"></RotateTransform>
				<RotateTransform x:Name="ActualTimeHour" Angle="0"></RotateTransform>
			</TransformGroup>
		</Control.RenderTransform>
	</Control>
	
	<!-- 场景变换 -->
	<Canvas.RenderTransform>
		<TransformGroup>                
			<ScaleTransform ScaleX="4.8" ScaleY="4.8" CenterX="0" CenterY="0"/>
			<TranslateTransform X="60" Y="60"></TranslateTransform>
		</TransformGroup>            
	</Canvas.RenderTransform>
	<!-- 动画效果 -->
	<Canvas.Triggers>
		<!-- 配置触发条件:窗口加载完毕即触发动画 -->
		<EventTrigger RoutedEvent="FrameworkElement.Loaded">
			<BeginStoryboard>
				<Storyboard>
					<!-- 时针动画,Duration表示12小时转动一周 -->
					<DoubleAnimation Storyboard.TargetName="ActualTimeHour"
									 Storyboard.TargetProperty="Angle"
									 From="0" To="360"
									 Duration="12:00:00.00" RepeatBehavior="Forever">
						
					</DoubleAnimation>
					<!-- 分针动画,Duration表示1小时转动一周 -->
					<DoubleAnimation Storyboard.TargetName="ActualTimeMinute"
									 Storyboard.TargetProperty="Angle"
									 From="0" To="360"
									 Duration="01:00:00.00" RepeatBehavior="Forever">
					</DoubleAnimation>
					<!-- 秒针动画,Duration表示1分钟转动一周 -->
					<DoubleAnimation Storyboard.TargetName="ActualTimeSecond"
									 Storyboard.TargetProperty="Angle"
									 From="0" To="360"
									 Duration="00:01:00.00" RepeatBehavior="Forever">
					</DoubleAnimation>
				</Storyboard>
			</BeginStoryboard>
		</EventTrigger>
	</Canvas.Triggers>
</Canvas>

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注