2010-01-14 49 views
23

Vấn đề:
Nếu tôi DataGrid không hoàn toàn có thể nhìn thấy (ngang & thanh cuộn dọc đang hiển thị) và tôi bấm vào một trong các tế bào của tôi đó là một phần có thể nhìn thấy, lưới tự động cuộn để mang lại cho rằng tế bào vào xem. Tôi không muốn điều này xảy ra. Tôi đã thử chơi xung quanh với RequestBringIntoView, như sau:WPF DataGrid: làm cách nào để dừng tự động cuộn khi một ô được nhấp?

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

Nhưng điều đó không có gì.

Những điều tôi đã cố gắng:

  • tế bào của tôi là tùy chỉnh UserControls; Tôi đã thử đặt một trình xử lý sự kiện cho RequestBringIntoView trên tất cả UserControls tạo nên các ô của mình và cố gắng xử lý sự kiện, nghĩ rằng có lẽ tôi không làm đủ bằng cách chỉ xử lý RequestBringIntoView trên chính bản thân số DataGrid. Điều này đã không hoạt động.
  • Đã lưu trữ DataGrid bên trong số ScrollViewer và xử lý sự kiện RequestBringIntoView của . Điều này thực sự hoạt động, và dừng hành vi tự động cuộn, nhưng trong trường hợp của tôi lưu trữ DataGrid bên trong của một ScrollViewer không phải là ở tất cả mong muốn, vì vậy tôi cần phải đưa ra một giải pháp khác nhau.

Tôi không chắc chắn cách ngăn chặn hành vi này, bất kỳ ý tưởng nào?

Trả lời

7

Bạn có thể truy cập ScrollViewer nội bộ của DataGrid bằng cách sửa đổi mẫu. Mặc dù thông thường bạn sẽ không đặt một trình xử lý sự kiện để mã sau trong một khuôn mẫu, nếu bạn khai báo mẫu nội tuyến, bạn có thể xử lý trình xử lý sự kiện giống như cách bạn đang có khi bạn đính kèm nó vào chính DataGrid. Đây là mẫu mặc định như được tạo ra từ Blend bao gồm một bộ xử lý gia tăng đối với ScrollViewer cho sự kiện 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

Tôi không chắc chắn này đang làm việc nhưng đây là một ý tưởng dựa trên một số cuộc điều tra được thực hiện trong mã nguồn của DataGrid dùng Reflector:

1/tạo ra một lớp mà được thừa hưởng DataGridCellsPanel. Đây là Bảng điều chỉnh được sử dụng trong nội bộ của DataGrid để sắp xếp các tế bào

2/ghi đè lên các phương pháp BringIndexIntoView bởi một phương pháp rỗng (không có cách gọi phương thức cơ sở)

3/thiết lập thuộc tính ItemsPanelTemplate tại của bạn XAML:

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

Dường như khi xảy ra sự kiện MouseDown, tại một số điểm, phương pháp BringIndexIntoView của bảng điều khiển được gọi để thực hiện tự động cuộn. Thay thế nó với một no-op có thể làm các trick.

Tôi không có thời gian để kiểm tra giải pháp này, vui lòng cho chúng tôi biết nếu nó hoạt động.

+0

Ý tưởng rất thú vị, tôi sẽ thử! Cảm ơn! :-) –

+0

Hmm ...mà dường như giết chết các quan điểm của tế bào của tôi, đó là cách thích hợp để tạo mẫu cho ô? –

+0

btw, không cần sử dụng bộ phản xạ. nguồn có sẵn tại http://wpf.codeplex.com/SourceControl/list/changesets – kenwarner

6

tôi mất nhiều thời gian hơn để có một cái nhìn tại vấn đề này như giải pháp đầu tiên của tôi là không đang làm việc.

Tuy nhiên câu trả lời của John gần như là câu trả lời hay nhất. Bí quyết là để nắm bắt các sự kiện RequestBringIntoView TRƯỚC KHI nó được vào ScrollViewer để đánh dấu nó đã xử lý.

Nếu bạn không cần phải tinh chỉnh toàn bộ mẫu, bạn có thể sử dụng đoạn mã sau:

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

Chúng tôi sử dụng ScrollContentPresenter vì nó ngay dưới ScrollViewer trong cây thị giác.

Hy vọng điều này sẽ hữu ích!

+1

Điều này làm việc tuyệt vời cho tôi. Việc triển khai khá hiệu quả FindVisualChild là ở đây: http://stackoverflow.com/questions/980120/finding-control-within-wpf-itemscontrol/984862#984862 –

25

Xác định một EventSetter trong DataGrid.RowStyle để gọi một handler có thể ngăn chặn hàng khỏi bị đưa vào xem:

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

Thật sao? 9 phiếu bầu và không có bình luận ??? Trên thực tế làm cho mọi thứ tồi tệ hơn ở đây. – Barton

+0

Tôi đã thử giải pháp này và nó hoạt động. Nhưng, vấn đề với cách tiếp cận này là, khi bạn cố gắng di chuyển bằng cách nhấn các phím mũi tên, nó không thực sự cuộn. Bạn đã nghĩ ra cách giải quyết nào cho việc này chưa? –

+0

chính xác những gì tôi đang tìm kiếm, cảm ơn –

4

tôi đã cùng một vấn đề và câu trả lời của Jan đã giúp tôi. Điều duy nhất còn thiếu là ScrollContentPresenter sẽ chỉ được tìm thấy sau khi sự kiện Loaded xảy ra. Tôi tạo ra một lớp DataGrid mở rộng kế thừa từ DataGrid với tính năng AutoScroll bổ sung để kiểm soát nếu tôi muốn lưới cuộn tự động hay không.

Dưới đây là các lớp:

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

Và đây là cách bạn sử dụng nó:

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

Đây là những gì làm việc cho tôi (sau khi thử tất cả các "câu trả lời" ít phức tạp cho đến nay):

<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" có vẻ như đang truy cập trực quan ...

-1

Vì Dr.WPF đã trả lời một câu hỏi tương tự here các RequestBringIntoView sẽ được xử lý trong ItemsPanel.

2

Tôi gặp vấn đề tương tự như Rumit, nhưng đã tìm thấy giải pháp/hack.

Tôi nghĩ nếu tôi có thể tìm cách phân biệt giữa các lần nhấp chuột và các phím mũi tên, thì tôi có thể đặt e.Handled cho phù hợp.

Sau một số thử nghiệm, tôi thấy rằng e.OriginalSource đã thay đổi tùy thuộc vào chuột hoặc phím mũi tên. Đối với một cú click chuột, trình xử lý cho RequestBringIntoView được gọi một lần và e.OriginalSource thuộc loại DataGridCell. Đối với một phím mũi tên, trình xử lý được gọi hai lần và e.OriginalSource thuộc loại DataGridRow và sau đó là DataGridCell.

Mã cho xử lý của tôi là:

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

Điều này có vẻ giống như một chút của một hack, nhưng hoạt động tuyệt vời đối với tôi.

Các vấn đề liên quan