本文章是由機器翻譯。

UI 前沿技術

Silverlight、Windows Phone 7 和多點觸控 Thumb

Charles Petzold

下載代碼示例

對於大多數 Silverlight 程式設計人員來說,有關 Windows Phone 7 的最激動人心的消息就是:它支援 Silverlight 作為它的兩個程式設計介面之一 (另一個是 XNA)。Silverlight 程式設計人員不但可以利用他們在為手機編寫新應用程式方面的現有知識和技能,還應該能為共用代碼的 Web 和手機構建 Silverlight 程式。

當然,共用代碼,特別是 UI 代碼,並不像表面看上去那麼簡單。 手機中所使用的 Silverlight 版本稱為 Silverlight for Windows Phone,其通常是 Silverlight 3 的精簡實現。 在構思共用代碼應用程式時,您需要仔細閱讀以下文檔:對於每個 Silverlight 類,連線文檔將指示支援該類的環境。 在每個類中,屬性、方法和事件的清單都使用圖示來指示 Windows Phone 7 支援。

適用于 Web 的 Silverlight 應用程式通過鍵盤、滑鼠和多點觸控來獲取使用者輸入。 在 Windows Phone 7 程式中,多點觸控是主要輸入方式。 由於沒有滑鼠,因此即使手機上可能有硬體鍵盤,Silverlight 程式也只能依賴于虛擬鍵盤(即軟體輸入面板,簡稱 SIP),且只能通過 TextBox 控制項執行。

如果現有的 Silverlight 程式從不直接獲取鍵盤或滑鼠輸入,並且完全依賴于控制項,則不必為轉換為多點觸控而費心。 並且,如果您的套裝程式含自己的滑鼠邏輯,那麼實際上在將該程式遷移到手機上時可以保留該邏輯。

在手機上,主要觸控事件會被轉換為滑鼠事件,以便現有的滑鼠邏輯能正常運轉。 (主要觸控事件是指在沒有其他手指觸控式螢幕幕時第一個觸控式螢幕幕的手指的整個活動。)

從滑鼠轉換到多點觸控需要注意一些問題:Silverlight for the Web 和 Silverlight for Windows Phone 都支援靜態 Touch.FrameReported 事件,但此事件是多點觸控的相當低級別的介面。 我在 2010 年 3 月發表的“手指之舞:探討 Silverlight 中的多點觸控支援”(msdn.microsoft.com/magazine/ee336026) 一文中著重討論過此事件。

Silverlight for Windows Phone 支援源自 Surface SDK 的操作事件的子集,並且此後已成為 Windows Presentation Foundation (WPF) 的一部分。 這是多點觸控如何逐步成為主流的示例。 手機僅支援轉換和縮放功能,並不支援旋轉,且未實現延時,儘管您自己擁有可實現延時的足夠多的資訊。 Web 版本的 Silverlight 中尚不支援這些操作事件。

總之,如果要想在 Silverlight for the Web 和 Silverlight for Windows Phone 之間共用代碼,則您將總需使用滑鼠事件或 Touch.FrameReported。

考慮使用 Thumb

然而,這裡還有另一個選擇:如果您只需要操作事件的轉換支援,並且您不想為判斷輸入是來自滑鼠還是觸控而費心,則可以使用一種能以非常純粹的形式提供這種支援的控制項。 這個控制項就是 Thumb。

您可能從未實際接觸過 Thumb。 Thumb 控制項在 System.Windows.Controls.Primitives 命名空間中是隱藏的,主要用於 ScrollBar 和 Slider 範本。 但您還可將其用於其他工作,最近我在考慮將 Thumb 用作操作事件的轉換功能的高級實現。

現在,Thumb 不再是真正的“多點”觸控控制項,其一次僅支援一個觸控。 但是,通過深入研究 Thumb,您將有機會嘗試支援觸控計算以及在 Silverlight 應用程式和 Windows Phone 7 應用程式之間共用代碼。

Thumb 可定義三個事件:

  • DragStarted 在使用者使用手指或滑鼠首次觸摸控制項時觸發。
  • DragDelta 指示滑鼠或手指相對於螢幕的移動。
  • DragCompleted 指示滑鼠或手指已抬起。

DragDelta 事件附帶了具有屬性 HorizontalChange 和 VerticalChange 的事件參數,自最後一次事件起,這兩個屬性開始指示滑鼠或手指的移動。 通常,您會將增量變化添加到 TranslateTransform(設置為某些可拖動元素的 RenderTransform 屬性)的 X 和 Y 屬性或添加到 Canvas.Left 和 Canvas.Top 附加屬性,以便處理此事件。

預設狀態下,Thumb 非常簡單。 與其他控制項一樣,我們通常將 HorizontalAlignment 和 VerticalAlignment 屬性設置為 Stretch,以使 Thumb 在一般情況下填充所允許的區域。 否則,Silverlight Thumb 僅適用于 4 個單位面積圖元數。 在 Silverlight for Windows Phone 中,Thumb 有 48 個單位面積圖元數,但在視覺上,其只有 24 個單位面積圖元數,因為所有四個側面上都有 12 個圖元寬的透明邊框。

您最起碼在 Thumb 上要設置顯式高度和寬度。 圖 1 並列顯示了 Silverlight 和 Windows Phone 7 版本,並使用手機的預設配色方案“在深色背景中顯示淺色文本”。 對於這兩個版本,我已將高度和寬度設置為 72 並將背景設置為藍色,在 Silverlight 版本中按 Thumb 則會變成漸變色。 Thumb 不會關注 Foreground 屬性。


圖 1 Silverlight 和 Windows Phone Thumb 控制項

通常,您將不但要調整 Thumb 的大小,還要應用用於重定義控制項的視覺效果的 ControlTemplate。 此 ControlTemplate 也可以是非常簡單的。

共用控制項

假設您需要一個允許使用者在螢幕上隨處拖動點陣圖的控制項。 最簡單的方法就是將一個 Image 元素和一個 Thumb 放入一個儲存格網格,保持 Thumb 的大小與圖像的大小一致,並覆蓋該圖像。 如果 Thumb 的 ControlTemplate 只是一個透明的矩形,則 Thumb 是不可見的,但它仍可以觸發拖動事件。

讓我們嘗試創建一個既可用於常規 Silverlight 又可用於 Windows Phone 7 專案的控制項。 我將假定您已經安裝了 Windows Phone 7 DeveloperTool (create.msdn.com)。 您可以利用這些工具從 Visual Studio 創建 Windows Phone 7 專案。

首先,創建一個名為 DragImage 的常規 Silverlight 4 專案。 生成的 DragImage 解決方案包含慣用的 DragImage 專案(Silverlight 程式本身)和一個 DragImage.Web 專案(在 HTML 或 ASP.NET 頁中承載 Silverlight 程式)。

接下來,向解決方案中添加一個類型為 Windows Phone 應用程式的新專案。 將此專案命名為 DragImage.Phone。 (可能您不希望手機的程式清單或手機模擬器中顯示該名稱,因此可以在 WMAppManifest.xml 檔中的 APP 標記的 Title 特性中更改顯示名稱。)

通過按右鍵 DragImage.Web 專案或 DragImage.Phone 專案,您可獲取一個上下文功能表,可從該功能表中選擇“設為啟動專案”並運行常規 Silverlight 程式或 Windows Phone 7 程式。 利用 Visual Studio 中的工具列下拉清單,您可將手機程式部署到實際的手機設備或手機模擬器中。 (如果對 Windows Phone 7 設備設置了此下拉清單,並且未附加任何手機,則 Visual Studio 將不會構建專案。)

在 DragImage 專案(常規 Silverlight 專案)中,添加類型為 Silverlight 使用者控制項的新專案。 將其命名為 DraggableImage。 如同往常一樣,Visual Studio 將為此控制項創建 DraggableImage.xaml 和 DraggableImage.xaml.cs 檔。

圖 2以控制項的視覺化樹的形式顯示了 DraggableImage.xaml。 名為 LayoutRoot 的標準外部網格將佔用該控制項的容器的全部維度;內部網格則設置為左上角對齊,但有一個 TranslateTransform 分配給了其 RenderTransform 屬性,目的是在外部網格中移動它。 此內部網格保留了一個 Image 元素,該元素的頂部有一個 Thumb 控制項,該控制項的 Template 屬性設置為只包含一個透明矩形的視覺化樹。

圖 2 DraggableImage.xaml

<UserControl x:Class="DragImage.DraggableImage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Name="ctrl">
    
  <Grid x:Name="LayoutRoot">
    <Grid HorizontalAlignment="Left"
          VerticalAlignment="Top">
      <Image Name="image" Stretch="None"
             Source="{Binding ElementName=ctrl, Path=Source}" />
      <Thumb DragDelta="OnThumbDragDelta">
        <Thumb.Template>
          <ControlTemplate>
            <Rectangle Fill="Transparent" />
          </ControlTemplate>
        </Thumb.Template>
      </Thumb>
      <Grid.RenderTransform>
        <TranslateTransform x:Name="translate" />
      </Grid.RenderTransform>
    </Grid>
  </Grid>
</UserControl>

請注意,Image 元素的 Source 屬性綁定到了該控制項本身的 Source 屬性。 該屬性在 DraggableImage.xaml.cs 檔中進行定義,如圖 3 所示。 該檔可通過更改 TranslateTransform 的 X 和 Y 屬性從 Thumb 中處理 DragDelta 事件。

圖 3 DraggableImage.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
namespace DragImage {
  public partial class DraggableImage : UserControl {
    public static readonly DependencyProperty SourceProperty =
      DependencyProperty.Register("Source",
      typeof(ImageSource),
      typeof(DraggableImage),
      new PropertyMetadata(null));
    public DraggableImage() {
      InitializeComponent();
    }
    public ImageSource Source {
      set { SetValue(SourceProperty, value); }
      get { return (ImageSource)GetValue(SourceProperty); }
    }
    void OnThumbDragDelta(object sender, DragDeltaEventArgs args) {
      translate.X += args.HorizontalChange;
      translate.Y += args.VerticalChange;
    }
  }
}

若要與 Windows Phone 7 專案共用該控制項,請按右鍵 DragImage.Phone 專案並選擇“添加”|“現有項”,以顯示“添加現有項”對話方塊。 導航到 DragImage 專案目錄。 選擇 DraggableImage.xaml 和 DraggableImage.xaml.cs,但不按一下“添加”按鈕, 而應按一下“添加”按鈕右側的小箭頭,並選擇“添加為連結”。 DragImage.Phone 專案中顯示的檔的圖示上帶有小箭頭則表明該檔可在兩個專案之間共用。

現在您可對 DraggableImage 檔做出更改,並且兩個專案都將使用修訂後的版本。

若要對其進行測試,則您將需要一個點陣圖。 將該點陣圖存儲在每個專案內的 Image 目錄中。 (您無需備份該點陣圖的副本,可以使用一個連結將該點陣圖添加到 Image 目錄。)

此處應該有兩個 MainPage.xaml 檔。 一個來自常規 Silverlight 專案,另一個來自 Windows Phone 7 專案。 在 Silverlight 專案的 MainPage.xaml 中,添加一個(傳統上)名為“local”的 XML 命名空間綁定:

xmlns:local="clr-namespace:DragImage"

現在您可將 DraggableImage 添加到頁面:

<Grid x:Name="LayoutRoot" Background="White">
  <local:DraggableImage 
    Source="Images/BuzzAldrinOnTheMoon.png" />
</Grid>

Windows Phone 7 專案的 MainPage 類位於名為 DragImage.Phone 的命名空間中,但是共用的 DraggableImage 類則位於名為 DragImage 的命名空間中。 您需要一個用於 DragImage 命名空間的 XML 命名空間綁定,可將其命名為“shared”:

xmlns:shared="clr-namespace:DragImage"

現在您可將 DraggableImage 添加到頁面的內容區域:

<Grid x:Name="ContentPanel" 
  Grid.Row="1" Margin="12,0,12,0">
  <shared:DraggableImage 
    Source="Images/BuzzAldrinOnTheMoon.png" />
</Grid>

這可能是您在兩個 Silverlight 專案(其中一個用於 Web,另一個用於 Windows Phone 7)之間共用控制項的最簡單方法。 因為該控制項使用 Thumb,所以兩個程式都使用滑鼠或觸控。

DragImage 解決方案的可下載代碼還包括一個名為 DragImage.Wpf 的專案,該專案也是使用此控制項的 WPF 程式。 但在一般情況下,在 Silverlight 和 WPF 之間共用控制項比在 Silverlight 和 Windows Phone 7 之間共用控制項要困難。

顏色和解析度

當嘗試在 Silverlight 和 Windows Phone 7 之間共用代碼時,除了滑鼠和觸控輸入,您還需要處理另外兩個問題:顏色和視頻解析度。

在桌上型電腦上,Silverlight 將在白色的背景上顯示黑色文本。 (但 Silverlight 程式可使用 SystemColors 類來顯示使用者選擇的 Windows 顏色。)預設情況下,Windows Phone 7 將在黑色的背景上顯示白色文本,除非使用者將配色方案更改為在白色的背景上顯示黑色文本。 Windows Phone 7 可提供一些方便的預定義資源鍵(如 PhoneForegroundBrush 和 PhoneBackgroundBrush),以説明某個程式使用選定的配色方案。

在使用顯式顏色的 Silverlight 和 Windows Phone 7 之間共用的任意代碼或標記將會找到一些方法來確定其運行的平臺,以便獲取正確的顏色。

視頻解析度問題則有些棘手。 所有 Silverlight 座標都以圖元為單位,並且此規則也適用于手機。 普通桌上型電腦視頻顯示器所具有的解析度約為 100 點/英寸 (DPI)。 (例如,假如有一個 21 英寸的視頻顯示器處理 1600 × 1200 圖元(或對角方向 2000 圖元), 則其解析度為 105 DPI。)預設情況下,Windows 會假定顯示器解析度為 96 DPI,但使用者可更改此解析度,以使螢幕更易於讀取。

Windows Phone 7 設備有一個 480 × 800 圖元的螢幕,且對角線為 933 圖元。 但是,螢幕的對角線只有 3.5 英寸長,也就是說,其解析度約為 264 DPI,這是桌上型電腦顯示器解析度的 2.75 倍。

這就意味著,在桌上型電腦上視覺效果好的特定大小的共用元素若放到手機上則會過小。 然而,手機的觀看距離通常比桌上型電腦顯示器的觀看距離要短,因此,若要使元素在手機上可見,不必將其整個放大 2.75 倍。

Thumb 應該多大才便於觸摸呢? 我看到的一條標準指示觸摸目標的寬度和高度應該為 9 毫米(0.25 英寸)。 在桌上型電腦顯示器上的解析度為 96 圖元/英寸(即 34 圖元),但是在手機上的解析度為 93 圖元。

另一方面,Windows Phone 7 設備的標準按鈕的高度僅為 72 圖元,這看來已經足夠了。 或許最好的方法就是不停嘗試直至找到易於使用的解析度,但這種方法太笨了。

進行調整

傳統上,程式通過使用條件編譯的前置處理器指令針對各種平臺對自身進行調整。 Silverlight 程式定義了條件編譯符號 ILVERLIGHT,Windows Phone 7 程式則定義了條件編譯符號 SILVERLIGHT 和 PHONE。 (您可通過選擇“專案屬性”頁上的“生成”選項卡查看這些。)這就意味著您可以具有以下形式的代碼:

#if PHONE
  // Code for Windows Phone 7
#else
  // Code for regular Silverlight
#endif

或者,您可通過訪問 Environment.OSVersion 物件在運行時進行區分。 如果 Platform 屬性為 PlatformID.WinCE,並且 Version.Major 屬性為 7 或更高,則表示您的代碼正在 Windows Phone 7 設備(也有可能是 Windows Phone 8 或 9)上運行。

理論上,通過使用在標記相容性 (mc) 命名空間中定義的 AlternateContent 和 Choice 標記,就可能定義 XAML 檔的條件部分,但是,Silverlight 中卻並不支援這些標記。

但是 XAML 可包含資料綁定,且這些綁定能夠根據平臺引用不同的物件。 XAML 也可具有針對不同平臺檢索不同物件的 StaticResource 引用。 我在 TextTransform 程式中使用的就是這種方法。

我按照創建 DragImage 解決方案的相同方法創建了 TextTransform 解決方案。 該解決方案包含三個專案:TextTransform(Silverlight 程式)、TextTransform.Web(承載 Silverlight 程式的 Web 專案)和 TextTransform.Phone (Windows Phone 7)。

然後,我在 Silverlight 專案中創建了一個派生自 UserControl 的 TextTransformer 控制項。 此控制項已在 Silverlight 專案和 Windows Phone 7 專案中共用。 TextTransformer 控制項包含一個硬編碼文本字串(單詞“TEXT”),字串用邊框包圍起來,邊框的四個角各有一個 Thumb 控制項。 移動 Thumb 將導致邊框和文字區塊發生非仿射轉換。 (僅當由邊框構成的四邊形不具有凹角時,系統才會正常運行。)

TextTransformer.xaml 檔未為 Thumb 創建新的範本,但定義了一個樣式,如圖 4 所示。

圖 4 TextTransformer.xaml 中的 Thumb 樣式

<Style x:Key="thumbStyle" TargetType="Thumb">
  <Setter Property="HorizontalAlignment" 
          Value="Left" />
  <Setter Property="VerticalAlignment" 
          Value="Top" />
  <Setter Property="Width" 
          Value="{StaticResource ThumbSize}" />
  <Setter Property="Height" 
          Value="{StaticResource ThumbSize}" />
  <Setter Property="RenderTransform">
    <Setter.Value>
      <TranslateTransform 
        X="{StaticResource HalfThumbOffset}"
        Y="{StaticResource HalfThumbOffset}" />
    </Setter.Value>
  </Setter>
</Style>

請注意對 ThumbSize 和 HalfThumbOffset 的引用。 儘管用於顯示文本的文字區塊通過屬性繼承獲取了正確的 Foreground 屬性,但是,邊框必須使用相同的前景色進行顯式著色:

<Border Name="border"
        BorderBrush="{StaticResource ForegroundBrush}"
        BorderThickness="1">

在何處定義這些資源? 在 App.xaml 中定義它們。 常規 Silverlight 專案在其包含以下內容的 App.xaml 檔中包含了一個 Resources 集合:

<Application.Resources>
  <SolidColorBrush x:Key="BackgroundBrush" Color="White" />
  <SolidColorBrush x:Key="ForegroundBrush" Color="Black" />
  <system:Double x:Key="ThumbSize">36</system:Double>
  <system:Double x:Key="HalfThumbOffset">-18</system:Double>
</Application.Resources>

Windows Phone 7 程式的 App.xaml 檔引用了兩個畫筆的預定義資源,並定義了更大的 ThumbSize 和 HalfThumbOffset 值:

<Application.Resources>
  <SolidColorBrush x:Key="BackgroundBrush"
     Color="{StaticResource PhoneBackgroundColor}" />
  <SolidColorBrush x:Key="ForegroundBrush"
     Color="{StaticResource PhoneForegroundColor}" />
  <system:Double x:Key="ThumbSize">96</system:Double>
  <system:Double x:Key="HalfThumbOffset">-48</system:Double>
</Application.Resources>

圖 5 演示在流覽器中運行的程式,圖 6 演示在 Windows Phone 7 模擬器上運行的程式。模擬器按照完整尺寸的百分之五十顯示,以補償手機上更高的圖元密度。

圖 5 流覽器中的 TextTransform 程式

圖 6 手機模擬器上的 TextTransform 程式

這些技術表明,在桌上型電腦和手機之間共用代碼已成為現實。如果您想要更加深入地探討本主題,則可以看看 Surface Toolkit for Windows Touch,它包括針對 WPF 開發人員的 SurfaceThumb 控制項。這就類似于 Thumb 控制項,但它會為真正的多點觸控添加支援並在縮略圖轉動時添加事件。有關詳細資訊,請參閱 Surface Toolkit for Windows Touch beta 頁,網址為 msdn.microsoft.com/library/ee957351

Charles Petzold* 是*《MSDN 雜誌》*的長期特約編輯。*他的新書《Programming Windows Phone 7》可從 bit.ly/cpebookpdf 免費下載獲得。

衷心感謝以下技術專家對本文的審閱:Doug Kramer 和 Robert Levy