本文章是由機器翻譯。

地基

撰寫更有效的 ItemsControls

Charles Petzold

可從 MSDN 程式庫 的程式碼下載
瀏覽線上的程式碼

內容

一個 ItemsControl 的散佈圖繪圖
[效能] Disappointment
隱藏的迴圈
使用數值轉換
監看出 Freezables !
中繼的主持人
自訂資料項目
biting the Bullet
DrawingVisual 解決方案

當為 true 的 DataTemplate 能力突然變成明顯,有提供每個 Windows Presentation Foundation (WPF) 程式設計人員的存留的時間。這個 epiphany 通常會伴隨在的解析 「 嗨,我可以使用一個 DataTemplate 建立長條圖或在散佈繪圖與幾乎任何編碼 」

搭配的 ItemsControl 或類別衍生自包含 ListBox,ComboBox,功能表,TreeView,工具列,StatusBar 的 ItemsControl 中最常建立一個 DataTemplate — 簡短,所有控制項維護的項目集合中。DataTemplate 會定義如何顯示集合中的每個項目。DataTemplate 包含大部分 Visual 樹狀目錄的一或多個的項目與連結,這些項目的屬性集合中項目的資料繫結。如果在集合中的項目 (通常藉由實作 INotifyPropertyChanged 介面),實作屬性變更告知的某些,項目的控制項可以動態回應項目的變更。

在 disappointment 可能稍後出現的。如果要顯示大量的資料,您可能發現,ItemsControl 和 DataTemplate 不調整也。這個資料行,就是您可以做什麼來對抗這些效能問題。

一個 ItemsControl 的散佈圖繪圖

從一個 ItemsControl] 和 [一個 DataTemplate,讓我們來建立在散佈圖的繪圖。第一個步驟是建立商務物件,表示資料項目。[圖 1 會顯示具有泛型名稱 DataPoint 稍微 abridged 簡單類別。DataPoint 實作 INotify­PropertyChanged 介面,表示它就會包含名為 PropertyChanged 物件屬性有變更時所引發的事件。

[圖 1: DataPoint 類別表示一個資料項目

public class DataPoint : INotifyPropertyChanged
{
    int _type;
    double _variableX, _variableY;
    string _id;

    public event PropertyChangedEventHandler PropertyChanged;

    public int Type
    {
        set
        {
            if (_type != value)
            {
                _type = value;
                OnPropertyChanged("Type");
            }
        }
        get { return _type; }
    }

    public double VariableX [...]
    public double VariableY [...]
    public string ID [...]

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

[VariableX] 和 [VariableY 屬性則表示在卡笛生的座標系統的點的位置。 (適用於本專欄,值範圍從 0 到 1)。 Type 屬性可用來群組資料點 (它會有從 0 到 5 的值以及用於顯示 6 種不同的色彩資訊),並 ID 屬性識別每個點,以文字字串。

在實際應用程式中,下列 DataCollection 類別中可能包含更多的屬性,但在這個範例中有只有一個,DataPoints,ObservableCol­lection <datapoint> 的型別:

public class DataCollection
{
    public DataCollection(int numPoints)
    {
        DataPoints = new ObservableCollection<DataPoint>();
        new DataRandomizer<DataPoint>(DataPoints, numPoints, 
                                          Math.Min(1, numPoints / 100));
    }

    public ObservableCollection<DataPoint> DataPoints { set; get; }
}

ObservableCollection 將具有您,CollectionChanged 屬性的項目會加入或從集合中移除時,會引發。

會這個特定的 DataCollection 類別所有資料項目在其建構函式中使用,都建立名為 DataPointRandomizer 進行測試的隨機資料所產生的類別。 DataPointRandomizer 物件也會設定計時器。 第二個的每個第十計時器刻度方法 「 VariableX 或 VariableY 屬性變更的點的 1%。 因此上, 平均而言,所有的點變更每隔 10 秒。

現在讓我們撰寫一些 XAML,這項資料顯示在散佈圖的繪圖。 [圖 2 ] 顯示包含一個 ItemsControl 的 UserControl。 DataCollection 的型別的物件的程式碼中會設定這個控制項的 DataContext。 ItemsControl,ItemsSource 屬性是繫結至的表示 ItemsControl 將會填入 DataPoint 的型別的項目,DataCollection DataPoints 屬性中。

[圖 2.DataDisplay1Control.xaml 檔案

<UserControl x:Class="DataDisplay.DataDisplayControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:data="clr-namespace:DataLibrary;assembly=DataLibrary">

    <ItemsControl ItemsSource="{Binding DataPoints}">
        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="data:DataPoint">
                <Path>
                    <Path.Data>
                        <EllipseGeometry RadiusX="0.003" RadiusY="0.003">
                            <EllipseGeometry.Transform>
                                <TranslateTransform X="{Binding VariableX}" 
                                                    Y="{Binding VariableY}" />
                            </EllipseGeometry.Transform>
                        </EllipseGeometry>
                    </Path.Data>

                    <Path.Style>
                        <Style TargetType="Path">
                            <Setter Property="Fill" Value="Red" />
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding Type}" Value="1">
                                    <Setter Property="Fill" Value="Yellow" />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding Type}" Value="2">
                                    <Setter Property="Fill" Value="Green" />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding Type}" Value="3">
                                    <Setter Property="Fill" Value="Cyan" />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding Type}" Value="4">
                                    <Setter Property="Fill" Value="Blue" />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding Type}" Value="5">
                                    <Setter Property="Fill" Value="Magenta" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Path.Style>

                    <Path.ToolTip>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding ID}" />
                            <TextBlock Text=", X=" />
                            <TextBlock Text="{Binding VariableX}" />
                            <TextBlock Text=", Y=" />
                            <TextBlock Text="{Binding VariableY}" />
                        </StackPanel>
                    </Path.ToolTip>
                </Path>
            </DataTemplate>
        </ItemsControl.ItemTemplate>

        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid Background="Azure" 
                      LayoutTransform="300 0 0 300 0 0" 
                      IsItemsHost="True" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</UserControl>

ItemsControl,ItemTemplate 屬性是設定為一個 DataTemplate,定義視覺樹狀結構的每個項目顯示中。此樹狀目錄包含其資料屬性,設定為的 EllipseGeometry 一個路徑項目。其實只是一個小點的 — 我是說極少的點和 — 與 0.003 單位的半徑。但它不是小,因為在 ItemPanel 屬性的 ItemsControl 設定為的 ItemsPanelTemplate 包含單一儲存格的框格以顯示所有點看來。這個方格會指定一個 LayoutTransform,將它縮放的 300,係數使得資料點幾乎 1 單位的半徑。

這個奇特的操作是一個 TranslateTransform 的 X 和 Y 屬性 VariableX] 和 [VariableY 屬性的資料繫結的規定。VariableX] 和 [VariableY 屬性只範圍從 0 到 1,所以需要增加以佔據的區域上螢幕 300 個單位方形格線的大小。

路徑項目的 [填滿] 屬性設定中每個 DataPoint 物件型別屬性的值為基礎的筆刷至樣式中。在路徑的物件也會指派工具提示,會顯示每個資料點的相關資訊。

[效能] Disappointment

本專欄所附原始程式碼會組成一個包含七個的專案的 Visual Studio 方案: 六個應用程式和一個的程式庫。名為 DataLibrary 程式庫將包含您,大部分共用程式碼包括 DataPoint、 DataCollection 和 DataPointRandomizer。

第一個應用程式專案會命名為 DataDisplay1。它包含 MainWindow 類別其他應用程式和 [圖 2 ] 所示的 DataDisplay1Control.xaml 檔案之間共用的。MainWindow 存取控制項顯示在散佈圖的繪圖,但是也包含一個 TextBox 輸入的項目計數,按鈕來開始建立集合和顯示三個階段的 culminate 在顯示圖表的處理序期間的已耗用時間的 TextBlock 物件中。

預設的情況下,項目數目是在 1,000,並且程式似乎正常運作。但為在 10,000,設定的項目數且顯示 [圖 3 ,也說明非常 artificiality 產生資料的顯示前延遲。

fig03.gif

[圖 3 : DataDisplay1 顯示

三個的已耗用時間會顯示。當您按一下按鈕時,來建立具有指定的資料點編號的物件型別 DataCollection 就會開始在 MainWindow.xaml.cs Click 處理常式。第一個已耗用時間,是這個集合的建立。然後集合設定為視窗的 DataContext。這是第二個的已耗用時間。第三個的已耗用 Time 是,ItemsControl 來顯示結果的資料點所需的時間。若要用來計算這個的第三個已耗用時間,我可以使用 LayoutUpdated 事件缺少的更好的替代方案。

您可以看到大量的時間會包括更新顯示 ; 我的電腦上的三個測試平均 7.7 秒。這是而干擾,特別考慮這個程式真的有任何錯誤的。它它們所設計的方式,以使用 WPF 的功能。什麼完全要在這裡?

當程式會將 DataContext 屬性設定為 DataPointCollection 的型別的物件中時,ItemsControl 就會收到 ItemsSource 屬性的屬性變更通知。ItemsControl 會列舉 DataPoint 的物件的集合,並為每個 DataPoint 物件,它會建立 ContentPresenter 物件。(ContentPresenter 類別衍生從 FrameworkElement 衍生,這是相同的項目,ContentControl 衍生性工具,例如按鈕和視窗可用來顯示控制項的內容)。

為每個 ContentPresenter 物件,請在內容的屬性設定為對應的 DataPoint 物件,且在 ContentTemplate 屬性設定 ItemsControl 的 ItemTemplate 屬性]。這些 ContentPresenter 物件然後加入面板 ItemsControl 用來顯示其項目 — 在這個案例單一儲存格框格。

很快就會處理序的這一部分。ItemsControl 必須被顯示時,會出現耗費時間的一部分。因為 [面板] 中,具有累積新的子系,呼叫它的的 MeasureOverride 方法,而且需要執行的 7.7 秒 10,000 個項目的這個呼叫。

面板的 MeasureOverride 方法會呼叫每個其 ContentPresenter 子系的量值方法。如果 ContentPresenter 子尚未建立 Visual 樹狀目錄顯示它的內容,它必須現在這樣做。在 ContentPresenter 會建立根據其 ContentTemplate 屬性中儲存範本此視覺樹狀目錄。在 ContentPresenter 也必須設定該視覺化樹狀結構中的項目的屬性和它的內容的屬性 (在這個範例 DataPoint 物件) 中儲存物件的屬性之間,資料繫結。在 ContentPresenter 然後會呼叫這個視覺化樹狀結構之根項目的量值方法。

如果您有興趣探索更詳細的說明這個程序,DataLibrary DLL 就會包含一個,可讓您命名的 SingleCellGrid 探查在面板內的類別。在 DataDisplay1Control.xaml 檔案中中,,您就可以資料: SingleCellGrid,以取代格線。

ListBox 包含許多項目,但顯示只有一些時, 它會略過這個初始工作,因為根據預設,它使用,只有在它們的顯示時,才建立子系的 VirtualizingStackPanel。這是不可能使用散佈圖的繪圖,不過的。

隱藏的迴圈

電腦程式設計的基本建構在迴圈。我們使用的電腦的唯一理由是撰寫迴圈執行重複性工作。但迴圈看起來會消失,從我們的程式設計經驗。例如 F # 功能的程式語言,迴圈會 relegated 舊樣式的程式設計,並通常會取代對整個陣列、 清單和設定工作的作業。同樣地,LINQ 的查詢運算子執行作業集合上沒有明確的迴圈。

明確的迴圈從這個移動不是只變更樣式,但電腦硬體變更跟上的基本增進開發的程式設計。新電腦今天定期有兩個或四個處理器,繼續進行年,我們可能會看到與上百個以平行方式執行的處理器的電腦。處理在幕後的程式設計語言或開發架構的迴圈更容易可以利用平行處理,而不需要任何特殊的動作,由程式設計人員。

之前的魔法的未來但是,我們必須維持請注意在迴圈仍存在雖然我們無法看到它們,身為程式設計明智的作法人員一次注意,」 有不想為可用的迴圈沒有這類的東西 」。

在隱藏的迴圈,是 ItemsControl 的 DataTemplate 的 ItemTemplate 區段中定義。叫用的 DataTemplate 的可能使用於數以千計的項目和其他的物件,以及建立資料繫結的建立。

如果我們實際上有迴圈的程式碼,我們所有可能會是更仔細設計的 DataTemplate。因此對效能影響,微調 DataTemplate 是值得在時間和精力。幾乎任何您如何將可能對 perceptible 的影響效能。通常,只是多少影響 (和在哪個方向) 可能會難以預測,所以您可能會想要實驗使用幾種方法。

一般來說,您會想要簡化視覺化的 DataTemplate 的樹狀結構。嘗試將項目、 物件和資料繫結數目最小化。

移至 DataDisplay1Control.xaml],並嘗試排除資料繫結 TextBlock 項目的工具提示上。(您只可以插入任何字元,在左邊的大括號的前面)。 您應該 shave 第二個關閉先前的 7.7 秒時間的幾十的分之。

現在,整個工具提示] 區段中,和您的註解將會看到顯示時間放往下到 4.7 秒。路徑項目部分的色彩,及註解 [整個樣式] 區段中,設定填滿屬性,並現在顯示時間會捨棄到 3.5 秒]。從路徑的項目中移除轉換],然後它會來到往下大約 1 秒。

不用說現在的沒有因為它未顯示在的資料,但您可能可以看到如何,您可以開始在操作中取得這些項目的影響。只是一些標記,但值得大量的實驗。

以下是變更,而不影響功能可改善效能和可讀性: path.ToolTip 標記的內容取代為下列:

<TextBlock>
    <TextBlock.Text>
        <MultiBinding StringFormat="{}{0}, X={1}, Y={2}">
            <Binding Path="ID" />
            <Binding Path="VariableX}" />
            <Binding Path="VariableY}" />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

繫結上的 [StringFormat] 選項是.NET 3.5 SP1,新和使用此處取得,顯示時間下從 7.7 秒到 6.4。

使用數值轉換

資料繫結,可以選擇性地參考呼叫實作 IValueConverter 或 IMultiValueConverter 介面的值轉換程式的一些類別。 方法名為值轉換程式的轉換,並 ConvertBack 執行資料繫結來源與目的地之間的轉換]。

轉換程式通常歸納起來,是適用於各種應用程式,其中一個範例分別將 True 和 False 轉換成 visibility.visible 和 visibility.collapsed,方便 BooleanToVisibilityConverter。 但轉換程式會為臨 hoc 您需要它們。

若要簡化 DataTemplate,並減少資料繫結的數目],就可以建立兩個轉換程式。 [圖 4 ] 所示的轉換器名為 IndexToBrushConverter (包含在 DataLibrary DLL),,轉換為筆刷的非負數的整數。 這個轉換子會有一個名為型別的筆刷陣列的筆刷的公用屬性,整數只要該陣列的索引。

[圖 4 IndexToBrushConverter 類別

public class IndexToBrushConverter : IValueConverter
{
    public Brush[] Brushes { get; set; }

    public object Convert(object value, Type targetType, 
                          object parameter, CultureInfo culture)
    {
        return Brushes[(int)value];
    }

    public object ConvertBack(object value, Type targetType, 
                              object parameter, CultureInfo culture)
    {
        return null;
    }
}

XAML 檔資源區段中, 轉換子可以具的 x: 陣列群組就像這樣:

<data:IndexToBrushConverter x:Key="indexToBrush">
    <data:IndexToBrushConverter.Brushes>
        <x:Array Type="Brush">
            <x:Static Member="Brushes.Red" />
            <x:Static Member="Brushes.Yellow" />
            <x:Static Member="Brushes.Green" />
            <x:Static Member="Brushes.Cyan" />
            <x:Static Member="Brushes.Blue" />
            <x:Static Member="Brushes.Magenta" />
        </x:Array>
    </data:IndexToBrushConverter.Brushes>
</data:IndexToBrushConverter>

然後您可以取代整個樣式區段中顯示 [圖 2] 與繫結,會參考這個轉換子:

<Path Fill="{Binding Type, Converter={StaticResource indexToBrush}}">

DataLibrary 中的第二個轉換子會呼叫 DoublesToPointConverter。 這個轉換子,實作 IMultiValueConverter) 介面,以建立從兩個替身 X 和 Y 的原點。 請注意使用這個轉換子允許 ellipse­geometry,直接,設定 」 的 Center 屬性不需要在 TranslateTransform。

但 DataDisplay2 專案會包含這些轉換程式,並使用 StringFormat 方法,以在的工具提示 disappointing 時間: 7.7 秒的工具提示沒有,4.4 秒,及 3.4 使用常數會填滿筆刷。

通常,我預期轉換程式,以改善效能。 我不確定的不在此,大小寫,但我不會很驚訝,它是一個 Boxing 和 Unboxing 的問題的原因。 在的 DoublesToPointConverter 例如,連入的雙值必須是封裝並 Unboxed,而將外寄的點 Boxed 和 Unboxed。

監看出 Freezables !

如果您希望真的請參閱 DataDisplay2 的效能降低,嘗試取代的筆刷的這些 x: 靜態成員:

<x:Static Member="Brushes.Red" />

使用 SolidColorBrush 物件:

<SolidColorBrush Color="Red" />

顯示時間設定 leaps,超過 20 秒後並 ! 但在 x: 靜態和 SolidColorBrush 項目看起來相當多相同。 使用色彩設定為紅色,則靜態的 Brushes.Red 屬性會傳回一個 SolidColorBrush。

但請記住 SolidColorBrush 會衍生自 Freezable 的。 Brushes.Red 屬性會傳回一個 SolidColorBrush,已凍結和執行緒安全。 無法變更值。 當這個筆刷傳遞至 Visual 撰寫系統時,它可以被視為常數。

在明確的 SolidColorBrush 不過,未凍結。 它會保持作用 Visual 撰寫系統中,並將回應變更其色彩屬性。 該動態 potentiality 更複雜的系統處理,而且,會顯示本身的效能降低。

結果,請注意您應該凍結任何可將無法再變更的物件。 在程式碼,請您呼叫凍結方法,,在 XAML 中,您可以使用 PresentationOptions:freeze 選項]。 通常,您會發現時,差異會微不足道,但在 10,000 個圖形物件未凍結的筆刷使用,很明顯的一個重要的因素。

中繼的主持人

在許多常見的應用程式架構,很習慣有某種使用者介面和實際的商務物件之間的媒介 (這個範例,DataPoint 和 DataCollection)。 出的相對傳統,我會呼叫此媒介物 「 主持人 」 (雖然傳統的主持人執行而多個,我將會在此顯示)。

其中一個這個主持人的角色可能是讓從商務物件的資訊更多 amenable 繫結至使用者介面。 舉例來說,DataPoint 商務物件會有名為 VariableX 和 VariableY 的雙精度浮點數的型別的屬性。 在 DataPointPresenter 類別 (如我會呼叫它) 可能而有一個屬性命名為 Point 型別的變數。

為了效能目的,我撰寫衍生自 DataPoint DataPointPresenter。 除了將變數的屬性它也會定義名為 (型別的筆刷) 的筆刷和 (型別的字串) 的工具提示的屬性。 [圖 5] 會顯示一個少量縮寫的版本,DataPointPresenter 類別。

圖 5]: DataPointPresenter 類別

public class DataPointPresenter : DataPoint
{
    static readonly Brush[] _brushes = 
    { 
        Brushes.Red, Brushes.Yellow, Brushes.Green, 
        Brushes.Cyan, Brushes.Blue, Brushes.Magenta 
    };
    Point _variable;
    Brush _brush;
    string _tooltip;

    public Point Variable [...]
    public Brush Brush [...]
    public string ToolTip [...]

    protected override void OnPropertyChanged(string propertyName)
    {
        switch (propertyName)
        {
            case "VariableX":
            case "VariableY":
                Variable = new Point(VariableX, VariableY);
                goto case "ID";

            case "ID":
                ToolTip = String.Format("{0}, X={1}, Y={2}",
                                        ID, VariableX, VariableY);
                break;

            case "Type":
                Brush = _brushes[Type];
                break;
        }
        base.OnPropertyChanged(propertyName);
    }
}

DataLibrary DLL 也會包含 DataCollectionPresenter 類別,但它可以維持 DataPointPresenter 物件的集合與 DataCollection 完全相同。

DataDisplay3 專案會包含這些 Presenter 類別。 整個的 DataTemplate 看起來如下:

<DataTemplate DataType="data:DataPointPresenter">
    <Path Fill="{Binding Brush}"
          ToolTip="{Binding ToolTip}">
        <Path.Data>
            <EllipseGeometry Center="{Binding Variable}"
                             RadiusX="0.003" RadiusY="0.003" />
        </Path.Data>
    </Path>
</DataTemplate>

這個方法的運作更好轉換程式使 3.3 秒,使用所有功能,包括工具提示下的,顯示時間比。 大的缺點 —,您可能 shrug 它關閉,或考慮嚴重的缺點,是各種不同的筆刷現在硬式編碼在 DataPointPresenter 類別中的。 這並不是最佳化。 在 WPF 中程式設計它很好永遠能夠繼續以微調 XAML 時關閉程式碼的檔案。

特別是如果型別的值過增加超過數字 5,在 XAML 檔中擁有的筆刷是慣用的。 其中一種方法可能是存入應用程式 XAML 檔的 [資源] 區段的筆刷的陣列,並有在第一個的執行個體,DataPointPresenter 然後將它們儲存在一個靜態的變數中存取它們。 既然您已經知道在這個範例的主持人有多少影響,我無法使用它在其餘的方法。

自訂資料項目

若要小的點繪製在散佈圖的繪圖,我已經被使用路徑項目其資料屬性,設定為的 EllipseGeometry。 因為 EllipseGeometry 的 Center 屬性的型別 Point,我了必須建立轉換程式] 或 [主持人,以取得點物件,從型別為雙精度浮點數的兩個屬性。

另一個解決方案是自訂的 FrameworkElement 衍生 CSimpleSoapAppService 繪製一個點,取代這個路徑] 和 [EllipseGeometry 的組合。 由於我們正在撰寫自己,它可以有 CenterX 和 double,而非型別的資料中心屬性的型別 CenterY 屬性點。 而不是型別的筆刷的填滿屬性,它可以有索引陣列型別,筆刷的筆刷屬性的型別 int 的 FillIndex 屬性。

這種類別 (稱為 DataDot DataLibrary 專案中) 是相當簡單,大部分包含相依性內容和這些相依性內容的換行的 CLR 屬性中。 MeasureOverride 包含一行:

return new Size(CenterX + RadiusX, CenterY + RadiusY);

OnRender 是幾乎一樣簡單:

if (Brushes != null)
    dc.DrawEllipse(Brushes[FillIndex], null, 
                   new Point(CenterX, CenterY), 
                   RadiusX, RadiusY);

在 [DataDisplay4 的專案 DataDot 會出現在如下 DataTemplate 中:

<data:DataDot CenterX="{Binding VariableX}" 
              CenterY="{Binding VariableY}"
              Brushes="{StaticResource brushes}"
              FillIndex="{Binding Type}"
              RadiusX="0.003" RadiusY="0.003">

筆刷資源索引鍵會參考包含六個色彩 x: 陣列項目。

DataDisplay4 專案使用這個自訂的項目會在 3.3 秒,以工具提示中顯示本身且不需要的 2.5 秒,這絕佳的效能改進考慮 DataDot 類別的一般性質。

biting the Bullet

如果您已移除 DataTemplate 往下到 tiniest 數目項目和物件,並減少可能,最小值,資料繫結您已經完成所有其他您可以把和效能不是仍然不夠好,讓您,可能的時候 bite the bullet。

並的意思,可能的時候的 ItemsControl 和一個 DataTemplate 的組合是確實的強大不過可能不是很對解決方案您需要這個特定的應用程式的認可。 此通知會不構成 WPF 的一個 abandonment: 您仍要使用 WPF 實作您的解決方案。 它是只在辨識 10,000 個 FrameworkElement 衍生衍生性工具的幕後建立或許並非是最有效率使用資源。 替代方案是自訂的 FrameworkElement 衍生 CSimpleSoapAppService 的整個重點 — 一個項目,而不是 10,000 個項目。

ScatterPlotRender 類別 (Class) DataLibrary DLL 中的) 衍生自 FrameworkElement 衍生,並有三個相依性屬性: ObservableNotifiableCollection <datapoint> 的型別為筆刷陣列,筆刷和背景筆刷的型別的型別的 ItemsSource。

從 [我的資料行,您可能記得 ObservableNotifiableCollection 類別 」 相依性內容和通知「 在 9 月) 2008 發行的 MSDN Magazine。 這個類別會需要其成員的實作 INotifyPropertyChanged 介面。 類別,會引發不只是當物件新增或移除要從集合,但在集合中物件的屬性變更的事件。 這是如何 ScatterPlotRender 會得到提醒當 VariableX 和 VariableY DataPoint 物件的屬性變更時。

ScatterPlotRender 類別會處理所有的這些事件非常簡單的方式。 當 ItemsSource 屬性變更,] 或 [在集合的變更或 [集合中 DataPoint 物件的屬性變更時,ScatterPlotRender 就會呼叫 InvalidateVisual。 這會產生的繪製,整個散佈繪圖的 OnRender 呼叫。 程式碼如 [圖 6 ] 所示。

[圖 6 OnRender

protected override void OnRender(DrawingContext dc)
{
    dc.DrawRectangle(Background, null, new Rect(RenderSize));

    if (ItemsSource == null || Brushes == null)
        return;

    foreach (DataPoint dataPoint in ItemsSource)
    {
        dc.DrawEllipse(Brushes[dataPoint.Type], null, 
            new Point(RenderSize.Width * dataPoint.VariableX,
                      RenderSize.Height * dataPoint.VariableY), 1, 1);
    }
}

請注意在 VariableX 和 VariableY 值乘寬度和高度,項目可能會在 XAML 檔案中要設定。 DataDisplay5 專案具有一個執行個體化 T: ScatterPlotRender 物件的 XAML 檔案就像這樣:

<data:ScatterPlotRender Width="300" Height="300"
                        Background="Azure" 
                        ItemsSource="{Binding DataPoints}"
                        Brushes="{StaticResource brushes}" />

筆刷資源索引鍵會參考凍結 SolidColor­brush 物件的陣列。

好消息是此散佈圖的繪圖出現在螢幕上非常快速地)。 繪製在其 OnRender 方法會是最快的方法,以視覺化方式呈現 WPF 項目。 壞消息是項目會繼續完全時,VariableX 或 VariableY 屬性變更中,] 和 [這個發生第二個的每個第十重繪本身)。 舊的版本用來約 10%的 CPU 時間 (在我的電腦) 更新本身。 為設定在 30%的區域中。 如果您的應用程式中,會顯示經常更新的資料,您可以修改您執行 (在隨後下),繪製方式。

其他大的缺點是現在沒有工具提示。 工具提示是不可能在這個類別,但相當繁瑣。 我必須在下一版中工具提示。

DrawingVisual 解決方案

通常從 FrameworkElement 衍生衍生之類別會在 Overload: OnRender 方法中繪製本身,但是它也可以藉由維護 Visual 的子系的集合中有視覺外觀。 這些子系會出現的上方任何在 OnRender 方法會繪製。

視覺化的子系可以是任何衍生自 Visual,包括 FrameworkElement 衍生和控制項中,但是一個 FrameworkElement 衍生 CSimpleSoapAppService 也可以建立較輕量型 Visual 的子系 DrawingVisual 物件的形式。

如果在 FrameworkElement 衍生 CSimpleSoapAppService 會建立 DrawingVisual 物件,它通常儲存它們處理一些維護 Visual 的子系所涉及的額外負荷 VisualChildren 集合中。 類別仍需要覆寫 VisualChildrenCount 和 GetVisualChild,但是。

建立 DrawingVisual 物件的每個 DataPoint,ScatterPlotVisual 類別運作方式。 當 DataPoint 物件的屬性變更時,類別只需要變更該 DataPoint 與相關聯,DrawingVisual。

如同 ScatterPlotRender 類別,ScatterPlotVisual] 類別會定義相依性名為 ItemsSource、 筆刷和背景的內容。 它也會維護 VisualChildren 集合,必須保持同步 ItemsSource 集合。 如果某個項目加入至 ItemsSource 集合,新視覺化必須加入 VisualChildren 集合中。 如果從 ItemsSource 集合中移除的項目,必須是從 VisualChildren 集合中移除對應的 Visual。 如果在 VariableX 或 VariableY 的屬性,ItemsSource 集合中項目的變更時,必須變更 VisualChildren 集合中的對應項目。

若要協助這項同步處理 VisualChildren 集合不會實際儲存 DrawingVisual 的型別的物件,但而維護型別定義內部 ScatterPlotVisual) 類別,像這樣的 DrawingVisualPlus 的物件:

class DrawingVisualPlus : DrawingVisual
{
    public DataPoint DataPoint { get; set; }
}

這個額外的屬性可讓您輕鬆在 VisualChildren 集合對應到特定的 DataPoint 物件中,尋找特定的 DrawingVisualPlus 物件。

實作這種方法之 DataDisplay6 專案是最佳的整體方法。 我確定啟動的建立時間大於稍微 DataDisplay5,但很難以明顯且更新程式的額外負荷會大幅降低。

如果您跳多達 100,000 個資料點,但是,WPF 再次 buckles 之下,負擔。 這個層級的圖形輸出,恐怕我已用盡的建議。 (可能是取得更快的電腦嗎?)

您問題或意見寄至 mmnet30@Microsoft.com.

Charles Petzold 會是 MSDN Magazine 在 longtime 特約編輯。 他的最新著作都有 The Annotated Turing: A Guided Tour through Alan Turing's Historic Paper on Computability and the Turing Machine (Wiley,2008)。 他的網站是 www.charlespetzold.com.