0%

UWP简单教程

本文以 WxaCode为例,讲一下 UWP 初学者用到的一些东西。

UWP 的坐标系

以 16:9 的屏幕为例,左上角是 (0,0) ,右下角是 (1920,1080),我使用的屏幕是 4K 屏,应用读出来的也还是 1920x1080 。这也省事一些,不用过度关心不同分辨率的情况。

动态响应布局

简单来说,动态响应布局可以根据当前屏幕的宽度(或高度),来选择不同的布局,对于PC来说,由于纵向可以用滚轮滚动,所以多数情况下,我们还是以宽度作为布局变化的点。

微信小程序码这个应用中,除去 UWP 自带的控件 NavigationView 之外(NavigationView 控件自带动态响应效果),手动实现了动态响应布局的是 HomePage ,下面是 HomePage 的 xml 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<Page
x:Class="WxaCode.HomePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WxaCode"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<Grid>

<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="WideLayout" >
<VisualState.StateTriggers>
<!--VisualState to be triggered when window width is >=720 effective pixels.-->
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="wxacodeimage.(Grid.Row)" Value="0" />
<Setter Target="wxacodeimage.(Grid.Column)" Value="1" />
<Setter Target="wxacodeimage.MaxHeight" Value="900" />
<Setter Target="wxacodeimage.MaxWidth" Value="900" />
<Setter Target="Container.HorizontalAlignment" Value="Stretch"/>
<Setter Target="Container.MinHeight" Value="600"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid x:Name="Container" MinHeight="1000" HorizontalAlignment="Center" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>

<Grid x:Name="WxaCodeParamsGrid" Width="350" Margin="10,10,10,10" Grid.Row="0" Grid.Column="0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<TextBox x:Name="AppidTextBlock" Header="Appid" PlaceholderText="必须填写,应用不缓存" Margin="5,5,5,5" VerticalAlignment="Center" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4" TextChanged="AppidTextBlock_TextChanged" />
<TextBox x:Name="AppsecretTextBlock" Header="Appsecret" PlaceholderText="必须填写,应用不缓存" Margin="5,5,5,5" VerticalAlignment="Center" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4" TextChanged="AppsecretTextBlock_TextChanged" />
<TextBox x:Name="WxacodeSceneTextBlock" Header="请输入 scene:" PlaceholderText="不超过32个字符(必须填写)" Margin="5,5,5,5" VerticalAlignment="Center" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4" TextChanged="WxacodeSceneTextBlock_TextChanged"/>
<TextBox x:Name="WxacodePageTextBlock" Header="请输入 page:" PlaceholderText="已发布的page(可不填,默认为 pages/index/index )" VerticalAlignment="Center" Margin="5,5,5,5" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="4" TextChanged="WxacodePageTextBlock_TextChanged"/>

<Slider x:Name="WxacodeWidthSlider" Margin="5,5,0,5" VerticalAlignment="Center" AutomationProperties.Name="simple slider" Minimum="280" Maximum="1280" StepFrequency="1" SmallChange="1" LargeChange="100" Value="430" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" ValueChanged="WxacodeWidthSlider_ValueChanged"/>
<TextBlock Text="小程序码宽度" Margin="5,5,0,5" VerticalAlignment="Center" Grid.Row="4" Grid.Column="2"/>
<TextBlock Text="{x:Bind WxacodeWidthSlider.Value.ToString(), Mode=OneWay}" Margin="5,5,5,5" VerticalAlignment="Center" HorizontalAlignment="Right" Grid.Row="4" Grid.Column="3" />

<ToggleSwitch x:Name="WxacodeAutoColorSwitch" Header="自动配置线条颜色" AutomationProperties.Name="simple ToggleSwitch" Toggled="ToggleAutoColorSwitch" VerticalAlignment="Center" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" />
<ToggleSwitch x:Name="WxacodeIsHyalineSwitch" Header="透明底色" AutomationProperties.Name="simple ToggleSwitch" Margin="5,0,0,0" VerticalAlignment="Center" Grid.Row="5" Grid.Column="2" Grid.ColumnSpan="2" Toggled="WxacodeIsHyalineSwitch_Toggled" />

<Slider IsEnabled="{x:Bind WxacodeAutoColorSwitch.IsOn.Equals(x:False), Mode=OneWay}" x:Name="WxacodeLineColorRedSlider" Foreground="Red" Margin="5,5,0,5" VerticalAlignment="Center" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" Minimum="0" Maximum="255" StepFrequency="1" SmallChange="1" LargeChange="10" Value="0" ValueChanged="WxacodeLineColorRedSlider_ValueChanged" />
<TextBlock Text="线条颜色(red)" Margin="5,5,0,5" VerticalAlignment="Center" Grid.Row="6" Grid.Column="2" />
<Rectangle x:Name="ColorPreview" Stroke="Black" Fill="White" Grid.Row="6" Grid.Column="3" Grid.RowSpan="3" VerticalAlignment="Stretch" />

<Slider IsEnabled="{x:Bind WxacodeAutoColorSwitch.IsOn.Equals(x:False), Mode=OneWay}" x:Name="WxacodeLineColorGreenSlier" Foreground="Green" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Center" Margin="5,5,0,5" Minimum="0" Maximum="255" StepFrequency="1" SmallChange="1" LargeChange="10" Value="0" ValueChanged="WxacodeLineColorGreenSlier_ValueChanged" />
<TextBlock Text="线条颜色(green)" Grid.Row="7" Grid.Column="2" VerticalAlignment="Center" Margin="5,5,0,5" />

<Slider IsEnabled="{x:Bind WxacodeAutoColorSwitch.IsOn.Equals(x:False), Mode=OneWay}" x:Name="WxacodeLineColorBlueSlider" Foreground="Blue" Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Center" Margin="5,5,0,5" Minimum="0" Maximum="255" StepFrequency="1" SmallChange="1" LargeChange="10" Value="0" ValueChanged="WxacodeLineColorBlueSlider_ValueChanged" />
<TextBlock Text="线条颜色(blue)" Grid.Row="8" Grid.Column="2" VerticalAlignment="Center" Margin="5,5,0,5" />

<Button Style="{StaticResource AccentButtonStyle}" Content="获取小程序码" x:Name="GetWxaCodeButton" Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Center" Click="GetWxaCodeUnlimited" />
<Button Style="{StaticResource AccentButtonStyle}" Content="保存小程序码" x:Name="SaveWxaCodeButton" Grid.Row="9" Grid.Column="2" Grid.ColumnSpan="2" VerticalAlignment="Center" Click="SaveWxacodeImage"/>

</Grid>
<Image x:Name="wxacodeimage" Source="/Assets/wxacodesample.jpg" MaxWidth="350" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" />

</Grid>
</Grid>
</Page>

对比一下UWP新建时,空白页的 xml 内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Page
x:Class="NavigationViewTest.BlankPage1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:NavigationViewTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<Grid>

</Grid>
</Page>

动态响应布局的关键内容 VisualStateManager 即放在 Grid 下。然后我们再在 Grid 新建一个 Grid 布局,用这个子布局来承载页面的内容。相当于这个 Page 下有一个根 Grid ,将这个根 Grid 的 (0,0) 单元格,作为一个新的子 Grid 的内容,这里面再添加这个页面的承载元素,这样 VisualStateManager 就能生效了。

VisualStateManager 的语法意思是,在页面大于或小于某个值(坐标系 1920x1080)时,改变控件的一些属性,本例中,改变了承载小程序码的 Image 控件所在的行和列,并且设置相应的宽度实现。下面两图是实现的动态响应布局的效果。

导航栏

导航栏是 UWP 自带的一个控件,Windows 10 的设置下的每一个子页面,都是导航栏的例子,这个控件是 NavigationView ,使用时在 Nuget 包管理器中,为当前解决方案添加 Microsoft.UI.Xaml。在页面的 Page 节点的配置中,添加xmlns:muxc="using:Microsoft.UI.Xaml.Controls"字段,然后在这个页面下的 Grid 内添加一个 NavigationView 控件,以微信小程序码为例,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<Page
x:Class="WxaCode.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WxaCode"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<Grid>
<muxc:NavigationView x:Name="NaviView" ItemInvoked="NaviView_ItemInvoked" IsBackButtonVisible="Collapsed" >
<muxc:NavigationView.MenuItems>
<muxc:NavigationViewItem Icon="Home" Content="操作" Tag="HomePage" x:Name="homepage" />
<muxc:NavigationViewItem Icon="Help" Content="关于" Tag="AboutPage" />
</muxc:NavigationView.MenuItems>
<ScrollViewer>
<Frame x:Name="ContentFrame" Padding="12,50,12,24" IsTabStop="True" />
</ScrollViewer>
</muxc:NavigationView>
</Grid>
</Page>

注意其中的 ScrollViewer 和其内的 Frame ,这里将会承载每一个页面不同页面的具体内容,本例中,实际实现是把 homepage 和 aboutpage 作为一个子布局,放到 Frame 上。这样导航栏可以一直存在,通过点击导航栏,替换里面的 Frame 对应的页面,即可达到通过导航栏来切换页面的效果,具体的代码实现可以查看应用的源码。

本地设置

本地设置通常是存放一些设置项,本例中通过封装UWP自带的 Windows.Storage.ApplicationData.Current.LocalSettings 来实现,比如是否保存 appid 和 appsecret ,如下图所示

在应用退出时保存数据

可能百度大学会告诉你,UWP在应用关闭时,需要申请一个受限权限”confirmAppClose”,但是由于是一个受限制的权限,有可能会因为这个权限,应用商店不允许发布(我没有尝试,我换了另外一种做法),如果你有安卓应用开发的经验,就会知道,安卓应用关闭时,Activity会收到 onPause,onStop,onDestroy三个事件通知,这三个地方足够你做很多事情,UWP有类似的地方,每一个UWP应用都有一个 App.xaml.cs,其内的

1
2
3
4
5
6
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: 保存应用程序状态并停止任何后台活动
deferral.Complete();
}

已经提示了开发者,UWP应用暂停或者关闭时,都会收到这个 OnSuspending 事件,虽然时间很短,但是足够保存一些设置项了。如果你的应用有大量数据要保存,建议应用运行时就定时保存,或者申请受限权限。