Inicjalizacja elementów obiektu poza drzewem obiektu

Niektóre aspekty inicjowania programu Windows Presentation Foundation (WPF) są odroczone do procesów, które zwykle polegają na połączeniu tego elementu z drzewem logicznym lub drzewem wizualnym. W tym temacie opisano kroki, które mogą być konieczne w celu zainicjowania elementu, który nie jest połączony z każdym drzewem.

Elementy i drzewo logiczne

Podczas tworzenia wystąpienia klasy Windows Presentation Foundation (WPF) w kodzie należy pamiętać, że kilka aspektów inicjowania obiektu dla klasy Windows Presentation Foundation (WPF) celowo nie jest częścią kodu wykonywanego podczas wywoływania konstruktora klasy. Szczególnie w przypadku klasy kontrolnej większość wizualnej reprezentacji tej kontrolki nie jest definiowana przez konstruktora. Zamiast tego reprezentacja wizualizacji jest definiowana przez szablon kontrolki. Szablon potencjalnie pochodzi z różnych źródeł, ale najczęściej szablon jest uzyskiwany ze stylów motywu. Szablony są skutecznie opóźnione; wymagany szablon nie jest dołączony do danej kontrolki, dopóki kontrolka nie będzie gotowa do układu. Kontrolka nie jest gotowa do układu, dopóki nie zostanie dołączona do drzewa logicznego łączącego się z powierzchnią renderowania w katalogu głównym. Jest to element poziomu głównego, który inicjuje renderowanie wszystkich jego elementów podrzędnych zgodnie z definicją w drzewie logicznym.

Drzewo wizualne uczestniczy również w tym procesie. Elementy, które są częścią drzewa wizualnego za pośrednictwem szablonów, nie są również w pełni tworzone do momentu połączenia.

Konsekwencje tego zachowania są takie, że niektóre operacje, które opierają się na ukończonych cechach wizualnych elementu, wymagają dodatkowych kroków. Przykładem jest próba uzyskania cech wizualnych klasy, która została skonstruowana, ale nie jest jeszcze dołączona do drzewa. Jeśli na przykład chcesz wywołać RenderRenderTargetBitmap element , a przekazywana wizualizacja jest elementem, który nie jest połączony z drzewem, ten element nie zostanie ukończony wizualnie do momentu ukończenia dodatkowych kroków inicjowania.

Inicjowanie elementu przy użyciu elementów BeginInit i EndInit

Różne klasy w WPF implementują ISupportInitialize interfejs. Metody i EndInit interfejsu służą BeginInit do oznaczania regionu w kodzie zawierającego kroki inicjowania (takie jak ustawianie wartości właściwości, które mają wpływ na renderowanie). Po EndInit wywołaniu metody w sekwencji system układu może przetworzyć element i rozpocząć wyszukiwanie niejawnego stylu.

Jeśli element, na którym ustawiasz właściwości, jest klasą lub pochodnąFrameworkElement, możesz wywołać wersje klas i BeginInitEndInit zamiast rzutować do ISupportInitializeklasy .FrameworkContentElement

Przykładowy kod

W poniższym przykładzie przedstawiono przykładowy kod aplikacji konsolowej, który używa interfejsów API renderowania i XamlReader.Load(Stream) luźnego pliku XAML w celu zilustrowania prawidłowego umieszczania interfejsu BeginInit API i EndInit wokół innych wywołań interfejsu API, które dostosowują właściwości wpływające na renderowanie.

W przykładzie pokazano tylko funkcję główną. Funkcje i Save (nie są wyświetlane) to funkcje Rasterize narzędzi, które zajmują się przetwarzaniem obrazów i operacjami we/wy.

[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

Zobacz też