【问题标题】:wpf DataGrid RowDetailsTemplate Scrollwpf DataGrid RowDetailsTemplate 滚动
【发布时间】:2014-06-24 19:23:07
【问题描述】:
<DataGrid>
  <DataGrid.Columns>
    <DataGridTextColumn Binding="{Binding Path=test}"></DataGridTextColumn>
  </DataGrid.Columns>
  <DataGrid.RowDetailsTemplate>
    <DataTemplate>
     <DataGrid Template="{DynamicResource TemplateDataGridPrintAndExport}"/>
    </DataTemplate>
  </DataGrid.RowDetailsTemplate>
 <DataGrid/>

我有一个像上面这样的数据网格。 Datgrid 的行详细信息模板还包含一个数据网格。单击父级的列时会填充内部数据网格。我的问题是:如果行详细信息模板数据网格已完成并且用户鼠标在滚动父数据网格时悬停在其上,则滚动不起作用。用户应将鼠标悬停在主数据网格上以滚动。但是,它对用户不友好。如何防止内部数据网格以这种方式运行?

【问题讨论】:

    标签: wpf xaml wpfdatagrid


    【解决方案1】:

    我通过尝试替代品找到了灵魂:

    <DataGrid  ScrollViewer.CanContentScroll="False">
      <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Path=test}"></DataGridTextColumn>
      </DataGrid.Columns>
      <DataGrid.RowDetailsTemplate>
        <DataTemplate>
         <DataGrid Template="{DynamicResource TemplateDataGridPrintAndExport}"  IsReadOnly="True" ScrollViewer.CanContentScroll="False"  IsEnabled="False"/>
        </DataTemplate>
      </DataGrid.RowDetailsTemplate>
     <DataGrid/>
    

    解决方案是将ScrollViewer.CanContentScroll="False" 属性赋予外部数据网格,将IsReadOnly="True" ScrollViewer.CanContentScroll="False" IsEnabled="False" 属性赋予内部数据网格。现在它正在平滑滚动并根据父数据网格。

    【讨论】:

    • 不幸的是,我的内部网格包含一个复选框。所以我无法将IsEnabled="False" 设置为内部网格。那么根据父数据网格滚动内部网格的任何其他想法?
    • 这个解决方案完全不直观,但它确实有效。至于为什么,这是一个谜。
    【解决方案2】:

    我想提出两种替代解决方案,因为所选择的一种具有严重的副作用。 Kaizen 提到了其中之一 - 您失去了与嵌套 DataGrid 及其子控件交互的能力。第二个是禁用状态下控件外观的变化。

    1. 在 osmanraifgunes 的解决方案中将 IsReadOnly="True" 更改为 IsHitTestVisible="False"。这将修复外观副作用,但您仍然无法与内部控件交互(使用鼠标)。代码:

      <DataGrid ScrollViewer.CanContentScroll="False">
          <DataGrid.Columns>
              <DataGridTextColumn Binding="{Binding Path=test}" />
          </DataGrid.Columns>
          <DataGrid.RowDetailsTemplate>
              <DataTemplate>
                  <DataGrid
                      IsHitTestVisible="False"
                      ScrollViewer.CanContentScroll="False"
                      Template="{DynamicResource TemplateDataGridPrintAndExport}" />
              </DataTemplate>
          </DataGrid.RowDetailsTemplate>
      </DataGrid>
      
    2. RowDetailsTemplate 内的控件中捕获隧道PreviewMouseWheel 事件并将其作为冒泡事件传递回父级。这将有效地使RowDetailsTemplate 中的控件仅对鼠标滚动视而不见,并允许可视树中的上方控件随心所欲地处理它。 xml:

      <DataGrid ScrollViewer.CanContentScroll="False">
          <DataGrid.Columns>
              <DataGridTextColumn Binding="{Binding Path=test}" />
          </DataGrid.Columns>
          <DataGrid.RowDetailsTemplate>
              <DataTemplate>
                  <DataGrid
                      PreviewMouseWheel="DataGrid_PreviewMouseWheel"
                      Template="{DynamicResource TemplateDataGridPrintAndExport}" />
              </DataTemplate>
          </DataGrid.RowDetailsTemplate>
      </DataGrid>
      

    后面的代码:

            private void DataGrid_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
            {
                if (e.Handled)
                {
                    return;
                }
                Control control = sender as Control;
                if(control == null)
                {
                    return;
                }
                e.Handled = true;
                var wheelArgs = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
                {
                    RoutedEvent = MouseWheelEvent,
                    Source = control
                };
                var parent = control.Parent as UIElement;
                parent?.RaiseEvent(wheelArgs);
            }
    

    【讨论】:

    • 我已经对您的第二个解决方案进行了快速修复,该解决方案似乎对我更有效。随时check it out.
    • @LuckyLikey 谢谢 - 我遇到了同样的问题。 Parent 始终为空,并且直接在外部网格上引发事件并没有做任何事情。您的解决方案 (VisualTreeHelper.GetParent(control)) 正是我所需要的。
    • @SeanWorle 感谢您的反馈,我很高兴它有所帮助。也许可以通过VisualTreeHelper 类快速阅读,因为它提供了更多有用的方法。
    【解决方案3】:

    如果您使用的是 .NET 4.5 及更高版本,则可以在外部网格上使用 VirtualizingPanel.ScrollUnit="Pixel",这将允许您按像素而不是单位(项目)滚动,因为当内部 DataGrid 较大时,这会导致非常奇怪的行为因为它开始跳来跳去。

    然后您可以使用内部 DataGrid 上的 PreviewMouseWheel 事件将滚动事件传递给父级,因为它正在被内部控件捕获。

    Xaml:

    <DataGrid VirtualizingPanel.ScrollUnit="Pixel">
        <DataGrid.RowDetailsTemplate>
            <DataTemplate>
                <DataGrid PreviewMouseWheel="DataGrid_PreviewMouseWheel"/>
            </DataTemplate>
        </DataGrid.RowDetailsTemplate>
    </DataGrid>
    

    cs:

    private void DataGrid_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        e.Handled = true;
        var parent = ((Control)sender).Parent as UIElement;
        parent?.RaiseEvent(new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
        {
            RoutedEvent = MouseWheelEvent,
            Source = sender
        });
    }
    

    【讨论】:

      【解决方案4】:

      我使用了@Bartłomiej Popielarz 的第二种方法来使其工作。

      在我的情况下,control.Parent 总是返回 null。这就是为什么我将相应的行更改为

          var parent = VisualTreeHelper.GetParent(control) as UIElement;
      

      我还创建了一个附加属性来执行转发(更适合 MVVM 方法)。

          public class FixScrollingBehaviorOn
          {
              public static readonly DependencyProperty ParentDataGridProperty = DependencyProperty.RegisterAttached("ParentDataGrid", typeof(DataGrid), typeof(FixScrollingBehaviorOn),
                  new FrameworkPropertyMetadata(default(DataGrid), OnParentDataGridPropertyChanged));
      
              public static bool GetParentDataGrid(DependencyObject obj)
      
              {
                  return (bool)obj.GetValue(ParentDataGridProperty);
              }
      
              public static void SetParentDataGrid(DependencyObject obj, bool value)
      
              {
                  obj.SetValue(ParentDataGridProperty, value);
              }
      
              public static void OnParentDataGridPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
      
              {
                  var dataGrid = sender as DataGrid;
      
                  if (dataGrid == null)
      
                  {
                      throw new ArgumentException("The dependency property can only be attached to a DataGrid", "sender");
                  }
      
                  if (e.NewValue is DataGrid parentGrid)
                  {
                      dataGrid.PreviewMouseWheel += HandlePreviewMouseWheel;
                      parentGrid.SetValue(ScrollViewer.CanContentScrollProperty, false);
                  }
                  else
                  {
                      dataGrid.PreviewMouseWheel -= HandlePreviewMouseWheel;
      
                      if (e.OldValue is DataGrid oldParentGrid)
                      {
                          oldParentGrid.SetValue(ScrollViewer.CanContentScrollProperty, ScrollViewer.CanContentScrollProperty.DefaultMetadata.DefaultValue);
                      }
                  }
              }
      
              private static void HandlePreviewMouseWheel(object sender, MouseWheelEventArgs e)
      
              {
                  if (e.Handled)
                  {
                      return;
                  }
                  var control = sender as DataGrid;
                  if (control == null)
                  {
                      return;
                  }
                  e.Handled = true;
                  var wheelArgs = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
                  {
                      RoutedEvent = UIElement.MouseWheelEvent,
                      Source = control
                  };
                  var parent = VisualTreeHelper.GetParent(control) as UIElement;
                  parent?.RaiseEvent(wheelArgs);
              }
      

      请随意在您的内部DataGrid 上使用它。请注意,ScrollViewer.CanContentScrollProperty 是在 AttachedProperty 中设置的,这可能不是每个人都喜欢的方法。

      <DataGrid>
          <DataGrid.Columns>
              <DataGridTextColumn Binding="{Binding Path=test}" />
          </DataGrid.Columns>
          <DataGrid.RowDetailsTemplate>
              <DataTemplate>
                  <DataGrid
                      attachedProperties:FixScrollingBehaviorOn.ParentDataGrid="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
                      Template="{DynamicResource TemplateDataGridPrintAndExport}" />
              </DataTemplate>
          </DataGrid.RowDetailsTemplate>
      </DataGrid>
      

      【讨论】:

        【解决方案5】:

        Xaml:

        <DataGrid ScrollViewer.CanContentScroll="False">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Path=test}" />
            </DataGrid.Columns>
            <DataGrid.RowDetailsTemplate>
                <DataTemplate>
                    <DataGrid
                        PreviewMouseWheel="DataGrid_PreviewMouseWheel"
                        Template="{DynamicResource TemplateDataGridPrintAndExport}" />
                </DataTemplate>
            </DataGrid.RowDetailsTemplate>
        </DataGrid>
        

        后面的代码:

        private void DataGrid_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
          DataGrid dg = new DataGrid();
                    if (sender is DataGrid)
                    {
                        dg = (DataGrid)sender;
                    }
                    dg.IsEnabled = false;
                    await Task.Delay(200);
                    dg.IsEnabled = true;
        }
        

        【讨论】:

        • 如果您只想更新现有的数据网格,为什么要创建新的数据网格?此外,我认为短期禁用网格以允许滚动是非常糟糕的做法。
        猜你喜欢
        • 2012-12-05
        • 1970-01-01
        • 2017-07-12
        • 2013-12-22
        • 2011-10-06
        • 2010-12-01
        • 2013-11-20
        • 1970-01-01
        • 2021-09-20
        相关资源
        最近更新 更多