Pregunta Posible InvalidateVisual () en una región determinada en lugar de todo el control de WPF?


Tengo un control WPF complejo que dibuja muchas primitivas en su OnRender (es como un mapa). Cuando una pequeña parte cambia, solo me gustaría volver a emitir comandos de renderizado para los elementos afectados, en lugar de ejecutar todo el OnRender. Si bien estoy bien con el rendimiento de mi función OnRender en un cambio de tamaño o lo que sea, no es lo suficientemente rápido para el resaltado basado en el mouse de primitivas.

Actualmente, la única forma en que sé cómo forzar una actualización de pantalla es llamar a InvalidateVisual (). No hay forma de enviar una región rect sucia para invalidar.

¿Es la granularidad más baja de la composición de pantalla de WPF el elemento de UI? ¿Tendré que hacer mis renderizaciones de primitivas en un objetivo intermedio y luego hacer que use InvalidateVisual () para actualizar a la pantalla?


7
2018-04-05 02:03


origen


Respuestas:


Cuando desee escribir controles personalizados / compuestos de WPF, debe intentar evitar anular OnRender tanto como sea posible, especialmente si planea invalidar partes del mismo. Es mucho más fácil utilizar AddVisualChild + anular VisualChildrenCount + anular GetVisualChild + anular Medir y organizar como este (pseudo código con 2 hijos):

private void BuildMyControls()
{
  AddVisualChild(subControl1);
  AddVisualChild(subControl2);
}

protected override int VisualChildrenCount
{
  get
  {
    return 2;
  }
}

protected override Visual GetVisualChild(int index)
{
  if (index == 0) return subControl1;
  if (index == 1) return subControl2;
  return null; // should never be called in fact...
}

protected override Size MeasureCore(Size availableSize)
{
  base.Measure...
  BuildMyControls();
  .. measure them, probably call subControlX.Measure(...);
}

protected override void ArrangeCore(Rect finalRect)
{
  base.ArrangeCore(finalRect);
  ... arrange them, probably call subControlX.Arrange
}

Con este tipo de código, puede invalidar solo una parte, con algo como subControlX.InvalidateXXX ();


3
2017-12-20 17:52



WPF no funciona así, por lo que no puede invalidar las regiones. Sin embargo, hay algunas optimizaciones que se pueden hacer. Hay una Medida, Organizar y luego Renderizar. Si un control se mueve pero lo que realmente representa no cambia, entonces puede decirle a WPF que solo haga el pase de organización. Puede desencadenar estas invalidaciones de los cambios en el valor de la propiedad de dependencia con FrameworkPropertyMetadata y FrameworkPropertyMetadataOptions (http://msdn.microsoft.com/en-us/library/system.windows.frameworkpropertymetadataoptions.aspx)


0
2017-07-12 04:37



No deberías estar usando InvalidateVisual() a menos que cambie el tamaño de su control, ya que causa un re-diseño bastante caro de su UI.

WPF es un retenido sistema de dibujo. Eso significa OnRender() podría ser mejor llamado AccumulateDrawingObjects(). En realidad, está acumulando un árbol de objetos de dibujo en vivo, que solo tiene que suceder una vez por diseño. A continuación, usa estos objetos para dibujar su IU siempre que lo necesite. Para cambiar la apariencia de una parte de su IU sin volver a diseñar, algunos objetos (como DrawingGroup, RenderTargetBitmap y WriteableBitmap) se pueden actualizar después de OnRender(), cuando quieras.

Para actualizar una parte de su UI más tarde, ajuste esos comandos en un DrawingGroup y pon ese objeto en el DrawingContext. Entonces tú puedes Open() y actualícelo cuando lo desee, y WPF volverá a pintar automáticamente esa parte de la IU.

Esto es lo que parece:

DrawingGroup backingStore = new DrawingGroup();

protected override void OnRender(DrawingContext drawingContext) {      
    base.OnRender(drawingContext);            

    Render(); // put content into our backingStore
    drawingContext.DrawDrawing(backingStore);
}

// I can call this anytime, and it'll update my visual drawing
// without ever triggering layout or OnRender()
private void Render() {            
    var drawingContext = backingStore.Open();
    Render(drawingContext);
    drawingContext.Close();            
}

0
2018-06-10 07:09