2010-01-14 16 views
23

Problema:
Si mi DataGrid no es totalmente visible (horizontales & barras de desplazamiento verticales están mostrando) y haga clic en una de mis células que es parcialmente visible, la rejilla se desplaza automáticamente para que ese celular a la vista. No quiero que esto suceda. He intentado jugar con RequestBringIntoView, así:WPF DataGrid: ¿cómo puedo detener el desplazamiento automático cuando se hace clic en una celda?

private void DataGrid_RequestBringIntoView(object sender, RequestBringIntoViewEventArgs e) 
{ 
    e.Handled = true; 
} 

Pero eso no hace nada.

cosas que he intentado:

  • Mis células son por encargo UserControls; Traté de poner un controlador de eventos para RequestBringIntoView en todos los UserControls que componen mis celdas, y traté de manejar el evento, pensando que tal vez no estaba haciendo lo suficiente solo con el manejo de RequestBringIntoView en el DataGrid. Esto no funcionó.
  • Alojado el DataGrid dentro de un ScrollViewer, y manejado el ScrollViewer 's RequestBringIntoView evento. Esto realmente funciona, y detiene el comportamiento del desplazamiento automático, pero en mi caso no es nada recomendable tener un DataGrid dentro de un ScrollViewer, así que tengo que encontrar una solución diferente.

No estoy seguro de cómo detener este comportamiento, alguna idea?

Respuesta

7

Puede acceder al ScrollViewer interno de DataGrid modificando la plantilla. Aunque normalmente no colocaría un controlador de eventos en el código en una plantilla, si declara la plantilla en línea, puede tratar el controlador de eventos de la misma forma que lo está cuando lo adjunta al DataGrid mismo. Esta es la plantilla por defecto como se genera a partir de mezcla que incluye un controlador adicional en el ScrollViewer para el evento RequestBringIntoView:

<ControlTemplate TargetType="{x:Type Controls:DataGrid}"> 
<Border SnapsToDevicePixels="True" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}"> 
    <ScrollViewer x:Name="DG_ScrollViewer" Focusable="False" RequestBringIntoView="DG_ScrollViewer_RequestBringIntoView"> 
     <ScrollViewer.Template> 
      <ControlTemplate TargetType="{x:Type ScrollViewer}"> 
       <Grid> 
        <Grid.RowDefinitions> 
         <RowDefinition Height="Auto"/> 
         <RowDefinition Height="*"/> 
         <RowDefinition Height="Auto"/> 
        </Grid.RowDefinitions> 
        <Grid.ColumnDefinitions> 
         <ColumnDefinition Width="Auto"/> 
         <ColumnDefinition Width="*"/> 
         <ColumnDefinition Width="Auto"/> 
        </Grid.ColumnDefinitions> 
        <Button Width="{Binding CellsPanelHorizontalOffset, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Controls:DataGrid}}}" Focusable="False"> 
         <Button.Visibility> 
          <Binding Path="HeadersVisibility" RelativeSource="{RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Controls:DataGrid}}"> 
           <Binding.ConverterParameter> 
            <Controls:DataGridHeadersVisibility>All</Controls:DataGridHeadersVisibility> 
           </Binding.ConverterParameter> 
          </Binding> 
         </Button.Visibility> 
         <Button.Template> 
          <ControlTemplate TargetType="{x:Type Button}"> 
           <Grid> 
            <Rectangle x:Name="Border" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" SnapsToDevicePixels="True"/> 
            <Polygon x:Name="Arrow" Fill="Black" Stretch="Uniform" HorizontalAlignment="Right" Margin="8,8,3,3" VerticalAlignment="Bottom" Opacity="0.15" Points="0,10 10,10 10,0"/> 
           </Grid> 
           <ControlTemplate.Triggers> 
            <Trigger Property="IsMouseOver" Value="True"> 
             <Setter Property="Stroke" TargetName="Border" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"/> 
            </Trigger> 
            <Trigger Property="IsPressed" Value="True"> 
             <Setter Property="Fill" TargetName="Border" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"/> 
            </Trigger> 
            <Trigger Property="IsEnabled" Value="False"> 
             <Setter Property="Visibility" TargetName="Arrow" Value="Collapsed"/> 
            </Trigger> 
           </ControlTemplate.Triggers> 
          </ControlTemplate> 
         </Button.Template> 
         <Button.Command> 
          <RoutedCommand/> 
         </Button.Command> 
        </Button> 
        <Custom:DataGridColumnHeadersPresenter x:Name="PART_ColumnHeadersPresenter" Grid.Column="1"> 
         <Custom:DataGridColumnHeadersPresenter.Visibility> 
          <Binding Path="HeadersVisibility" RelativeSource="{RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Controls:DataGrid}}"> 
           <Binding.ConverterParameter> 
            <Controls:DataGridHeadersVisibility>Column</Controls:DataGridHeadersVisibility> 
           </Binding.ConverterParameter> 
          </Binding> 
         </Custom:DataGridColumnHeadersPresenter.Visibility> 
        </Custom:DataGridColumnHeadersPresenter> 
        <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" Grid.ColumnSpan="2" Grid.Row="1" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" ContentTemplate="{TemplateBinding ContentTemplate}" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False"/> 
        <ScrollBar x:Name="PART_VerticalScrollBar" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Grid.Column="2" Grid.Row="1" Maximum="{TemplateBinding ScrollableHeight}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" Orientation="Vertical" ViewportSize="{TemplateBinding ViewportHeight}"/> 
        <Grid Grid.Column="1" Grid.Row="2"> 
         <Grid.ColumnDefinitions> 
          <ColumnDefinition Width="{Binding NonFrozenColumnsViewportHorizontalOffset, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Controls:DataGrid}}}"/> 
          <ColumnDefinition Width="*"/> 
         </Grid.ColumnDefinitions> 
         <ScrollBar x:Name="PART_HorizontalScrollBar" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Grid.Column="1" Maximum="{TemplateBinding ScrollableWidth}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" Orientation="Horizontal" ViewportSize="{TemplateBinding ViewportWidth}"/> 
        </Grid> 
       </Grid> 
      </ControlTemplate> 
     </ScrollViewer.Template> 
     <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> 
    </ScrollViewer> 
</Border> 

1

No estoy seguro de que esto esté funcionando pero aquí es una idea basada en un poco de investigación se hace en el código fuente de la cuadrícula de datos usando Reflector:

1/Crear una clase que hereda DataGridCellsPanel. Este es el panel que se utiliza internamente por la cuadrícula de datos con el fin de disponer las células

2/anular el método BringIndexIntoView por un método vacío (sin llamar al método de base)

3 establecer la propiedad/ItemsPanelTemplate en su XAML:

<tk:DataGrid> 
    <tk:DataGrid.ItemsPanel> 
     <ItemsPanelTemplate> 
      <local:DataGridCellsPanelNoAutoScroll /> 
     </ItemsPanelTemplate> 
    </tk:DataGrid.ItemsPanel> 
</tk:DataGrid> 

parece que cuando se produce un evento MouseDown, en algún momento el método BringIndexIntoView del panel está llamado a hacer el desplazamiento automático. Reemplazarlo con un no-operativo podría hacer el truco.

No tuve tiempo de probar esta solución, por favor, háganos saber si está funcionando.

+0

¡Una idea muy interesante, lo intentaré! ¡Gracias! :-) –

+0

Hmm ...eso parece matar mis vistas de celda, ¿es esa la forma correcta de moldear la celda? –

+0

por cierto, no hay necesidad de usar reflector. la fuente está disponible en http://wpf.codeplex.com/SourceControl/list/changesets – kenwarner

6

me tomó más tiempo para echar un vistazo a este problema ya que mi primera solución no era trabajando.

Sin embargo, la respuesta de John es casi la buena. El truco es atrapar el evento RequestBringIntoView ANTES de que llegue al ScrollViewer para marcarlo.

Si usted no tiene que filtrar toda la plantilla, puede utilizar el siguiente código:

var scp = TreeHelper.FindVisualChild<ScrollContentPresenter>(this.datagrid); 
scp.RequestBringIntoView += (s, e) => e.Handled = true; 

Usamos el ScrollContentPresenter porque es justo debajo de la ScrollViewer en el árbol visual.

Espero que esto ayude!

+1

Esto funciona muy bien para mí. Una implementación decente de FindVisualChild está aquí: http://stackoverflow.com/questions/980120/finding-control-within-wpf-itemscontrol/984862#984862 –

25

definir un EventSetter en el DataGrid.RowStyle para llamar a un controlador que impide la fila para poder recurrir a la vista:

XAML

<DataGrid> 
    <DataGrid.RowStyle> 
     <Style TargetType="{x:Type DataGridRow}"> 
      <EventSetter Event="Control.RequestBringIntoView" Handler="DataGrid_Documents_RequestBringIntoView" /> 
     </Style> 
    </DataGrid.RowStyle> 
</DataGrid> 

Handler

private void DataGrid_Documents_RequestBringIntoView(object sender, RequestBringIntoViewEventArgs e) 
{ 
    e.Handled = true;  
} 
+0

¿De verdad? 9 votos y no hay comentarios ??? En realidad empeora las cosas aquí. – Barton

+0

Intenté esta solución y funciona. Pero, el problema con este enfoque es que cuando intenta desplazarse presionando las teclas de flecha, no se desplaza realmente. ¿Has encontrado alguna solución para esto? –

+0

exactamente lo que estaba buscando, gracias –

4

que tenía el mismo problema y la respuesta de Jan ayudó yo. Lo único que faltaba era que ScrollContentPresenter se encontraría solo después de que ocurriera el evento Loaded. Creé una clase DataGrid extendida heredada de DataGrid con propiedad adicional AutoScroll para controlar si quiero que la cuadrícula se desplace automáticamente o no.

Aquí está la clase:

using System.Windows; 
using System.Windows.Controls; 
using Microsoft.Windows.Controls; 

namespace Bartosz.Wojtowicz.Wpf 
{ 
    public class ExtendedDataGrid : DataGrid 
    { 
     public bool AutoScroll { get; set; } 

     public ExtendedDataGrid() 
     { 
      AutoScroll = true; 
      Loaded += OnLoaded; 
     } 

     private void OnLoaded(object sender, RoutedEventArgs eventArgs) 
     { 
      if (!AutoScroll) 
      { 
       ScrollContentPresenter scp = DataGridHelper.GetVisualChild<ScrollContentPresenter>(this); 
       if (scp != null) scp.RequestBringIntoView += OnRequestBringIntoView; 
      } 
     } 

     private static void OnRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e) 
     { 
      e.Handled = true; 
     } 
    } 
} 

Y aquí es cómo lo usa:

<local:ExtendedDataGrid AutoScroll="False"> 
     <!-- your grid definition --> 
    </local:ExtendedDataGrid> 
0

Esto es lo que funcionó para mí (después de probar todos los menos complejos "respuestas" a la fecha):

<DataGrid Grid.Column="0" Grid.Row="1" 
       Name="ListItemContainerDataGrid" 
       ScrollViewer.VerticalScrollBarVisibility="Visible" 
       ScrollViewer.CanContentScroll="False" 
       And.Others 
       ItemsSource="{Binding Path=ListItemModels}" 
       > 
    </DataGrid> 

ScrollViewer.CanContentScroll = "False" parece alucinantemente contador intuitivo ...

-1

Como Dr.WPF ha respondido una pregunta similar here, RequestBringIntoView debe manejarse en ItemsPanel.

2

Tuve el mismo problema que Rumit, pero encontré una solución/pirateo.

Pensé que si podía encontrar una manera de diferenciar entre los clics del mouse y las teclas de flecha, podría establecer e.Handled en consecuencia.

Después de un poco de experimentación, encontré que e.OriginalSource cambió dependiendo del mouse o la tecla de flecha. Por un clic del mouse, el manejador de RequestBringIntoView se llama una vez y e.OriginalSource era del tipo DataGridCell. Para una tecla de flecha, el manejador se llama dos veces y e.OriginalSource es de tipos DataGridRow y luego DataGridCell.

El código para mi manejador es:

e.Handled = (e.OriginalSource is DataGridCell); 

Este parece ser un poco de un truco, pero funciona muy bien para mí.

Cuestiones relacionadas