2009-10-31 17 views

Respuesta

7

Puede detectar cambios en el FlowDocument creando un rango de texto y supervisándolo para ver los cambios. Desplazarse hacia abajo es más difícil porque tiene que encontrar el ScrollViewer. También para el rendimiento no desea volver a hacer todos los cálculos de desplazamiento en cada cambio, por lo que debe utilizar DispatcherOperations.

Poniendo todo junto, este código debe hacer el truco:

var range = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd); 
object operation = null; 

range.Changed += (obj, e) => 
{ 
    if(operation==null) 
    operation = Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() => 
    { 
     operation = null; 

     var scrollViewer = FindFirstVisualDescendantOfType<ScrollViewer>(flowDocument); 
     scrollViewer.ScrollToBottom(); 
    }); 
}; 

donde FindFirstVisualDescendantOfType es una simple búsqueda de prefijo primero en profundidad del árbol visual usando VisualTreeHelper.GetChildrenCount() y VisualTreeHelper.GetChild() y devolver la primera Visual encontrado de la especificada tipo.

Tenga en cuenta que, para una generalidad completa, no precompute scrollViewer en la parte superior del código porque la plantilla FlowDocumentScrollViewer puede cambiar. Si esto no sucederá, este código puede acelerarse llamando .ApplyTemplate() en el FlowDocumentScrollViewer y luego calcular scrollViewer antes de que el controlador de eventos se ha registrado:

var range = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd); 
object operation = null; 

flowDocument.ApplyTemplate(); 
var scrollViewer = FindFirstVisualDescendantOfType<ScrollViewer>(flowDocument); 

range.Changed += (obj, e) => 
{ 
    if(operation==null) 
    operation = Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() => 
    { 
     operation = null; 
     scrollViewer.ScrollToBottom(); 
    }); 
}; 

Tenga en cuenta que no podemos llamar simplemente scrollViewer.GetTemplateChild("PART_ContentHost") y pasa el árbol de búsqueda visual porque GetTemplateChild está protegido.

+0

No necesitaba nada de esto con FlowDocument y el código de árbol visual FlowDocumentScrollViewer. Solo 2 Invocaciones, 1 para crear y agregar el párrafo de una cadena y 1 para ver ese párrafo. –

2

Después de conectar a un evento TextChanged, sólo tiene que utilizar:

// Showing Last Block 
YourReader.Document.Blocks.LastBlock.BringIntoView(); 

// Or.. showing the last Inline 
(YourReader.Document.Blocks.LastBlock as Paragraph).Inlines.LastInline.BringIntoView(); 

Pero, esto funciona única en un FlowDocumentPageViewer, y también en un FlowDocumentReader (con páginas ViewingModes), para un FlowDocumentScrollViewer debe usar el árbol visual como se menciona

+0

Estoy utilizando solo un FlowDocument y FlowDocumentscrollViewer.La solución BringIntoView anterior funciona perfectamente para mí. No necesitaba VisualTreeHelper Mi solución es usar Dispatcher.Invoke para escribir crear el párrafo, almacenar ese párrafo como una variable y luego Dispatcher.Invocar nuevamente para ponerlo a la vista. Este parece ser el método más simple. –

0

Usted puede utilizar el siguiente método de extensión para llegar al espectador de desplazamiento interno:

public static class FlowDocumentScrollViewerExtensions 
{ 
    public static ScrollViewer GetScrollViewer(this FlowDocumentScrollViewer element) { 
    if (element == null) { 
     throw new ArgumentNullException(nameof(element)); 
    } 

    return element.Template?.FindName("PART_ContentHost", element) as ScrollViewer; 
    } 
} 

Además, puede utilizar estos métodos de extensión antes de añadir contenido a comprobar la posición de desplazamiento del ScrollViewer sí mismo (en caso de que desee desplazarse - solamente- si el espectador de desplazamiento ya estaba al final, por ejemplo):

public static class ScrollViewerExtensions 
{ 
    public static bool IsAtHome(this ScrollViewer element) { 
    if (element == null) { 
     throw new ArgumentNullException(nameof(element)); 
    } 

    return element.VerticalOffset <= 0; 
    } 

    public static bool IsAtEnd(this ScrollViewer element) { 
    if (element == null) { 
     throw new ArgumentNullException(nameof(element)); 
    } 

    return element.VerticalOffset >= element.ScrollableHeight; 
    } 
} 

Posteriormente a llamarlo scrollViewer.ScrollToEnd() por ejemplo.

Cuestiones relacionadas