用 Silverlight 饼图样式

摘要

在本篇文章中,Pete Brown 介紹了多種方法,教您如何改變 Silverlight 工具組隨附的圖表控制項的外觀與風格。雖然主要著重在圓形圖,不過您也可以將學到的技巧運用在工具組中其他類型的圖表上。這篇文章適用於喜歡使用 Xaml 的設計人員或是喜歡使用 Expression Blend 的開發人員。

簡介

Silverlight 工具組是一組 Microsoft 控制項和樣式,是在一般的 Silverlight 開發週期之外提供的。完整原始程式碼、測試和 Xaml 可在 CodePlex 上取得。

雖然工具組中包含非常多種控制項,對於商務應用程式開發社群來說,最值得期待的是原生 Silverlight 圖表功能。像是 Silverlight 和 WPF 這樣的向量呈現技術,能夠讓他們自然輕鬆地建立圖表。不過,要建置健全的圖表控制項並不是件簡單的事。幸好,Silverlight 工具組團隊已經代勞了所有困難的部分。

[本文章是使用 12 月 9 日發佈的工具組所撰寫的。]

在圖表元件中,包含水平和垂直的長條圖、泡泡圖、圓形圖、折線圖和散佈圖。上述每一種類型的圖表都能加上豐富的樣式功能,除了所有視覺效果之外,還有元素動畫。

從簡單的圓形圖開始

來建立一個簡單的圓形圖吧。按照您平常的方式建立一個 Silverlight 專案,然後加入 Silverlight 工具組圖表 DLL 的參照以及主要控制項 DLL。這兩個項目的位置視您安裝工具組二進位檔的位置而定。

接下來,您需要將圖表控制項拖曳到標記中 (或拖曳到 Blend 設計介面),或是手動建立 xmlns。由於我對於命名空間的命名有非常明確的要求,所以我喜歡使用 Visual Studio 的 Xaml 編輯器手動輸入:

xmlns : charting ="clr-namespace:Microsoft.Windows.Controls.DataVisualization.Charting;
assembly=Microsoft.Windows.Controls.DataVisualization"

xmlns : datavis ="clr-namespace:Microsoft.Windows.Controls.DataVisualization;
assembly=Microsoft.Windows.Controls.DataVisualization"

xmlns : controls ="clr-namespace:Microsoft.Windows.Controls;
assembly=Microsoft.Windows.Controls"

xmlns : sys ="clr-namespace:System;assembly=mscorlib"

這會告訴 Silverlight 要從何處提取我們以列出的 xmln 做為前置字元的控制項。第一個要用於圖表。“datavis” 要用於樣式,“controls” 和 “sys” 則要用來直接在 Xaml 中模擬資料。在一般具有繫結資料的應用程式中,通常不需要 “controls” 和 “sys” 命名空間宣告。

趁現在還在 Xaml 編輯器中,我們來進一步了解控制項的結構。

圖表結構

除了用來顯示各部分的區域之外,圖表控制項本身並不會多加定義圖表。例如,它並不會控制圖表是圓形圖或橫條圖。按照邏輯來說,我們可以假定如下其中之一的內容:

        < charting : PieChart />
        < charting : Chart Type ="Pie" />

不過,事實上我們的內容更有彈性,能夠加入新的圖表樣式,而不會改變現有的列舉或是讓大量的 XyzChart 控制項淹沒命名空間。圖表的類型是由您加入圖表的「數列類型」所定義的。目前支援的數列包括:

  • BarSeries
  • BubbleSeries
  • ColumnSeries
  • LineSeries
  • PieSeries
  • ScatterSeries

這每一種數列都會符合我們所想的圖表類型。例如,以下是一個簡單圓形圖的標記,其中具有硬式編碼資料 (請注意,該種資料只有單一維度而沒有實際的標籤,同時並未使用任何一種繫結運算式加以繫結):

< charting : Chart x : Name ="ExampleChart">
    < charting : PieSeries >
        < charting : PieSeries.ItemsSource >
           <controls:ObjectCollection>
               <sys:Double>1</sys:Double>
               <sys:Double>2.3</sys:Double>
               <sys:Double>3.5</sys:Double>
               <sys:Double>5</sys:Double>
            </controls:ObjectCollection>
        </ charting : PieSeries.ItemsSource >
    </ charting : PieSeries >
</ charting : Chart >

使用 Xaml 設定 ItemsSource 雖然可以很順利地塑造原型,不過如果您偏好在程式碼中設定資料,也可以這樣做:

< charting : Chart x : Name ="ExampleChart">
    < charting : PieSeries />
</ charting : Chart >
void Page_Loaded(object sender, RoutedEventArgs e)
{
    SetChartData();
}

private void SetChartData()
{
    List<double> data = new List<double>() { 1, 2.3, 3.5, 5 };

    ((PieSeries)ExampleChart.Series[0]).ItemsSource = data;  
}

不論使用哪一種方法,最後都會做出外觀如下的圖表;

這麼棒的圖表,只用了寥寥幾行的標記!圖表的預設視覺效果包括圖表和圖例的漸層背景、整個圖表周圍的框線,以及具有柔和色彩和強調用放射狀漸層的「樣式面板」。

樣式面板

要自訂圖表的外觀,其中一個最簡單的方式就是更改樣式面板。樣式面板提供一組樣式,可供數列用來呈現其個別資料點。以圓形圖為例,一個資料點是指圓形圖上的一個切片。在橫條圖上,資料點則是指橫條。

如果您要簡單的單色圖表,可以提供如下的單一樣式:

< charting : Chart x : Name ="ExampleChart">
    < charting : PieSeries >
  
         <!-- Styles for the pie slices -->
        < charting : PieSeries.StylePalette >
            < datavis : StylePalette >
                < Style TargetType ="charting:PieDataPoint">
                    < Setter Property ="Background"
                           Value="Black">
                   </Setter>
               </Style>
           </datavis:StylePalette>
        </charting:PieSeries.StylePalette>
        <!-- End Styles -->

        <charting:PieSeries.ItemsSource>
            <controls:ObjectCollection>
               <sys:Double>1</sys:Double>
               <sys:Double>2.3</sys:Double>
               <sys:Double>3.5</sys:Double>
               <sys:Double>5</sys:Double>
            </ controls : ObjectCollection >
        </ charting : PieSeries.ItemsSource >
    </ charting : PieSeries >
</ charting : Chart >

既然我們已經提供了樣式面板,圖表就會限制自己只使用面板內的樣式。圖表會循環配置可用樣式中的所有資源,以自然的順序在資料點中使用資源。在本範例中,我們只提供單一樣式,所以結果會像是這樣:

(請注意,我為了這篇文章將黑色的框線裁切掉了,讓圖看起來小一點。後面我們會設定整個控制項的樣式,到時候再實際移除框線。)

您應該可以想見,只要提供兩種樣式,就能讓圖表在兩種樣式之間進行選擇:

...
< charting : PieSeries.StylePalette >
    < datavis : StylePalette >
        < Style TargetType ="charting:PieDataPoint">
            < Setter Property ="Background"
                   Value="Black">
           </Setter>
        </Style>
        <StyleTargetType="charting:PieDataPoint">
           <Setter Property="Background"
                   Value="DarkGray">
           </Setter>
        </Style>
   </datavis:StylePalette>
</charting:PieSeries.StylePalette>
...

樣式還可以改變除了背景之外的其他屬性。例如,您可以變更 BorderBrush。像是框線粗細之類的設定會在呈現時產生不自然感 (至少在圓形圖上會發生),所以我會避免使用這些設定,或起碼在處理這些設定時特別小心。對於不共用框線的圖表來說 (例如橫條圖或直條圖),這些設定的效果會比較好。

您可以隨意加入任何數量的樣式。如果您加入的樣式少於資料點,可能會有一些樣式重複。如果您加入的樣式多於資料點,多餘的樣式不會使用。

如果您要跨多種類型圖表重複使用樣式,請將目標類型定為控制項而非特定類型的資料點。這樣比較方便重複使用,不過當您使用控制項範本時要特別小心,我們會在文章稍後這麼做。

要自訂圖表的外觀,另外一個簡單的方式是提供自訂的樣式面板。接下來,讓我們一探圖表其餘的奧妙之處,了解它的組成方式。

分解圖表範本

圖表有四個主要區域和四個相關樣式:

屬性樣式目標類型
ChartAreaStyle格線
LegendStyle圖例 (一種控制項,類似於具有標題的 ItemsControl)
PlotAreaStyle格線
TitleStyle標題 (屬於 ContentControl)

您可以在以下的範例圖表中看到這些區域。我在 PlotArea 周圍加入 5px 的邊界,讓 ChartArea 更為顯眼。在圓形圖上,這些區域則會佔據相同的空間,而 PlotArea 在最前面。在多軸圖表上 (例如LineSeries 或 ScatterSeries),PlotArea 是軸內的區域,而軸和標籤則在 ChartArea 裡面和 PlotArea 的外面。

在範本中,負責控制圖表元素整體排列的部分,看起來像這樣 (有加入說明註解):

< ControlTemplate TargetType ="charting:Chart">
    < Border Background ="{ TemplateBinding Background }"
           BorderThickness="{TemplateBinding BorderThickness}"
            Padding="10">
        <Grid>
           <Grid.RowDefinitions>
               <RowDefinition Height="Auto" />
               <RowDefinition Height="*" />
           </Grid.RowDefinitions>
          
           <!-- Title -->
           <datavis:TitleStyle="{TemplateBinding TitleStyle}"
                           Content="{TemplateBinding Title}" />
          
          
           <Grid Margin="0,15,0,15"
                 Grid.Row="1">
               <Grid.ColumnDefinitions>
                   <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
               </Grid.ColumnDefinitions>
              
               <!-- Legend -->
               <datavis:Legendx:Name="Legend"
                               Style="{TemplateBinding LegendStyle}"
                                Grid.Column="1"
                               Title="{TemplateBinding LegendTitle}"
                               BorderBrush="{x:Null}"
                               Background="{x:Null}" />
              
               <!-- Chart Area -->
               <Grid x:Name="ChartArea"
                     Style="{TemplateBinding ChartAreaStyle}">
                  
                   <!-- Plot Area -->
                   <Grid x:Name="PlotArea"
                         Style="{TemplateBinding PlotAreaStyle}"
                         Background="{x:Null}">
                       <Grid x:Name="GridLinesContainer" />
                       <Grid x:Name="SeriesContainer" />
                       <Border BorderBrush="{x:Null}"
                               BorderThickness="1" />
                   </Grid>
                  
               </Grid>
              
           </Grid>
        </Grid>
   </Border>
</ControlTemplate>

因為有版面配置,您可以隨意地四處移動這些片段。Silverlight (和 WPF) 的其中一個優點就是不必依賴協力廠商提供具有 LegendPosition 等屬性 (包含上、左、下、右等值) 的圖表,控制項作者只需要透過範本上的元素確認圖例的位置即可 (在此範例中是一個簡單的 [格線] 欄)。身為設計人員,這可以讓您完全掌握圖例的位置和樣式。

Blend 中的圖表範本

記住這一點之後,到 Blend 來了解樣式吧。請先選取圓形圖的圖表 (而非 PieSeries),然後編輯範本。我們要建立一個新的樣式,並且將它保留在 Page.xaml 中,供本範例使用。

產生的範本很容易了解,並且與上方具有註釋的圖片和 Xaml 相對應。

還記得圖表周圍的黑色框線嗎?框線的來源就是最外面的 Border 元素。如同您所推測的,色彩會繫結到控制項的 BorderBrush,所以您可以直接編輯框線,或是設定控制項的 BorderBrush。設定控制項的筆刷比較單純,不過如果根本不需要框線,將框線完全移除比較合理。

在這裡我選簡單的方法,直接將框線設為透明的 BorderBrush。由於框線的框線筆刷會以範本繫結到圖表控制項,如果您要直接編輯筆刷,必須重設筆刷或是轉換為本機值。

當您將最外面的圖表樣式設定完畢後,有幾個選擇。您可以直接設定內部組件 (ChartArea、PlotArea) 的樣式,或是為它們建立個別樣式。既然我已經開啟了控制項範本,我打算直接編輯它們。如果您只需要設定一小部分的圖表樣式,而且/或您需要在應用程式的其他圖表樣式中重複使用某個特定的樣式,您應該會想要建立獨立的樣式。

當您要與設計團隊中的其他成員共用樣式時,也是另一個需要編輯獨立樣式的原因。其他成員照理會預期您將樣式資訊包含在 XyzStyle 屬性之中,而非以硬式編碼直接寫入範本。

要編輯現有的樣式並避免與新樣式重複,請選取您要的物件 (例如「標題」),然後依序選取 [物件]、[編輯樣式] 和 [編輯樣式]:

當我清除各種框線和背景筆刷之後,圖表的外觀會變成這樣:

通常,越簡單抽象的圖表越適合用在您的應用程式中。您可以在圖表本身之外提供色彩,而且當然也可以為資料點提供一組更好的樣式。

圖例樣式

在圖表控制項本身之中,圖例是唯一複雜的元素樣式。讓我們稍微深入了解一下。不過在此之前,我們需要更多的可用資料。到目前為止,我們的資料點及其隨之而來的圖例,與我們在實際的應用程式中看到的不完全一樣。

我們要將其餘的樣式保持簡單,並且開始進行一個單純的圖表實作 (不重做範本)。

< charting : Chart x : Name ="ExampleChart"
               Title="Hello World" >
   <charting:PieSeries>
   </charting:PieSeries>
  
</charting:Chart>

建立 ChartPointData 類別

由於 Silverlight Xaml 無法直接使用泛型,我們不能只使用 KeyValuePair<TKey, TValue> 這樣的程式碼。相反地,我們需要建立特殊用途的類別,在其中包含標籤和值。這真的很簡單:

namespace PeteBrown.ExpressionNewsletterCharts
{
    public classChartPointData
    {
        public double Value { get; set; }
        public string Label { get; set; }
    }
}

現在,我們可以使用這個類別的執行個體而非 sys:Double。首先,我們需要建立本機命名空間的 xmlns 項目:

xmlns:local="clr-namespace:PeteBrown.ExpressionNewsletterCharts"

接著,我們可以加入自己的圖表資料:

< charting : PieSeries.ItemsSource >
    < controls : ObjectCollection >
        < local : ChartPointData Value ="1"
                             Label="Pete" />
        <local:ChartPointData Value="3"
                             Label="David" />
        <local:ChartPointData Value="9"
                             Label="Justin" />
        <local:ChartPointData Value="5"
                             Label="Shawn" />
        <local:ChartPointData Value="5"
                             Label="John" />
   </controls:ObjectCollection>
</charting:PieSeries.ItemsSource>


繫結資料點

最後,我們需要設定一些繫結。請如下變更 PieSeries 宣告:

<charting:PieSeries DependentValueBinding="{Binding Value}"
                    IndependentValueBinding="{Binding Label}">

相依值是顯示在圖表上的值。非相依值是顯示在圖例中的標籤。這樣的說明,對於我們的圓形圖已經足夠了,不過離完成還有一大段距離。如需相依和非相依變數的詳細說明,請參閱 this Wikipedia Entry

當然,您不必在 Xaml 中加入資料,只要在 Xaml 背後的程式碼中加入就可以了。不過,我發現設計人員如果能夠直接操控 Xaml 中的資料,試用不同組合並且偵錯/測試自己的樣式成果,應該會方便的多。當然,您也可以在之後改用 DataContext Set 陳述式。

執行後會獲得如下結果:

身為設計人員,現在您有一些適當的資料可供使用了。在建立設計時,最糟糕的莫過於因為資料不實,最後發現實際的資料模型根本不一樣。

圖例

回想一下,圖例是在圖表右邊的區域,用來顯示圓形圖切片的色彩和非相依值。現在,來認識圖例吧。完整的範本外觀如下:

還有在 Xaml 中的外觀:

< Style x : Key ="LegendStyle1"
       TargetType="datavis:Legend">
   <Setter Property="BorderBrush"
            Value="Black" />
   <Setter Property="BorderThickness"
            Value="1" />
   <Setter Property="IsTabStop"
            Value="False" />
   <Setter Property="TitleStyle">
        <Setter.Value>
           <Style TargetType="datavis:Title">
               <Setter Property="Margin"
                       Value="0,5,0,10" />
               <Setter Property="FontWeight"
                        Value="Bold" />
               <Setter Property="HorizontalAlignment"
                       Value="Center" />
           </Style>
        </Setter.Value>
   </Setter>
   <Setter Property="Template">
        <Setter.Value>
           <ControlTemplate TargetType="datavis:Legend">
               <Border Background="{TemplateBinding Background}"
                       BorderBrush="{TemplateBinding BorderBrush}"
                       BorderThickness="{TemplateBinding BorderThickness}"
                       Padding="2">
                   <Grid>
                       <Grid.RowDefinitions>
                           <RowDefinition Height="Auto" />
                           <RowDefinition />
                       </Grid.RowDefinitions>
                      
                       <!-- Title -->
                       <datavis:TitleStyle="{TemplateBinding TitleStyle}"
                                      Grid.Row="0"
                                      Content="{TemplateBinding Title}" />
                      
                       <!-- ScrollViewer for Items -->
                       <ScrollViewer BorderThickness="0"
                                     IsTabStop="False"
                                     Padding="0"
                                     Grid.Row="1"
                                     VerticalScrollBarVisibility ="Auto">
                          
                           <!-- StackPanel containing all legend items -->
                             <StackPanelMargin="10,0,10,10"
                                       x:Name="LegendItemsArea"
                                       Grid.Column="1" />
                       </ScrollViewer>
                   </Grid>
               </Border>
           </ControlTemplate>
        </Setter.Value>
   </Setter>
</Style>

從這裡可以看出,您可以變更圖例的版面配置、改變面板類型 (例如使用 WrapPanel 而非 StackPanel),以及允取刪除捲動功能。

圖例項目樣式

如果您要修改圖例項目本身的外觀,就要使用 LegendItemStyle,這是 Series 而非 Legend 的成員。預設範本的外觀如下 (索引碼是我加入的):

< Style x : Key ="LegendItemStyle" TargetType ="charting:LegendItem">
    < Setter Property ="IsTabStop"
           Value="False" />
   <Setter Property="Template">
        <Setter.Value>
           <ControlTemplate TargetType="charting:LegendItem">
               <StackPanel Orientation="Horizontal">
                   <Rectangle Width="8"
                              Height="8"
                              Fill="{BindingBackground}"
                              Stroke="{BindingBorderBrush}"
                              StrokeThickness="1"
                              Margin="0,0,3,0" />
                   <datavis:TitleContent="{TemplateBinding Content}" />
               </StackPanel>
           </ControlTemplate>
        </Setter.Value>
   </Setter>
</Style>

如果要變更索引碼,只要加入您的資源樣式,然後使用 XAML 資源庫直接從數列樣式設定參照,或是透過如下的圖表標記:

< charting : PieSeries DependentValueBinding ="{ Binding Path =Value}"
                   IndependentValueBinding ="{Binding Path=Label}"
                   LegendItemStyle="{StaticResource LegendItemStyle}">

例如,這個圖例控制項範本會以格線取代堆疊面板、設定一些邊界、變更字型色彩,並且刪除 8px 大小的矩形。

< ControlTemplate TargetType ="charting:LegendItem">
    < Grid >
        < Rectangle Fill ="{ Binding Background }"
                  Stroke="{BindingBorderBrush}"
                  StrokeThickness="1"
                  Margin="1" />
        <datavis:TitleContent="{TemplateBinding Content}"
                      Margin="4"
                      Foreground="White"/>
   </Grid>
  
</ControlTemplate>

最後的圖例會像這樣:

/p>

使用工具提示取代圖例

如果您根本不想要使用圖例,該怎麼辦?這時,您可以修改顯示圖例資訊的工具提示。這個方法,不是那麼立刻容易理解:您需要重新製作 PieDataPoint 的範本,不過要新增一個空白的 PieDataPoint。這很簡單,因為 PieDataPoint 就跟其他控制項一樣。

在頁面裡面和圖表的外面,加入下行:

< charting : PieDataPoint x : Name ="DummyPointForStyling" />

現在您可以修改包括工具提示在內的樣式了。完整的樣式如下:

< Style x : Key ="PieDataPointStyle1"
       TargetType="charting:PieDataPoint">
   <Setter Property="Background"
            Value="Orange" />
   <Setter Property="BorderBrush"
            Value="White" />
   <Setter Property="BorderThickness"
            Value="1" />
   <Setter Property="IsTabStop"
            Value="False" />
   <Setter Property="RatioStringFormat"
            Value="{}{0:p2}" />
   <Setter Property="Template">
        <Setter.Value>
           <ControlTemplate TargetType="charting:PieDataPoint">
               <Grid x:Name="Root"
                     Opacity="0">
                  
                   <vsm:VisualStateManager.VisualStateGroups >
                       <vsm:VisualStateGroup x:Name="CommonStates">
                           <vsm:VisualStateGroup.Transitions>
                               <vsm:VisualTransition GeneratedDuration="0:0:0.1" />
                           </vsm:VisualStateGroup.Transitions>
                           <vsm:VisualStatex:Name="Normal" />
                           <vsm:VisualStatex:Name="MouseOver">
                               <Storyboard>
                                   <DoubleAnimation Duration="0"
                                                    Storyboard.TargetName ="MouseOverHighlight"
                                                    Storyboard.TargetProperty ="Opacity"
                                                    To="0.6" />
                               </Storyboard>
                           </vsm:VisualState>
                       </vsm:VisualStateGroup>
                      
                       <vsm:VisualStateGroup x:Name="SelectionStates">
                           <vsm:VisualStateGroup.Transitions>
                               <vsm:VisualTransition GeneratedDuration="0:0:0.1" />
                           </vsm:VisualStateGroup.Transitions>
                           <vsm:VisualStatex:Name="Unselected" />
                           <vsm:VisualStatex:Name="Selected">
                               <Storyboard>
                                   <DoubleAnimation Duration="0"
                                                    Storyboard.TargetName ="SelectionHighlight"
                                                    Storyboard.TargetProperty ="Opacity"
                                                    To="0.6" />
                               </Storyboard>
                           </vsm:VisualState>
                       </vsm:VisualStateGroup>
                      
                       <vsm:VisualStateGroup x:Name="RevealStates">
                           <vsm:VisualStateGroup.Transitions>
                               <vsm:VisualTransition GeneratedDuration="0:0:0.5" />
                           </vsm:VisualStateGroup.Transitions>
                           <vsm:VisualStatex:Name="Shown">
                               <Storyboard>
                                   <DoubleAnimation Duration="0"
                                                     Storyboard.TargetName ="Root"
                                                    Storyboard.TargetProperty ="Opacity"
                                                    To="1" />
                               </Storyboard>
                           </vsm:VisualState>
                           <vsm:VisualStatex:Name="Hidden">
                               <Storyboard>
                                   <DoubleAnimation Duration="0"
                                                    Storyboard.TargetName ="Root"
                                                    Storyboard.TargetProperty ="Opacity"
                                                    To="0" />
                               </Storyboard>
                            </vsm:VisualState>
                       </vsm:VisualStateGroup>
                   </vsm:VisualStateManager.VisualStateGroups >
                  
                   <!-- This is the geometry for the slice -->
                    <Pathx:Name="Slice"
                         Fill="{TemplateBinding Background}"
                         Stroke="{TemplateBinding BorderBrush}"
                         Data="{TemplateBinding Geometry}">
                       <ToolTipService.ToolTip>
                           <StackPanel>
                               <ContentControl Content="{TemplateBindingFormattedDependentValue}" />
                               <ContentControl Content="{TemplateBindingFormattedRatio}" />
                             </StackPanel>
                       </ToolTipService.ToolTip>
                   </Path>
                  
                   <!-- These are used by VSM for state visualization -->
                   <Path x:Name="SelectionHighlight"
                         IsHitTestVisible="False"
                         Opacity="0"
                         Fill="Red"
                         Data="{TemplateBinding GeometrySelection}" />
                  
                   <Path x:Name="MouseOverHighlight"
                         IsHitTestVisible="False"
                         Opacity="0"
                         Fill="White"
                         Data="{TemplateBinding GeometryHighlight}" />
               </Grid>
            </ControlTemplate>
        </Setter.Value>
   </Setter>
</Style>

工具提示包含在圓形圖切片的路徑中。根據預設,它會顯示數字和比例。此外,您還可以繫結其他一些屬性,以便提供更多資訊:

屬性描述
DependentValue圓形圖切片代表的值
FormattedDependentValue使用 DependentValueStringFormat 中的格式字串設定格式的 DependentValue 屬性
IndependentValue圓形圖切片的標籤
Ratio此切片在整個圓形圖中的比例
FormattedRatio使用 RatioStringFormat 中的格式字串設定格式的 Ratio 屬性

如果要看一些實際運作的屬性,可以如下變更工具提示:

< ToolTipService.ToolTip >
    < StackPanel >
        < ContentControl Content ="{ TemplateBinding IndependentValue }" />
        < ContentControl Content ="{ TemplateBinding FormattedDependentValue }" />
        < ContentControl Content ="{ TemplateBinding FormattedRatio }" />
    </ StackPanel >
</ ToolTipService.ToolTip >


然後,修改樣式面板,確認這個範本已用在每一個圓形圖切片上。要這麼做,需要將樣式轉換為 ControlTemplate。我們可以將它保留為一個樣式,不過之後我們需要每一個 StylePalette 項目的完整複本。藉由擷取範本,我們可以加入獨特的樣式資訊,同時避免 ControlTemplate 重複。最簡單的方法,就是從樣式中擷取 ControlTemplate,然後加上 ”PieDataPointTemplate” 之類的索引碼。然後,在樣式面板內的每一個樣式中加入:

< Setter Property ="Template" Value="{StaticResource PieDataPointTemplate}" />

在 Xaml 中將新的 ControlTemplate 移到圖表樣式上方 (不然會出現找不到資源的錯誤)。最後,確認您將空白的 PieDataPoint 轉換為註解。

最後的工具提示外觀如下。因為資料這時都在圖表本身之內,所以我們可以在不遺失任何資訊的情況下,刪除圖例。

其他資料點樣式

您可能會注意到,到目前為止我們看過的控制項中,PieDataPoint 是第一個包含視覺狀態的控制項。在 Visual State Manager 中,您可以做很多事情。如果您還沒用過,建議您試試其中一個簡單直接卻完整的範例 (例如 Button)。

接著我們要修改 PieDataPoint,加入一些要顯示的動畫。您可以廣泛運用此範例中的步驟,一併處理其他狀態。

將我們為了要設定樣式而建立的空白 PieDataPoint 取消註解,讓它使用我們建立的範本來取代原本的樣式。

< charting : PieDataPoint x : Name ="DummyPointForStyling"
   Template ="{ StaticResource PieDataPointTemplate }" />

使用 Blend 中的階層連結列,編輯 PieDataPoint 目前的範本。這個範本會共用於 StylePalette 中的所有樣式。

首先,我們要讓每個切片在顯示時出現一點彈跳效果。

在 [狀態] 窗格中,從 RevealStates 群組選取 [顯示] 狀態。確認 [顯示] 狀態目前在錄製模式中,然後展開其時間軸。

在零的位置建立主要畫面格,將切片的 RenderTransform X 軸縮放和 Y 軸縮放都設為零。這就是我們的起點。

接下來,在 0.5 秒的位置錄製主要畫面格,將 X 軸縮放和 Y 軸縮放都設為 1.2。這是第二個主要畫面格。

最後,在 0.7 秒的位置錄製主要畫面格,將 X 軸縮放和 Y 軸縮放都設為 1.0。這是第三個也是最後一個主要畫面格。

到目前為止,彈跳效果都還是線性的,所以我們要調整加/減速曲線,以非線性的方式控制值。按一下第二個主要畫面格的主要畫面格標記,同時調整 KeySpline 讓轉換效果由慢變快漸增為主要畫面格值。像下圖這樣就可以了:

您可以視需要調整曲線,讓效果更明顯。然後,調整最後的主要畫面格,讓它稍微往內彈。

最後,我們要移除控制顯示不透明度的 DoubleAnimation。您可以在 Xaml 中手動刪除或是使用 Blend 移除。在此範例中,我發現用 Xaml 編輯比較簡單。只要從 [顯示] 視覺狀態移除這個腳本即可。

< DoubleAnimation Duration ="0"
      Storyboard.TargetName ="Root"
      Storyboard.TargetProperty ="Opacity"
      To ="1" />

接著,選取 [根] 並將其不透明度改成 100%。目前值是零,因為預設顯示動畫會隨著時間將不透明度從 0 變成 1。

最後,將 “RevealStates” 上面的轉換時間改成 0.7,使其符合我們新的時間軸。

如果您在這時執行,會發現切片一開始是完整大小,然後收縮,接著又展開。要修正這個問題,我們需要將 ScaleTransform 的開始值改設為零。這是最後一件要做的事,因為當切片大小設為 0% 之後,就很難設定切片的樣式了。

[顯示] 視覺狀態的最終 Xaml 會像下面這樣:

< vsm : VisualState x : Name ="Shown">
    < Storyboard >
        < DoubleAnimationUsingKeyFrames BeginTime ="00:00:00"
                                      Storyboard.TargetName ="Slice"
                                      Storyboard.TargetProperty ="(UIElement.RenderTransform).
                                            (TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
           <SplineDoubleKeyFrame KeyTime="00:00:00"
                                 Value="0" />
           <SplineDoubleKeyFrame KeyTime="00:00:00.5000000"
                                 Value="1.2"
                                 KeySpline="0.964999973773956,0.188999995589256,
                                            0.889999985694885,0.449000000953674" />
           <SplineDoubleKeyFrame KeyTime="00:00:00.7000000"
                                 Value="1"
                                  KeySpline ="0.280000001192093,0.768999993801117,
                                              0.307000011205673,0.833999991416931" />
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                      Storyboard.TargetName ="Slice"
                                      Storyboard.TargetProperty ="(UIElement.RenderTransform).
                                            (TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
           <SplineDoubleKeyFrame KeyTime="00:00:00"
                                 Value="0" />
           <SplineDoubleKeyFrame KeyTime="00:00:00.5000000"
                                 Value="1.2"
                                 KeySpline="0.964999973773956,0.188999995589256,
                                            0.889999985694885,0.449000000953674" />
            <SplineDoubleKeyFrameKeyTime="00:00:00.7000000"
                                 Value="1"
                                 KeySpline="0.280000001192093,0.768999993801117,
                                            0.307000011205673,0.833999991416931" />
        </DoubleAnimationUsingKeyFrames>
   </Storyboard>
</vsm:VisualState>

在此範例中,所有圓形圖切片都會同時彈現。如果想要更有趣一點,您可以將 PieSeries 上的 AnimationSequence 從 Simultaneous 改成其他順序。

< charting : PieSeries DependentValueBinding ="{ Binding Path =Value}"
                   IndependentValueBinding ="{Binding Path=Label}"
                   AnimationSequence="FirstToLast">

這時產生的動畫會依序彈現各切片,而且各切片開始時都有些許的延遲。以下是放映中的動畫螢幕擷取畫面:

如果您用這個方法,我建議縮減動畫的時間,讓它不要超過 1/3 秒左右。

延伸技巧

Chart 控制項具備足夠的開放性與彈性,即使是團隊在建立控制項時沒想到的樣式設計方式也受到支援。例如絕對位置漸層和重疊這兩種樣式都受到支援,不過我們將在別篇文章中另外討論。

絕對對應漸層

當您要保留不同圓形圖切片之間的界線,但又要使用一致的漸層外觀時,這個技巧非常有用。在此範例中,我要在圓形圖周圍加上深色的框線,藉此凸顯所有切片。您也可以使用這個技巧,用單一漸層覆蓋所有切片,並且在左上方加上單一亮點 (這很適合使用放射狀漸層)。

雖然您可以在不使用任何程式碼的情況下實作這個技巧,不過很快就會遇到縮放問題。幸好 Silverlight 工具組團隊的 David Anson 已經整理了一篇好用的部落格文章,其中提供程式碼讓您在自己的應用程式中實作這個技巧。如需這個技巧的詳細資訊,請參閱 Yummier pies!– Delay’s Blog (英文)

重疊

重疊效果包括特殊玻璃效果,以及其他比起簡單漸層或多重漸層更為複雜的技巧。一般而言,重疊技巧會與絕對對應漸層技巧一起使用。我們將在 my blog 上的最新文章中討論重疊效果。

結論

隨附於 Silverlight 工具組的圖表控制項有一些很棒的預設視覺效果,而且還能如同其他任何 Silverlight 控制項一般讓您加以自訂。我們只介紹了其中一小部分的功能,不過您可以將我們示範的技巧輕鬆地運用到其他數列類型並且進一步延伸,建立完全不同的效果。

如果您對本文章有所疑問,請透過 Pete Brown 的部落格 www.irritatedVowel.com/blog 與他連絡。

Pete Brown 是用戶端應用程式開發 (Silverlight 和 WPF) 領域的 Microsoft 最有價值專家 (Microsoft MVP),同時也是 INETA Speakers Bureau 發言人以及 Applied Information Sciences 公司 (位於美國華盛頓特區) 的設計師/專案經理。從他在 Commodore 64 電腦上第一次製作小精靈圖形和自訂字元集,一直到透過 Silverlight、Surface、Xna 和 WPF 建立 3D 模型和設計,他一向對程式編寫、設計和使用者體驗深感興趣。Pete 與 Silverlight 的淵源,始自於共同撰寫 Silverlight 1.1 Alpha 版應用程式,該程式於 2007 年 7 月開始生產。他閒時喜歡為自己的木工計劃編寫程式、建立部落格、進行設計和建造。他與他的太太在美國馬里蘭州育有兩子。Pete 的部落格位於
www.irritatedVowel.com/Blog

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值