Initiering för objektelement som inte finns i ett objektträd
Vissa aspekter av initieringen av Windows Presentation Foundation (WPF) skjuts upp till processer som vanligtvis förlitar sig på att elementet är anslutet till antingen det logiska trädet eller det visuella trädet. Det här avsnittet beskriver de steg som kan vara nödvändiga för att initiera ett element som inte är anslutet till något av träden.
Element och det logiska trädet
När du skapar en instans av en WPF-klass (Windows Presentation Foundation) i kod bör du vara medveten om att flera aspekter av objektinitiering för en WPF-klass (Windows Presentation Foundation) avsiktligt inte är en del av koden som körs när du anropar klasskonstruktorn. Särskilt för en kontrollklass definieras de flesta visuella representationerna av den kontrollen inte av konstruktorn. I stället definieras den visuella representationen av kontrollens mall. Mallen kommer eventuellt från en mängd olika källor, men oftast hämtas mallen från temaformat. Mallar är i praktiken sen bindning; den nödvändiga mallen är inte kopplad till den aktuella kontrollen förrän kontrollen är klar för layout. Och kontrollen är inte redo för layout förrän den är ansluten till ett logiskt träd som ansluter till en renderingsyta i roten. Det är elementet på rotnivå som initierar återgivningen av alla underordnade element enligt definitionen i det logiska trädet.
Det visuella trädet deltar också i den här processen. Element som ingår i det visuella trädet via mallarna instansieras inte heller helt förrän de är anslutna.
Konsekvenserna av det här beteendet är att vissa åtgärder som förlitar sig på slutförda visuella egenskaper för ett element kräver ytterligare steg. Ett exempel är om du försöker hämta de visuella egenskaperna för en klass som har konstruerats men ännu inte är kopplad till ett träd. Om du till exempel vill anropa Render på en RenderTargetBitmap och det visuella objekt som du skickar är ett element som inte är anslutet till ett träd, är det elementet inte visuellt slutfört förrän ytterligare initieringssteg har slutförts.
Använda BeginInit och EndInit för att initiera elementet
Olika klasser i WPF implementerar ISupportInitialize-gränssnittet. Du använder metoderna BeginInit och EndInit i gränssnittet för att ange en region i koden som innehåller initieringssteg (till exempel att ange egenskapsvärden som påverkar renderingen). När EndInit anropas i sekvensen kan layoutsystemet bearbeta elementet och börja leta efter ett implicit format.
Om det element som du anger egenskaper för är en FrameworkElement- eller FrameworkContentElement-härledd klass, kan du anropa klassversionerna av BeginInit och EndInit istället för att omvandla till ISupportInitialize.
Exempelkod
Följande exempel är exempelkod för ett konsolprogram som använder renderings-API:er och XamlReader.Load(Stream) av en lös XAML-fil för att illustrera korrekt placering av BeginInit och EndInit runt andra API-anrop som justerar egenskaper som påverkar rendering.
Exemplet illustrerar endast huvudfunktionen. Funktionerna Rasterize
och Save
(visas inte) är verktygsfunktioner som tar hand om bildbearbetning och 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
Se även
- Träd i WPF
- översikt över WPF-grafikrenderingen
- XAML i WPF
.NET Desktop feedback