Инициализация для элементов типа Object вне дерева объектов
За некоторые аспекты инициализации Windows Presentation Foundation (WPF) отвечают процессы, которые обычно предполагают, что элемент подключен либо к логическому, либо к визуальному дереву. В этом разделе описываются действия, необходимые для инициализации элемента, который не подключен ни к одному из деревьев.
Элементы и логическое дерево
При создании экземпляра класса Windows Presentation Foundation (WPF) в коде следует иметь в виду, что несколько аспектов инициализации объекта для класса Windows Presentation Foundation (WPF) преднамеренно не являются частью кода, выполняющегося при вызове конструктора класса. Большая часть визуального представления элемента управления, особенно для класса элемента управления, не определяется конструктором. Вместо этого визуальное представление определяется шаблоном элемента управления. Шаблон может быть получен из различных источников, но наиболее часто он берется из стилей темы. Шаблоны фактически выполняют позднее связывание. Необходимый шаблон не присоединяется к элементу управления до тех пор, пока элемент управления не будет подготовлен для макета. А элемент управления не готов для макета до тех пор, пока он не будет присоединен к логическому дереву, которое подключается к отрисовываемой поверхности в корневом элементе. Именно элемент корневого уровня инициирует отрисовку всех дочерних элементов, как определено в логическом дереве.
Визуальное дерево также участвует в этом процессе. Элементы, которые являются частью визуального дерева посредством шаблонов, также полностью не создаются до тех пор, пока не произойдет подключение.
Последствием этого является то, что для определенных операций, зависящих от завершенных визуальных характеристик элемента, требуется выполнение дополнительных действий. Примером является попытка получить визуальные характеристики класса, который был создан, но еще не присоединен к дереву. Например если вы хотите вызвать Render на RenderTargetBitmap и визуальный элемент не подключен к дереву, этот элемент не визуально завершена, пока не будут выполнены дополнительные действия по инициализации.
Использование BeginInit и EndInit для инициализации элемента
Различные классы в WPF реализовать ISupportInitialize интерфейс. Использовании BeginInit и EndInit методы интерфейса для обозначения области кода, содержащей действия для инициализации (такие как установка значений свойств, влияющих на отрисовку). После EndInit вызывается в последовательности система макета может обработать элемент и начать поиск неявного стиля.
Если элемент при установке свойств на FrameworkElement или FrameworkContentElement производного класса, то можно вызвать версии класса BeginInit и EndInit вместо приведения к ISupportInitialize.
Пример кода
Ниже приведен пример кода для консольного приложения, использующего отрисовки API и XamlReader.Load(Stream) свободного XAML файл для иллюстрации правильного размещения BeginInit и EndInit относительно других API вызовы корректировать, влияющих на отрисовку.
В этом примере иллюстрируется только основная функция. Функции Rasterize
и Save
(не показаны) являются служебными функциями, которые отвечают за обработку изображения и ввод-вывод.
[STAThread]
static void Main(string[] args)
{
UIElement e;
string file = Directory.GetCurrentDirectory() + "\\starting.xaml";
using (Stream stream = File.Open(file, FileMode.Open))
{
// loading files from current directory, project settings take care of copying the file
ParserContext pc = new ParserContext();
pc.BaseUri = new Uri(file, UriKind.Absolute);
e = (UIElement)XamlReader.Load(stream, pc);
}
Size paperSize = new Size(8.5 * 96, 11 * 96);
e.Measure(paperSize);
e.Arrange(new Rect(paperSize));
e.UpdateLayout();
/*
* Render effect at normal dpi, indicator is the original RED rectangle
*/
RenderTargetBitmap image1 = Rasterize(e, paperSize.Width, paperSize.Height, 96, 96);
Save(image1, "render1.png");
Button b = new Button();
b.BeginInit();
b.Background = Brushes.Blue;
b.Width = b.Height = 200;
b.EndInit();
b.Measure(paperSize);
b.Arrange(new Rect(paperSize));
b.UpdateLayout();
// now render the altered version, with the element built up and initialized
RenderTargetBitmap image2 = Rasterize(b, paperSize.Width, paperSize.Height, 96, 96);
Save(image2, "render2.png");
}