En Wpf (4.0) mi listbox (usando VirtualizingStackPanel) contiene 500 elementos. Cada elemento es de un tipo de encargo¿Por qué es tan costoso DrawingContext.DrawText de Wpf?
class Page : FrameworkElement
...
protected override void OnRender(DrawingContext dc)
{
// Drawing 1000 single characters to different positions
//(formattedText is a static member which is only instantiated once and contains the string "A" or "B"...)
for (int i = 0; i < 1000; i++)
dc.DrawText(formattedText, new Point(....))
// Drawing 1000 ellipses: very fast and low ram usage
for (int i = 0; i < 1000; i++)
dc.DrawEllipse(Brushes.Black, null, new Point(....),10,10)
}
Ahora cuando se mueve la barra de desplazamiento del cuadro de lista de ida y vuelta para que la visual de cada elemento se crea al menos una vez el uso de memoria RAM sube a 500 Mb después de un tiempo y luego - después un tiempo - vuelve a ca 250 Mb, pero se mantiene en este nivel. Pérdida de memoria ? Pensé que la ventaja de un VirtualizingStackPanel es que los elementos visuales que no son necesarios/visibles quedan eliminados ...
De todos modos, este uso extremo del ram solo aparece cuando se dibuja texto con "DrawText". Dibujar otros objetos como "DrawEllipse" no consume tanta memoria.
¿Hay una manera más eficiente de dibujar muchos elementos de texto que usar Drawing.Context's "DrawText"?
Aquí está la muestra completa (basta con crear un nuevo proyecto de aplicación de WPF y reemplazar el código window1): (Sé que hay FlowDocument y FixedDocument pero son otra alternativa) Xaml:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="900" Width="800">
<Grid Background="Black">
<ListBox Name="lb" ScrollViewer.CanContentScroll="True" Background="Black">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
</Window>
Y la Window1.xaml.cs:
public partial class Window1 : Window
{
readonly ObservableCollection<FrameworkElement> collection = new ObservableCollection<FrameworkElement>();
public Window1()
{
InitializeComponent();
for (int i = 0; i < 500; i++)
{
collection.Add(new Page(){ Width = 500, Height = 800 });
}
lb.ItemsSource = collection;
}
}
public class Page : FrameworkElement
{
static FormattedText formattedText = new FormattedText("A", CultureInfo.GetCultureInfo("en-us"),
FlowDirection.LeftToRight,
new Typeface(new FontFamily("Arial").ToString()),
12,Brushes.Black);
protected override void OnRender(DrawingContext dc)
{
dc.DrawRectangle(Brushes.White, null, new Rect(0, 0, Width, Height));
double yOff = 0;
for (int i = 0; i < 1000; i++) // draw 1000 "A"s
{
dc.DrawText(formattedText, new Point((i % 80) * 5, yOff));
if (i % 80 == 0) yOff += 10;
}
}
}
Puede probar StreamGeometry. Que es relativamente liviano http://msdn.microsoft.com/en-us/library/ms742199.aspx Por otro lado. Debo decir. DrawText es algo relativamente menos pesado. No sé por qué está tomando tanto recurso. ¿Tiene alguna muestra para el escenario anterior? –
DrawingContext.DrawGlyph parece ser mucho más rápido que DrawText. – fritz