Inizializzazione di elementi oggetto non presenti in una struttura ad albero di oggetti

Alcuni aspetti dell'inizializzazione di Windows Presentation Foundation (WPF) vengono posticipati ai processi che in genere si basano su tale elemento connesso all'albero logico o alla struttura ad albero visuale. In questo argomento vengono descritti i passaggi necessari per inizializzare un elemento non connesso ad alcuno di questi alberi.

Elementi e albero logico

Quando si crea un'istanza di una classe Windows Presentation Foundation (WPF) nel codice, è necessario tenere presente che diversi aspetti dell'inizializzazione degli oggetti per una classe Windows Presentation Foundation (WPF) non sono deliberatamente una parte del codice che viene eseguito quando si chiama il costruttore della classe. In particolare, per le classi di controlli, la maggior parte della rappresentazione visiva di un controllo non viene definita dal costruttore, ma dal modello del controllo. Sebbene possa provenire da diverse origini, nella maggior parte dei casi il modello viene ottenuto dagli stili dei temi. I modelli sono in pratica un binding tardivo. Il modello necessario viene associato al controllo in questione solo quando il controllo è pronto per il layout e quest'ultima condizione si verifica solo quando il controllo viene associato a un albero logico che si connette a una superficie di rendering nella radice. È tale elemento a livello radice che avvia il rendering di tutti i relativi elementi figlio definiti nell'albero logico.

Anche la struttura ad albero visuale partecipa a questo processo. Anche un'istanza completa per gli elementi che fanno parte della struttura ad albero visuale tramite i modelli viene creata solo quando gli elementi stessi vengono connessi.

La conseguenza di questo comportamento è la necessità di passaggi aggiuntivi per alcune operazioni basate sulle caratteristiche visive completate di un elemento. Un esempio è rappresentato dal tentativo di ottenere le caratteristiche visive di una classe costruita, ma non ancora associata a un albero. Ad esempio, se si desidera chiamare Render su un RenderTargetBitmap oggetto e l'oggetto visivo che si sta passando è un elemento non connesso a una struttura ad albero, tale elemento non viene completato visivamente fino al completamento di ulteriori passaggi di inizializzazione.

Uso di BeginInit e EndInit per inizializzare l'elemento

Varie classi in WPF implementano l'interfaccia ISupportInitialize . Si usano i BeginInit metodi e EndInit dell'interfaccia per indicare un'area nel codice contenente i passaggi di inizializzazione, ad esempio l'impostazione dei valori delle proprietà che influiscono sul rendering. Dopo EndInit aver chiamato nella sequenza, il sistema di layout può elaborare l'elemento e iniziare a cercare uno stile implicito.

Se l'elemento su cui si impostano le proprietà è una FrameworkElement classe derivata o FrameworkContentElement , è possibile chiamare le versioni della classe di BeginInit e EndInit anziché eseguire il cast a ISupportInitialize.

Codice di esempio

L'esempio seguente è il codice di esempio per un'applicazione console che usa le API di rendering e XamlReader.Load(Stream) di un file XAML libero per illustrare il posizionamento corretto di BeginInit e EndInit intorno ad altre chiamate API che regolano le proprietà che influiscono sul rendering.

L'esempio illustrata solo la funzione principale. Le funzioni Rasterize e Save (non illustrate) sono funzioni di utilità che gestiscono l'elaborazione di immagini e l'I/O.

[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

Vedi anche