2010-01-14 16 views
23

Problem:
Jeśli moja DataGrid nie jest całkowicie widoczne pionowe (poziome & przewijania są wyświetlane) i kliknąć na jedną z moich komórek, który jest częściowo widoczne, siatka auto-przewija przynieść że komórka do widzenia. Nie chcę, aby to się stało. Próbowałem gry z RequestBringIntoView, tak:WPF DataGrid: jak zatrzymać automatyczne przewijanie po kliknięciu komórki?

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

jednak, że nic nie robi.

Czego próbowałem:

  • Moi komórki są niestandardowe UserControls; Próbowałem umieścić program obsługi zdarzeń dla RequestBringIntoView na wszystkich UserControls, które tworzą moje komórki i próbowałem obsługiwać wydarzenie, myśląc, że być może nie robiłem wystarczająco, po prostu obsługując RequestBringIntoView samemu sobie. To nie zadziałało.
  • Wystąpiło DataGrid wewnątrz ScrollViewer i obsłużyło zdarzenie ScrollViewer 'RequestBringIntoView. To faktycznie działa i zatrzymuje automatyczne przewijanie, ale w moim przypadku hostowanie DataGrid wewnątrz ScrollViewer wcale nie jest pożądane, więc muszę wymyślić inne rozwiązanie.

Nie jestem pewien, jak zatrzymać to zachowanie, jakieś pomysły?

Odpowiedz

7

Można uzyskać dostęp do wewnętrznego przewijacza DataGrid, modyfikując szablon. Chociaż normalnie nie wstawiłbyś programu obsługi zdarzeń do kodu w szablonie, jeśli zadeklarujesz szablon w linii, możesz traktować procedurę obsługi zdarzeń w taki sam sposób, jak przy dołączaniu do samego DataGrid. Jest to domyślny szablon jak generowane mieszanka zawierająca dodatkowy obsługi na ScrollViewer dla zdarzenia 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

nie jestem pewien, że to działa, ale tutaj jest pomysł opiera się na jakimś dochodzeniu jest w kodzie źródłowym DataGrid Korzystanie Reflektor:

1/utworzyć klasę, która dziedziczy DataGridCellsPanel. Jest to panel, który jest używany wewnętrznie przez DataGrid w celu ustalenia komórki

2/zastąpić metodę BringIndexIntoView przez pustą metody (bez wywoływania metody bazowej)

3/ustaw właściwość ItemsPanelTemplate w twojej XAML:

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

wydaje się, że w przypadku wystąpienia zdarzenia MouseDown, w pewnym momencie metoda BringIndexIntoView panelu nazywa zrobić auto-scroll. Zastąpienie go no-op może załatwić sprawę.

Nie miałem czasu, aby przetestować to rozwiązanie, proszę dać nam znać, czy działa.

+0

Bardzo ciekawy pomysł, spróbuję! Dzięki! :-) –

+0

Hmm ...która wydaje się zabijać moje widoki komórek, czy jest to właściwy sposób na szablonowanie komórki? –

+0

przy okazji, nie trzeba używać reflektora. źródło jest dostępne pod adresem http://wpf.codeplex.com/SourceControl/list/changesets – kenwarner

6

wziąłem więcej czasu, aby spojrzeć na ten problem jako moje pierwsze rozwiązanie nie było pracujący.

Jednak odpowiedź Jana jest prawie dobra. Sztuką jest złapać zdarzenie RequestBringIntoView, zanim dotrze do ScrollViewer, aby oznaczyć, że zostało obsłużone.

Jeśli nie masz udoskonalić cały szablon, można użyć następującego kodu:

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

Używamy ScrollContentPresenter bo to jest po prostu poniżej ScrollViewer w drzewie wizualne.

Mam nadzieję, że to pomoże!

+1

To działa doskonale dla mnie. Przyzwoite wdrożenie FindVisualChild jest tutaj: http://stackoverflow.com/questions/980120/finding-control-within-wpf-itemscontrol/984862#984862 –

25

Definiowanie EventSetter w DataGrid.RowStyle do wywołania obsługi, która zapobiega wiersz z ich wprowadzeniem do widzenia:

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

Naprawdę? 9 głosów w górę i brak komentarzy ??? Właściwie to pogarsza sprawę. – Barton

+0

Próbowałem tego rozwiązania i to działa. Ale problem z tym podejściem polega na tym, że próba przewinięcia za pomocą klawiszy strzałek nie jest w rzeczywistości przewijana. Czy wymyśliłeś jakieś obejście tego problemu? –

+0

dokładnie to, czego szukałem, dzięki –

4

miałem ten sam problem i odpowiedź Jana pomogły mnie. Jedynym brakującym elementem było to, że ScrollContentPresenter zostanie znaleziony dopiero po wystąpieniu zdarzenia Loaded. Utworzono rozszerzoną klasę DataGrid odziedziczoną z DataGrid z dodatkową właściwością AutoScroll, aby kontrolować, czy chcę, aby siatka przewijała się automatycznie, czy nie.

Oto klasa:

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

A oto jak go używać:

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

Oto co pracował dla mnie (po wypróbowaniu wszystkich mniej skomplikowanych „odpowiedzi” Do tej pory):

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

ScrollViewer.CanContentScroll = "Fałsz" wydaje się być lękliwie intuicyjny ...

-1

Jako Dr.WPF odpowiedział na podobne pytanie here RequestBringIntoView powinny być obsługiwane w ItemsPanel.

2

Miałem ten sam problem co Rumit, ale znalazłem rozwiązanie/hack.

Pomyślałem, że gdybym mógł znaleźć sposób na rozróżnienie między kliknięciami myszy a klawiszami strzałek, to mógłbym odpowiednio ustawić e. Trzymane.

Po kilku eksperymentach odkryłem, że e.OriginalSource zmienia się w zależności od myszy lub klawisza strzałki. Dla kliknięcia myszą, obsługa dla RequestBringIntoView jest wywoływana raz, a e.OriginalSource jest typu DataGridCell. Dla klawisza strzałki handler jest wywoływany dwa razy, a e.OriginalSource jest typu DataGridRow, a następnie DataGridCell.

Kod dla mojego przewodnika jest:

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

To wydaje się trochę hack, ale działa świetnie dla mnie.

Powiązane problemy