2010-01-14 11 views
23

Problema:
Se la mia DataGrid non è interamente visibile (orizzontale & scorrimento verticali sono mostrando) e clicca su uno dei miei celle che è parzialmente visibile, la griglia scorre automaticamente per portare quella cella in vista. Non voglio che ciò accada. Ho provato a giocare con RequestBringIntoView, in questo modo:DataGrid WPF: come posso interrompere lo scorrimento automatico quando si fa clic su una cella?

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

Ma questo non fa nulla.

Le cose che ho provato:

  • mie cellule sono personalizzati UserControls; Ho provato a inserire un gestore di eventi per RequestBringIntoView su tutti gli UserControls che compongono le mie celle e ho provato a gestire l'evento, pensando che forse non stavo facendo abbastanza semplicemente gestendo lo DataGrid nello stesso RequestBringIntoView. Questo non ha funzionato.
  • Ospitato il DataGrid all'interno di un ScrollViewer e gestito l'evento RequestBringIntoViewScrollViewer. Questo in realtà funziona e interrompe il comportamento di scorrimento automatico, ma nel mio caso l'hosting di uno DataGrid all'interno di uno ScrollViewer non è affatto auspicabile, quindi ho bisogno di trovare una soluzione diversa.

Non so come fermare questo comportamento, qualche idea?

risposta

7

È possibile accedere al ScrollViewer interno di DataGrid modificando il modello. Sebbene normalmente non si possa inserire un gestore di eventi in codice in un modello, se si dichiara il modello in linea è possibile trattare il gestore di eventi nello stesso modo in cui si è quando lo si collega al DataGrid stesso. Questo è il modello predefinito come generata da miscela tra cui un gestore aggiunto sul ScrollViewer per l'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

io non sono sicuro che questo funziona, ma qui è un'idea sulla base di alcune indagini è fatto nel codice sorgente del DataGrid con riflettore:

1/creare una classe che eredita DataGridCellsPanel. Questo è il pannello che viene utilizzato internamente dal DataGrid per disporre le cellule

2/sostituire il metodo BringIndexIntoView da un metodo vuoto (senza chiamare il metodo base)

3/impostare la proprietà ItemsPanelTemplate nel XAML:

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

sembra che quando si verifica un evento MouseDown, ad un certo punto il metodo BringIndexIntoView del pannello è chiamato a fare lo scorrimento automatico. Sostituirlo con un no-op potrebbe fare il trucco.

Non ho avuto il tempo di testare questa soluzione, fatecelo sapere se funziona.

+0

Idea molto interessante, ci provo! Grazie! :-) –

+0

Hmm ...quello sembra uccidere le mie viste delle cellule, è quello il giusto modo di mascherare la cellula? –

+0

btw, non è necessario utilizzare il riflettore. la fonte è disponibile su http://wpf.codeplex.com/SourceControl/list/changesets – kenwarner

6

ho preso più tempo per dare un'occhiata a questo problema come la mia prima soluzione non è stata lavoro.

Tuttavia la risposta di Giovanni è quasi quella giusta. Il trucco è catturare l'evento RequestBringIntoView PRIMA che arrivi al ScrollViewer per contrassegnarlo come gestito.

Se non si dispone per perfezionare l'intero modello, è possibile utilizzare il seguente codice:

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

Usiamo lo ScrollContentPresenter perché è appena sotto la ScrollViewer nella struttura ad albero visuale.

Spero che questo aiuti!

+1

Questo funziona perfettamente per me. Un'implementazione decente di FindVisualChild è disponibile qui: http://stackoverflow.com/questions/980120/finding-control-within-wpf-itemscontrol/984862#984862 –

25

definire un EventSetter nel DataGrid.RowStyle per chiamare un gestore che impedisce la riga venga portato in 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

Davvero? 9 voti e nessun commento ??? In realtà rende le cose peggiori qui. – Barton

+0

Ho provato questa soluzione e funziona. Tuttavia, il problema con questo approccio è che quando si prova a scorrere premendo i tasti freccia, in realtà non scorre. Hai trovato qualche soluzione per questo? –

+0

esattamente quello che stavo cercando, grazie –

4

avevo lo stesso problema e la risposta di Jan ha aiutato me. L'unica cosa che mancava era che ScrollContentPresenter fosse trovato solo dopo che si verificava l'evento Loaded. Ho creato una classe DataGrid estesa ereditata da DataGrid con proprietà aggiuntive AutoScroll per controllare se voglio che la griglia scorra automaticamente o meno.

Ecco la classe:

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; 
     } 
    } 
} 

Ed ecco come lo si utilizza:

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

Ecco cosa ha funzionato per me (dopo aver provato tutti i meno complessi "risposte" ad oggi):

<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" sembra controproducente da un punto di vista mentale ...

-1

Come Dr.WPF ha risposto a una domanda simile here il RequestBringIntoView deve essere gestito in ItemsPanel.

2

Ho avuto lo stesso problema di Rumit, ma ho trovato una soluzione/hack.

Ho pensato di trovare un modo per distinguere tra clic del mouse e tasti freccia, quindi ho potuto impostare e.Handled di conseguenza.

Dopo alcuni esperimenti, ho scoperto che e.OriginalSource è cambiata in base al mouse o al tasto freccia. Per un clic del mouse, il gestore per RequestBringIntoView viene chiamato una volta e e.OriginalSource era di tipo DataGridCell. Per un tasto freccia, il gestore viene chiamato due volte e e.OriginalSource è di tipo DataGridRow e quindi DataGridCell.

Il codice per il mio gestore è:

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

Questo mi sembra un po 'di un hack, ma funziona grande per me.

Problemi correlati