Udostępnij za pomocą


Inicjowanie elementów obiektów, które nie są w drzewie obiektów

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 żadnym z drzew.

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ą efektywnie późnym wiązaniem; wymagany szablon nie jest przypisany do kontrolki, dopóki kontrolka nie będzie gotowa do rozmieszczenia. Kontrolka nie jest gotowa do rozmieszczenia, dopóki nie zostanie dołączona do drzewa logicznego łączącego się z powierzchnią renderowania w korzeniu. 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 jeszcze nie jest dołączona do drzewa. Jeśli na przykład chcesz wywołać Render na RenderTargetBitmap, 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ą interfejs ISupportInitialize. Metody BeginInit i EndInit interfejsu służą 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 wywołaniu EndInit w sekwencji system układu może przetworzyć element i rozpocząć wyszukiwanie ukrytego stylu.

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

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 BeginInit 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 Rasterize i Save (nie są pokazane) to funkcje narzędziowe, 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 także