次の方法で共有


凍結可能オブジェクトの概要

このトピックでは、アプリケーションのパフォーマンス向上に役立つ特別な機能を提供する Freezable オブジェクトを効果的に使用して作成する方法について説明します。 フリーズ可能なオブジェクトの例としては、ブラシ、ペン、変換、ジオメトリ、アニメーションなどがあります。

「Freezable」とは何ですか?

Freezable は、2 つの状態 (unfrozen と frozen) を持つ特殊な種類のオブジェクトです。 フローゼンを解除すると、Freezable は他のオブジェクトと同様に動作するように見えます。 フリーズすると、Freezable を変更できなくなります。

Freezable は、オブジェクトへの変更をオブザーバーに通知する Changed イベントを提供します。 Freezable を固定すると、変更通知にリソースを費やす必要がなくなったため、パフォーマンスが向上する可能性があります。 固定された Freezable はスレッド間で共有することもできますが、固定されていない Freezable は共有できません。

Freezable クラスには多くのアプリケーションがありますが、Windows Presentation Foundation (WPF) のほとんどの Freezable オブジェクトはグラフィックス サブタイプに関連しています。

Freezable クラスを使用すると、特定のグラフィックス システム オブジェクトを簡単に使用でき、アプリケーションのパフォーマンスを向上させることができます。 Freezable から継承する型の例としては、BrushTransform、および Geometry クラスがあります。 アンマネージド リソースが含まれているため、システムはこれらのオブジェクトの変更を監視し、元のオブジェクトに変更がある場合は、対応するアンマネージ リソースを更新する必要があります。 グラフィックス システム オブジェクトを実際に変更しない場合でも、オブジェクトを変更した場合に備えて、システムはそのリソースの一部を監視する必要があります。

たとえば、SolidColorBrush ブラシを作成し、それを使用してボタンの背景を描画するとします。

Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myButton.Background = myBrush;
Dim myButton As New Button()
Dim myBrush As New SolidColorBrush(Colors.Yellow)
myButton.Background = myBrush

ボタンがレンダリングされると、WPF グラフィックス サブシステムは、指定した情報を使用してピクセルのグループを描画し、ボタンの外観を作成します。 単色ブラシを使用してボタンの塗り方を説明しましたが、純色ブラシでは実際には描画されません。 グラフィックス システムは、ボタンとブラシのための高速かつ低レベルなオブジェクトを生成し、そのオブジェクトが実際に画面に表示されます。

ブラシを変更する場合は、低レベルのオブジェクトを再生成する必要があります。 freezable クラスは、生成された低レベルの対応するオブジェクトを見つけて、変更時にそれらを更新する機能をブラシに付与するものです。 この機能を有効にすると、ブラシは「解凍されます」とされます。

freezable の Freeze メソッドを使用すると、この自己更新機能を無効にすることができます。 このメソッドを使用して、ブラシを "固定" または変更不可にすることができます。

手記

すべての Freezable オブジェクトを固定できるわけではありません。 InvalidOperationException がスローされないようにするには、固定する前に、Freezable オブジェクトの CanFreeze プロパティの値を確認して固定できるかどうかを判断します。

if (myBrush.CanFreeze)
{
    // Makes the brush unmodifiable.
    myBrush.Freeze();
}
If myBrush.CanFreeze Then
    ' Makes the brush unmodifiable.
    myBrush.Freeze()
End If

freezable を変更する必要がなくなった場合は、固定するとパフォーマンスが向上します。 この例でブラシを固定する場合、グラフィックス システムで変更を監視する必要がなくなりました。 また、ブラシが変わらないことを認識しているため、グラフィックス システムは他の最適化を行うこともできます。

手記

便宜上、フリーズ可能なオブジェクトは、明示的に凍結しない限り、そのままの状態です。

Freezables の使用

凍結解除されたフリーザブルの使用は、他の種類のオブジェクトを使用するのと同じです。 次の例では、ボタンの背景を描画するために使用した後、SolidColorBrush の色が黄色から赤に変更されます。 グラフィックス システムはバックグラウンドで動作し、次回画面が更新されるときにボタンを黄色から赤に自動的に変更します。

Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myButton.Background = myBrush;

// Changes the button's background to red.
myBrush.Color = Colors.Red;
Dim myButton As New Button()
Dim myBrush As New SolidColorBrush(Colors.Yellow)
myButton.Background = myBrush


' Changes the button's background to red.
myBrush.Color = Colors.Red

Freezable の固定

Freezable を変更不可能にするには、その Freeze メソッドを呼び出します。 freezable オブジェクトを含むオブジェクトを固定すると、それらのオブジェクトも固定されます。 たとえば、PathGeometryを固定すると、含まれている図形とセグメントも固定されます。

次のいずれかに該当する場合、Freezable を固定できません

  • アニメーションまたはデータ バインドプロパティがあります。

  • 動的リソースによって設定されたプロパティがあります。 (動的リソースの詳細については、XAML リソースの を参照してください)。

  • 固定できない Freezable サブオブジェクトが含まれています。

これらの条件が偽で、Freezableを変更するつもりがない場合は、前に説明したパフォーマンス上の利点を得るためにこれを凍結するべきです。

freezable の Freeze メソッドを呼び出すと、変更できなくなります。 凍結されたオブジェクトを変更しようとすると、InvalidOperationException が投げられます。 次のコードでは、フリーズされた後にブラシを変更しようとするため、例外が発生します。


Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);

if (myBrush.CanFreeze)
{
    // Makes the brush unmodifiable.
    myBrush.Freeze();
}

myButton.Background = myBrush;

try {

    // Throws an InvalidOperationException, because the brush is frozen.
    myBrush.Color = Colors.Red;
}catch(InvalidOperationException ex)
{
    MessageBox.Show("Invalid operation: " + ex.ToString());
}


Dim myButton As New Button()
Dim myBrush As New SolidColorBrush(Colors.Yellow)

If myBrush.CanFreeze Then
    ' Makes the brush unmodifiable.
    myBrush.Freeze()
End If

myButton.Background = myBrush

Try

    ' Throws an InvalidOperationException, because the brush is frozen.
    myBrush.Color = Colors.Red
Catch ex As InvalidOperationException
    MessageBox.Show("Invalid operation: " & ex.ToString())
End Try

例外をスローしないようにするためには、IsFrozen メソッドを使用して、Freezable が固定されているかどうかを確認してください。


Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);

if (myBrush.CanFreeze)
{
    // Makes the brush unmodifiable.
    myBrush.Freeze();
}

myButton.Background = myBrush;

if (myBrush.IsFrozen) // Evaluates to true.
{
    // If the brush is frozen, create a clone and
    // modify the clone.
    SolidColorBrush myBrushClone = myBrush.Clone();
    myBrushClone.Color = Colors.Red;
    myButton.Background = myBrushClone;
}
else
{
    // If the brush is not frozen,
    // it can be modified directly.
    myBrush.Color = Colors.Red;
}


Dim myButton As New Button()
Dim myBrush As New SolidColorBrush(Colors.Yellow)

If myBrush.CanFreeze Then
    ' Makes the brush unmodifiable.
    myBrush.Freeze()
End If

myButton.Background = myBrush


If myBrush.IsFrozen Then ' Evaluates to true.
    ' If the brush is frozen, create a clone and
    ' modify the clone.
    Dim myBrushClone As SolidColorBrush = myBrush.Clone()
    myBrushClone.Color = Colors.Red
    myButton.Background = myBrushClone
Else
    ' If the brush is not frozen,
    ' it can be modified directly.
    myBrush.Color = Colors.Red
End If


前のコード例では、変更可能なコピーは、Clone メソッドを使用して固定オブジェクトから作成されました。 次のセクションでは、複製について詳しく説明します。

手記

フリーズされた freezable はアニメーション化できないため、固定された Freezable オブジェクトを Storyboardでアニメーション化しようとすると、アニメーション システムによって自動的に変更可能な複製が作成されます。 複製によって発生するパフォーマンスのオーバーヘッドを排除するには、オブジェクトをアニメーション化する場合は、オブジェクトをアンフローゼンのままにします。 ストーリーボードを使用したアニメーション化の詳細については、「ストーリーボードの概要」を参照してください。

マークアップからの固定

マークアップで宣言された Freezable オブジェクトを固定するには、PresentationOptions:Freeze 属性を使用します。 次の例では、SolidColorBrush がページ リソースとして宣言され、固定されています。 その後、ボタンの背景を設定するために使用されます。

<Page 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options" 
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="PresentationOptions">

  <Page.Resources>

    <!-- This resource is frozen. -->
    <SolidColorBrush 
      x:Key="MyBrush"
      PresentationOptions:Freeze="True" 
      Color="Red" />
  </Page.Resources>


  <StackPanel>

    <Button Content="A Button" 
      Background="{StaticResource MyBrush}">
    </Button>

  </StackPanel>
</Page>

Freeze 属性を使用するには、プレゼンテーション オプション名前空間 (http://schemas.microsoft.com/winfx/2006/xaml/presentation/options) にマップする必要があります。 PresentationOptions は、この名前空間をマッピングするために推奨されるプレフィックスです。

xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"

すべての XAML リーダーがこの属性を認識するわけではないため、mc:Ignorable Attribute を使用して、PresentationOptions:Freeze 属性を無視可能としてマークすることをお勧めします。

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions"

詳細については、「mc:Ignorable Attribute」ページを参照してください。

Freezable の "固定解除"

いったん凍結すると、Freezable を変更したり、フローゼンを解除したりすることはできません。ただし、Clone または CloneCurrentValue メソッドを使用して、unfrozen クローンを作成できます。

次の例では、ボタンの背景をブラシで設定し、そのブラシを固定します。 固定されていないコピーは、Clone メソッドを使用してブラシから作成されます。 複製が変更され、ボタンの背景を黄色から赤に変更するために使用されます。

Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);

// Freezing a Freezable before it provides
// performance improvements if you don't
// intend on modifying it.
if (myBrush.CanFreeze)
{
    // Makes the brush unmodifiable.
    myBrush.Freeze();
}

myButton.Background = myBrush;

// If you need to modify a frozen brush,
// the Clone method can be used to
// create a modifiable copy.
SolidColorBrush myBrushClone = myBrush.Clone();

// Changing myBrushClone does not change
// the color of myButton, because its
// background is still set by myBrush.
myBrushClone.Color = Colors.Red;

// Replacing myBrush with myBrushClone
// makes the button change to red.
myButton.Background = myBrushClone;
Dim myButton As New Button()
Dim myBrush As New SolidColorBrush(Colors.Yellow)

' Freezing a Freezable before it provides
' performance improvements if you don't
' intend on modifying it. 
If myBrush.CanFreeze Then
    ' Makes the brush unmodifiable.
    myBrush.Freeze()
End If


myButton.Background = myBrush

' If you need to modify a frozen brush,
' the Clone method can be used to
' create a modifiable copy.
Dim myBrushClone As SolidColorBrush = myBrush.Clone()

' Changing myBrushClone does not change
' the color of myButton, because its
' background is still set by myBrush.
myBrushClone.Color = Colors.Red

' Replacing myBrush with myBrushClone
' makes the button change to red.
myButton.Background = myBrushClone

手記

使用する複製方法に関係なく、アニメーションは新しい Freezableにコピーされません。

Clone メソッドと CloneCurrentValue メソッドは、freezable のディープ コピーを生成します。 freezable に他の固定された freezable オブジェクトが含まれている場合は、それらのオブジェクトも複製され、変更可能になります。 たとえば、固定された PathGeometry を複製して変更可能にした場合、その PathGeometry に含まれる図形とセグメントもコピーされ、変更可能になります。

独自の Freezable クラスの作成

Freezable から派生したクラスには、次の機能があります。

  • 特殊な状態: 読み取り専用 (固定) および書き込み可能な状態。

  • スレッド セーフ: 固定された Freezable をスレッド間で共有できます。

  • 詳細な変更通知: 他の DependencyObjectとは異なり、Freezable オブジェクトは、サブプロパティ値が変更されたときに変更通知を提供します。

  • 簡単な複製: Freezable クラスには、ディープ クローンを生成するいくつかのメソッドが既に実装されています。

FreezableDependencyObjectの一種であるため、依存関係プロパティ システムを使用します。 クラスプロパティは依存関係プロパティである必要はありませんが、依存関係プロパティを使用すると、Freezable クラスが依存関係プロパティを念頭に置いて設計されているため、記述する必要があるコードの量が減ります。 依存関係プロパティ システムの詳細については、「依存関係プロパティの概要」を参照してください。

すべての Freezable サブクラスは、CreateInstanceCore メソッドをオーバーライドする必要があります。 クラスがすべてのデータに依存関係プロパティを使用している場合は、完了です。

クラスに依存関係プロパティ以外のデータ メンバーが含まれている場合は、次のメソッドもオーバーライドする必要があります。

また、依存関係プロパティではないデータ メンバーにアクセスしてデータ メンバーに書き込むには、次の規則を確認する必要があります。

  • 依存関係プロパティ以外のデータ メンバーを読み取る API の先頭で、ReadPreamble メソッドを呼び出します。

  • 非依存関係プロパティ のデータ メンバーを書き込む API の先頭で、WritePreamble メソッドを呼び出します。 (API で WritePreamble を呼び出したら、依存関係のないプロパティ データ メンバーも読み取る場合は、ReadPreamble を追加で呼び出す必要はありません)。

  • 依存関係プロパティ以外のデータ メンバーに書き込むメソッドを終了する前に、WritePostscript メソッドを呼び出します。

クラスに、DependencyObject オブジェクトである非依存関係プロパティ データ メンバーが含まれている場合は、メンバーを nullに設定している場合でも、その値の 1 つを変更するたびに、OnFreezablePropertyChanged メソッドも呼び出す必要があります。

手記

オーバーライドする各 Freezable メソッドを基本実装の呼び出しで開始することが非常に重要です。

カスタム Freezable クラスの例については、「カスタム アニメーションのサンプル」を参照してください。

関連項目