2010-01-14 4 views
23

Problème:
Si mon DataGrid est pas tout à fait visibles (horizontales & verticales montrent des barres de défilement) et je clique sur un de mes cellules qui est partiellement visible, la grille défile auto-pour amener cette cellule en vue. Je ne veux pas que cela se produise. J'ai essayé de jouer avec RequestBringIntoView, comme ceci:WPG DataGrid: comment arrêter le défilement automatique lorsqu'une cellule est cliquée?

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

Mais cela ne fait rien.

choses que j'ai essayé:

  • Mes cellules sont personnalisées UserControls; J'ai essayé de mettre un gestionnaire d'événement pour RequestBringIntoView sur tous les UserControls qui composent mes cellules, et j'ai essayé de gérer l'événement, pensant que peut-être je ne faisais pas assez en manipulant simplement RequestBringIntoView sur le DataGrid lui-même. Cela n'a pas fonctionné.
  • Hébergé le DataGrid à l'intérieur d'un ScrollViewer, et géré l'événement RequestBringIntoView de ScrollViewer. Cela fonctionne réellement, et arrête le comportement de défilement automatique, mais dans mon cas, l'hébergement d'un DataGrid à l'intérieur d'un ScrollViewer n'est pas du tout souhaitable, donc j'ai besoin de trouver une solution différente.

Je ne sais pas comment arrêter ce comportement, des idées?

Répondre

7

Vous pouvez accéder au ScrollViewer interne du DataGrid en modifiant le modèle. Bien que normalement vous ne mettiez pas un gestionnaire d'événements à coder derrière dans un modèle, si vous déclarez le modèle en ligne, vous pouvez traiter le gestionnaire d'événements de la même manière que vous le faites lorsque vous l'attachez au DataGrid lui-même. Ceci est le modèle par défaut tel qu'il est généré à partir Blend, y compris un gestionnaire ajouté le ScrollViewer pour l'événement 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

Je ne suis pas sûr que cela fonctionne, mais voici une idée fondée sur une enquête est faite dans le code source du DataGrid à l'aide du réflecteur:

1/créer une classe qui hérite DataGridCellsPanel. Ceci est le panneau qui est utilisé en interne par la grille de données afin d'organiser les cellules

2/remplacer la méthode BringIndexIntoView par une méthode vide (sans faire appel à la méthode de base)

3/définir la propriété ItemsPanelTemplate dans votre XAML:

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

Il semble que lorsqu'un événement se produit MouseDown, à un moment donné la méthode BringIndexIntoView du panneau est appelé à faire l'auto-défilement. Le remplacer par un no-op pourrait faire l'affaire.

Je n'avais pas le temps de tester cette solution, s'il vous plaît laissez-nous savoir si cela fonctionne.

+0

Idée très intéressante, je vais essayer! Merci! :-) –

+0

Hmm ...cela semble tuer mes vues de cellule, est-ce la bonne manière de modéliser la cellule? –

+0

BTW, pas besoin d'utiliser le réflecteur. la source est disponible à http://wpf.codeplex.com/SourceControl/list/changesets – kenwarner

6

J'ai pris plus de temps pour jeter un oeil à ce problème que ma première solution n'a pas été travail.

Cependant, la réponse de John est presque la bonne. L'astuce consiste à intercepter l'événement RequestBringIntoView AVANT qu'il n'atteigne ScrollViewer afin de marquer qu'il a été traité.

Si vous ne disposez pas d'affiner le tout modèle, vous pouvez utiliser le code suivant:

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

Nous utilisons le ScrollContentPresenter parce qu'il est juste en dessous du ScrollViewer dans l'arbre visuel.

Espérons que cela aide!

+1

Cela fonctionne très bien pour moi. Une mise en œuvre correcte de FindVisualChild est ici: http://stackoverflow.com/questions/980120/finding-control-within-wpf-itemscontrol/984862#984862 –

25

définir un EventSetter dans le DataGrid.RowStyle pour appeler un gestionnaire qui empêche la rangée d'être amenée en vue:

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

Vraiment? 9 votes et aucun commentaire ??? En fait, les choses empirent ici. – Barton

+0

J'ai essayé cette solution et cela fonctionne. Mais, le problème avec cette approche est, lorsque vous essayez de faire défiler en appuyant sur les touches fléchées, il ne fait défiler. Avez-vous trouvé une solution de contournement pour cela? –

+0

exactement ce que je cherchais, merci –

4

I eu le même problème et la réponse de Jan a aidé moi. La seule chose qui manquait était que ScrollContentPresenter sera trouvé seulement après l'événement Loaded. J'ai créé une classe DataGrid héritée de DataGrid avec la propriété supplémentaire AutoScroll pour contrôler si je veux que la grille défile automatiquement ou non.

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

Et voici comment vous l'utilisez:

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

Voilà ce qui a fonctionné pour moi (après avoir essayé toutes les « réponses » moins complexes à ce jour):

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

ScrollViewer.CanContentScroll = "Faux" semble stupéfiant contre intuitif ...

-1

Comme Dr.WPF a répondu à une question similaire here le RequestBringIntoView doit être géré dans ItemsPanel.

2

J'ai eu le même problème que Rumit, mais j'ai trouvé une solution/hack.

Je pensais que si je pouvais trouver un moyen de différencier les clics de souris et les touches fléchées, alors je pourrais régler e.Handled en conséquence. Après quelques expériences, j'ai trouvé que e.OriginalSource a changé en fonction de la souris ou de la touche fléchée. Pour un clic de souris, le gestionnaire de RequestBringIntoView est appelé une fois et e.OriginalSource était de type DataGridCell. Pour une touche fléchée, le gestionnaire est appelé deux fois et e.OriginalSource est de type DataGridRow puis DataGridCell.

Le code pour mon gestionnaire est:

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

Cela semble être un peu un hack, mais fonctionne très bien pour moi.

Questions connexes