Поделиться через


Обзор замораживаемых объектов

В этом разделе описывается, как эффективно использовать и создавать Freezable объекты, предоставляющие специальные функции, которые могут помочь повысить производительность приложения. Примерами замораживаемых объектов являются кисти, ручки, преобразования, геометрии и анимации.

Что такое замораживаемое?

A Freezable — это особый тип объекта, который имеет два состояния: не заморожено и заморожено. При разморозке Freezable, кажется, ведет себя как любой другой объект. После замораживания Freezable больше нельзя изменить.

Freezable Предоставляет Changed событие для уведомления наблюдателей о любых изменениях объекта. Freezable Замораживание может повысить производительность, так как больше не требуется тратить ресурсы на уведомления об изменениях. Замороженный Freezable также может быть разделён между потоками, в то время как незамороженный Freezable не может быть.

Freezable Хотя класс имеет множество приложений, большинство Freezable объектов в Windows Presentation Foundation (WPF) связаны с подсистемой графики.

Класс Freezable упрощает использование определенных графических системных объектов и помогает повысить производительность приложения. Примеры типов, которые наследуются от Freezable, включают классы Brush, Transform и 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, проверьте значение свойства CanFreeze объекта Freezable, чтобы определить, возможно ли его заморозить, прежде чем предпринимать такую попытку.

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

Если вам больше не нужно изменять замораживаемый объект, его заморозка обеспечивает преимущества в производительности. Если бы вы заморозили кисть в этом примере, графическая система больше не должна отслеживать её для изменений. Графические системы также могут выполнять другие оптимизации, так как она знает, что кисть не изменится.

Замечание

Для удобства замораживаемые объекты остаются размороженными, если их явно не заморозить.

Использование замораживаемых объектов

Использование размороженного объекта типа 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 неизменяемым, вызовите его Freeze метод. При замораживании объекта, содержащего замораживаемые объекты, эти объекты также замораживаются. Например, если заморозить PathGeometry, цифры и сегменты, которые он содержит, тоже будут заморожены.

A 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 объектов, когда вы попытаетесь анимировать их с помощью 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 , чтобы пометить PresentationOptions:Freeze атрибут как игнорируемый:

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

Дополнительные сведения см. на странице mc:Ignorable Attribute.

"Unfreezing" 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 объекты, они также клонируются и делаются модифицируемыми. Например, если клонировать замороженный PathGeometry , чтобы сделать его изменяемым, цифры и сегменты, содержащиеся в нем, также копируются и изменяются.

Создание собственного класса Freezable

Класс, производный от Freezable, приобретает следующие функции.

  • Специальные состояния: доступная только для чтения (замороженная) и записываемое состояние.

  • Безопасность потоков: замороженный Freezable может использоваться несколькими потоками.

  • Подробное уведомление об изменении: объекты Freezable, в отличие от других DependencyObject, предоставляют уведомления об изменениях при изменении значений вложенных свойств.

  • Простое клонирование: класс Freezable уже реализовал несколько методов для создания глубоких клонов.

A Freezable — это тип DependencyObjectи, следовательно, использует систему свойств зависимостей. Свойства класса не должны быть свойствами зависимостей, но использование свойств зависимостей уменьшит объем кода, который необходимо написать, так как Freezable класс был разработан с учетом свойств зависимостей. Дополнительные сведения о системе свойств зависимостей см. в обзоре свойств зависимостей.

Каждый подкласс Freezable должен переопределить метод CreateInstanceCore. Если класс использует свойства зависимостей для всех своих данных, завершите работу.

Если класс содержит элементы данных свойств, отличных от зависимостей, необходимо также переопределить следующие методы:

Кроме того, необходимо соблюдать следующие правила для доступа и записи к элементам данных, которые не являются свойствами зависимостей:

  • При запуске любого API, который считывает независимые элементы данных свойств, вызовите метод ReadPreamble.

  • В начале любого API, который записывает элементы данных свойств, не зависящих от зависимостей, вызовите WritePreamble метод. (После вызова WritePreamble в API вам не нужно выполнять дополнительный вызов ReadPreamble , если вы также читаете элементы данных свойств, не зависящих от зависимостей.)

  • Вызовите метод WritePostscript перед выходом из методов, которые записывают данные в члены данных свойств, не являющихся зависимостями.

Если класс содержит элементы данных, являющихся DependencyObject объектами, не зависящие от зависимостей, необходимо также вызывать OnFreezablePropertyChanged метод при каждом изменении одного из их значений, даже если для элемента задано значение null.

Замечание

Очень важно, чтобы вы начинали каждый Freezable метод, который переопределяете, вызовом базовой реализации.

Для примера пользовательского Freezable класса, см. в примере пользовательской анимации.

См. также