Freezable オブジェクトの概要

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

Freezable とは

Freezable は、非固定と固定という 2 つの状態を持つ特殊な型のオブジェクトです。 非固定の場合、Freezable は他のオブジェクトと同じように動作します。 固定されると、Freezable を変更できなくなります。

Freezable には、オブジェクトに対する変更をオブザーバーに通知する Changed イベントが用意されています。 Freezable を固定すると、変更通知にリソースを費やす必要がなくなるため、パフォーマンスが向上します。 固定された Freezable は複数のスレッドで共有できますが、非固定の Freezable はできません。

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

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

たとえば、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 を変更する必要がなくなった場合は、固定するとパフォーマンスが向上します。 この例のブラシを固定した場合、グラフィックス システムでブラシの変更を監視する必要がなくなります。 また、ブラシが変更されないことがわかっているため、グラフィックス システムで他の最適化を行うこともできます。

注意

便宜上、明示的に固定しない限り、freezable オブジェクトは固定されないままです。

Freezable の使用

固定されていない freezable の使用は、他の種類のオブジェクトの使用と同様です。 次の例では、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 サブオブジェクトが含まれています。

これらの条件が false であり、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 オブジェクトはアニメーション化できないため、Storyboard でアニメーション化しようとすると、アニメーション システムによって、固定された Freezable オブジェクトの変更可能なクローンが自動的に作成されます。 複製によるパフォーマンスのオーバーヘッドをなくすには、アニメーション化する場合にオブジェクトを固定しないでください。 アニメーション化とストーリーボードの詳細については、「ストーリーボードの概要」を参照してください。

マークアップからの固定

マークアップで宣言された 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 属性を使用して、PresentationOptions:Freeze 属性を無視できるものとしてマークすることをお勧めします。

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

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

Freezable の "固定解除"

固定された後に、Freezable を変更または固定解除することはできなくなります。ただし、Clone または CloneCurrentValue メソッドを使用して、固定されていない複製を作成できます。

次の例では、ボタンの背景にブラシを設定し、そのブラシを固定しています。 固定されていないコピーは、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 を複製して変更可能にすると、そこに含まれる図形とセグメントもコピーされ、変更可能になります。

独自の Freezable クラスの作成

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

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

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

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

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

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

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

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

また、依存関係プロパティではないデータ メンバーへのアクセスと書き込みについては、次の規則に従う必要があります。

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

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

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

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

注意

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

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

関連項目