在 Windows 8 Store Apps 中,「契約」是應用程式之間以及應用程式與系統互動的核心機制。設定合約讓使用者能從右側邊欄的 Charm 列直接存取應用程式的設定選項,提供一致且直觀的使用體驗。本文將詳細解析 Settings Contract 的實作方式,並透過完整的程式碼範例示範如何註冊設定命令、開啟設定面板以及建立自訂的詳細設定頁面。
GetForCurrentView() 取得目前視圖的實例,並使用 Show() 方法開啟。ApplicationCommands 集合。Id(識別碼)、Label(顯示名稱)與 Invoked(點擊後的回呼處理函式)。IUICommand 介面的物件。在頁面載入時註冊事件,離開頁面時移除註冊,避免記憶體洩漏。
<Page x:Class="XamlDemo.Contracts.SettingsContract.Demo" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0"> <Button Name="btnAddSettings" Content="註冊 SettingsPane 的 CommandsRequested 事件" Click="btnAddSettings_Click_1" /> <Button Name="btnShowSettings" Content="開啟 SettingsPane" Click="btnShowSettings_Click_1" Margin="0 10 0 0" /> <TextBlock Name="lblMsg" FontSize="14.667" TextWrapping="Wrap" Margin="0 10 0 0" /> </StackPanel> </Grid> </Page>
using System;
using Windows.UI.ApplicationSettings;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation;
namespace XamlDemo.Contracts.SettingsContract
{
public sealed partial class Demo : Page
{
private Popup _settingsPopup = new Popup();
private bool _commandsRequestedRegistered = false;
public Demo()
{
this.InitializeComponent();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
if (this._commandsRequestedRegistered)
{
SettingsPane.GetForCurrentView().CommandsRequested -= onCommandsRequested;
_commandsRequestedRegistered = false;
}
}
private void btnAddSettings_Click_1(object sender, RoutedEventArgs e)
{
if (!_commandsRequestedRegistered)
{
SettingsPane.GetForCurrentView().CommandsRequested += onCommandsRequested;
_commandsRequestedRegistered = true;
}
else
{
lblMsg.Text += "已經為 SettingsPane 註冊了 CommandsRequested 事件";
lblMsg.Text += Environment.NewLine;
}
}
private void btnShowSettings_Click_1(object sender, RoutedEventArgs e)
{
SettingsPane.Show();
}
void onCommandsRequested(SettingsPane settingsPane, SettingsPaneCommandsRequestedEventArgs eventArgs)
{
UICommandInvokedHandler handler = new UICommandInvokedHandler(onSettingsCommand);
SettingsCommand aboutCommand = new SettingsCommand("about", "關於", handler);
eventArgs.Request.ApplicationCommands.Add(aboutCommand);
SettingsCommand contactCommand = new SettingsCommand("contactUs", "聯繫我們", handler);
eventArgs.Request.ApplicationCommands.Add(contactCommand);
SettingsCommand flyoutCommand = new SettingsCommand("flyout", "彈出一個類「設定」風格的詳細設定頁", handler);
eventArgs.Request.ApplicationCommands.Add(flyoutCommand);
}
void onSettingsCommand(IUICommand command)
{
SettingsCommand settingsCommand = (SettingsCommand)command;
lblMsg.Text += string.Format("commandId:{0} - label:{1}", settingsCommand.Id.ToString(), settingsCommand.Label);
lblMsg.Text += Environment.NewLine;
if (settingsCommand.Id.ToString() == "flyout")
{
double settingsPageWidth = 600;
_settingsPopup.IsLightDismissEnabled = true;
_settingsPopup.Width = settingsPageWidth;
_settingsPopup.Height = Window.Current.Bounds.Height;
_settingsPopup.ChildTransitions = new TransitionCollection();
_settingsPopup.ChildTransitions.Add(new PaneThemeTransition() { Edge = EdgeTransitionLocation.Right });
CustomSettingsPage mySettingsPage = new CustomSettingsPage();
mySettingsPage.Width = settingsPageWidth;
mySettingsPage.Height = Window.Current.Bounds.Height;
_settingsPopup.Child = mySettingsPage;
_settingsPopup.HorizontalOffset = Window.Current.Bounds.Width - settingsPageWidth;
_settingsPopup.VerticalOffset = 0;
_settingsPopup.IsOpen = true;
}
}
}
}
建立一個獨立的 Page 作為詳細設定頁,包含返回按鈕、標題區塊與捲動內容區域,並套用 PaneThemeTransition 動畫以符合系統視覺風格。
<Page x:Class="XamlDemo.Contracts.SettingsContract.CustomSettingsPage" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Contracts.SettingsContract" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
<Page.Resources>
<Style x:Key="SettingsBackButtonStyle" TargetType="Button">
<Setter Property="MinWidth" Value="0"/>
<Setter Property="FontFamily" Value="Segoe UI Symbol"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="FontSize" Value="26.66667"/>
<Setter Property="AutomationProperties.AutomationId" Value="BackButton"/>
<Setter Property="AutomationProperties.Name" Value="Back"/>
<Setter Property="AutomationProperties.ItemType" Value="Navigation Button"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid x:Name="RootGrid" Width="30" Height="30">
<Grid Margin="-6,-6,0,0">
<TextBlock x:Name="BackgroundGlyph" Text="" Foreground="Transparent"/>
<TextBlock x:Name="NormalGlyph" Text="{StaticResource BackButtonSnappedGlyph}" Foreground="White"/>
<TextBlock x:Name="ArrowGlyph" Text="" Foreground="#00b2f0" Opacity="0"/>
</Grid>
<Rectangle x:Name="FocusVisualWhite" IsHitTestVisible="False" Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}" StrokeEndLineCap="Square" StrokeDashArray="1,1" Opacity="0" StrokeDashOffset="1.5" />
<Rectangle x:Name="FocusVisualBlack" IsHitTestVisible="False" Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush" StrokeEndLineCap="Square" StrokeDashArray="1,1" Opacity="0" StrokeDashOffset="0.5" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource BackButtonPointerOverBackgroundThemeBrush}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="White"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="ArrowGlyph" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
<DoubleAnimation Storyboard.TargetName="NormalGlyph" Storyboard.TargetProperty="Opacity" To="0" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="FocusVisualWhite" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
<DoubleAnimation Storyboard.TargetName="FocusVisualBlack" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused" />
<VisualState x:Name="PointerFocused" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid Background="Transparent" Name="root">
<Border BorderBrush="Red" BorderThickness="1,0,0,0">
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="80"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Background="#00b2f0" Grid.Row="0">
<StackPanel Orientation="Horizontal" Margin="40, 32, 17, 13">
<Button Name="btnBack" Click="btnBack_Click_1" Margin="0,3,0,0" Style="{StaticResource SettingsBackButtonStyle}"/>
<TextBlock Margin="10,0,0,0" FontFamily="Segoe UI" FontWeight="SemiLight" FontSize="24.6667" Text="Defaults" Foreground="White"/>
</StackPanel>
</Grid>
<ScrollViewer Grid.Row="1">
<StackPanel>
<StackPanel.ChildrenTransitions>
<TransitionCollection>
<PaneThemeTransition Edge="Right" />
</TransitionCollection>
</StackPanel.ChildrenTransitions>
<TextBlock FontSize="56" Foreground="Black" LineHeight="100">
<Run>設定1</Run> <LineBreak />
<Run>設定2</Run> <LineBreak />
<Run>設定3</Run> <LineBreak />
<Run>設定4</Run> <LineBreak />
<Run>設定5</Run> <LineBreak />
<Run>設定6</Run> <LineBreak />
<Run>設定7</Run> <LineBreak />
<Run>設定8</Run> <LineBreak />
<Run>設定9</Run>
</TextBlock>
</StackPanel>
</ScrollViewer>
</Grid>
</Border>
</Grid>
</Page>
using Windows.UI.ApplicationSettings;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation;
namespace XamlDemo.Contracts.SettingsContract
{
public sealed partial class CustomSettingsPage : Page
{
public CustomSettingsPage()
{
this.InitializeComponent();
}
private void btnBack_Click_1(object sender, RoutedEventArgs e)
{
Popup parent = this.Parent as Popup;
if (parent != null)
{
parent.IsOpen = false;
}
if (Windows.UI.ViewManagement.ApplicationView.Value != Windows.UI.ViewManagement.ApplicationViewState.Snapped)
{
SettingsPane.Show();
}
}
}
}
CommandsRequested 事件動態建立設定命令,支援簡單選項與複雜的自訂頁面。Popup 搭配 PaneThemeTransition 實現從右側滑入的效果。