Compartir a través de


Inicialización de elementos de objeto no en un árbol de objetos

Algunos aspectos de la inicialización de Windows Presentation Foundation (WPF) se postergan para los procesos que normalmente se basan en ese elemento estando conectado al árbol lógico o al árbol visual. En este tema se describen los pasos que pueden ser necesarios para inicializar un elemento que no está conectado a ninguno de los árboles.

Elementos y el árbol lógico

Al crear una instancia de una clase de Windows Presentation Foundation (WPF) en el código, debe tener en cuenta que varios aspectos de la inicialización de objetos para una clase de Windows Presentation Foundation (WPF) no forman deliberadamente parte del código que se ejecuta al llamar al constructor de clase. Especialmente para una clase de control, la mayoría de la representación visual de ese control no está definida por el constructor. En su lugar, la representación visual se define mediante la plantilla del control. La plantilla puede provenír de una variedad de orígenes, pero la mayoría de las veces se obtiene de estilos de tema. Las plantillas son efectivamente de enlace tardío; la plantilla necesaria no está asociada al control en cuestión hasta que el control esté listo para la presentación. Y el control no está listo para el diseño hasta que se adjunta a un árbol lógico que se conecta a una superficie de representación en la raíz. Es ese elemento de nivel raíz que inicia la representación de todos sus elementos secundarios tal y como se define en el árbol lógico.

El árbol visual también participa en este proceso. Los elementos que forman parte del árbol visual por medio de las plantillas tampoco están completamente instanciados hasta que se conectan.

Las consecuencias de este comportamiento son que ciertas operaciones que dependen de las características visuales completadas de un elemento requieren pasos adicionales. Un ejemplo es si está intentando obtener las características visuales de una clase que se construyó pero que aún no está anexada a un árbol. Por ejemplo, si quiere llamar a Render en un RenderTargetBitmap y el visual que está pasando es un elemento que no está conectado a un árbol, ese elemento no está visualmente completo hasta que se completen los pasos de inicialización adicionales.

Usar BeginInit y EndInit para inicializar el elemento

Varias clases de WPF implementan la ISupportInitialize interfaz . Usas los métodos BeginInit y EndInit de la interfaz para indicar una región en tu código que contiene pasos de inicialización (como configurar valores de propiedad que afectan la representación). Después de que se llame a EndInit en la secuencia, el sistema de diseño puede procesar el elemento y comenzar a buscar un estilo implícito.

Si el elemento en el que está estableciendo propiedades es una FrameworkElement o clase derivada de FrameworkContentElement, puede llamar a las versiones de clase de BeginInit y EndInit en lugar de convertir en ISupportInitialize.

Código de ejemplo

El ejemplo siguiente es código de ejemplo para una aplicación de consola que usa las API de representación y el uso de XamlReader.Load(Stream) de un archivo XAML flexible para ilustrar la colocación adecuada de BeginInit y EndInit entre otras llamadas a las API que ajustan las propiedades que afectan a la representación.

En el ejemplo se muestra solo la función principal. Las funciones Rasterize y Save (no se muestran) son funciones de utilidad que se encargan del procesamiento de imágenes y la E/S.

[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");
}
<STAThread>
Shared Sub Main(ByVal args() As String)
    Dim e As UIElement
    Dim _file As String = Directory.GetCurrentDirectory() & "\starting.xaml"
    Using stream As Stream = File.Open(_file, FileMode.Open)
        ' loading files from current directory, project settings take care of copying the file
        Dim pc As New ParserContext()
        pc.BaseUri = New Uri(_file, UriKind.Absolute)
        e = CType(XamlReader.Load(stream, pc), UIElement)
    End Using

    Dim paperSize As 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
    '             
    Dim image1 As RenderTargetBitmap = Rasterize(e, paperSize.Width, paperSize.Height, 96, 96)
    Save(image1, "render1.png")

    Dim b As New Button()
    b.BeginInit()
    b.Background = Brushes.Blue
    b.Height = 200
    b.Width = b.Height
    b.EndInit()
    b.Measure(paperSize)
    b.Arrange(New Rect(paperSize))
    b.UpdateLayout()

    ' now render the altered version, with the element built up and initialized

    Dim image2 As RenderTargetBitmap = Rasterize(b, paperSize.Width, paperSize.Height, 96, 96)
    Save(image2, "render2.png")
End Sub

Consulte también