【问题标题】:How to stop DataGridRow from selecting if mouse is clicked inside RowDetails如果在 RowDetails 中单击鼠标,如何阻止 DataGridRow 选择
【发布时间】:2017-09-25 00:44:26
【问题描述】:

我有一个DataGrid,每个DataGridRow 都有行详细信息,其中包含几个控件。

我想要的是,如果在行详细信息中单击任何内容:
- 不选择行,或更准确地说,
- 更改现有的DataGrid 选择。

我在考虑如何在行为中处理 PreviewMouseDown 和 MouseDown 事件,以某种方式使 DataGrid 跳过选择过程,但不知道如何继续。

最终我将在详细信息中添加一个 TabControl 以及更多信息,因此我也不希望单击 TabItem 来更改 DataGrid 的现有选择。

是否有办法在网格“DetailsContainer”级别启动 PreviewMouseDown 隧道,并在网格“DetailsContainer”级别停止 MouseDown 冒泡

<DataGrid Name="dgAudit"
          CanUserReorderColumns="False"
          CanUserAddRows="False"
          CanUserDeleteRows="False"
          CanUserResizeColumns="False"
          CanUserResizeRows="False"
          CanUserSortColumns="False"
          IsReadOnly="True"
          ItemsSource="{Binding GEOM_ASSET_OC_LIST}"
          VirtualizingPanel.ScrollUnit="Pixel"
          RowDetailsVisibilityMode="Visible"
          >
    <i:Interaction.Behaviors>
        <behaviors:DataGridBehaviors />
    </i:Interaction.Behaviors>

    <DataGrid.Columns>
        <DataGridTextColumn Header="Asset ID" Binding="{Binding ASSET_ID}" Width="200" />
        <DataGridTextColumn Header="Asset Type" Binding="{Binding ASSET_TYPE}" Width="200" />
        <DataGridTextColumn Header="Last Update By" Binding="{Binding LAST_UPDATE_BY}" Width="150" />
        <DataGridTextColumn Header="Last Update Date" Binding="{Binding LAST_UPDATE_DATETIME, StringFormat=\{0:dd.MM.yy HH:mm:ss tt\}}" Width="150" />
    </DataGrid.Columns>
    <DataGrid.RowDetailsTemplate>
        <DataTemplate>
            <Grid Name="DetailsContainer">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <TextBlock Name Text="{Binding Notes}" Width="400" HorizontalAlignment="Left" TextWrapping="Wrap"/>
                <Button Content="Button" Grid.Column="1" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
            </Grid>
        </DataTemplate>
    </DataGrid.RowDetailsTemplate>
</DataGrid>

只是一个空行为的快速模拟

public class DataGridBehaviors : Behavior<DataGrid>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.MouseDown += DataGrid_MouseDown;
        this.AssociatedObject.PreviewMouseDown += DataGrid_PreviewMouseDown;
    }

    protected override void OnDetaching()
    {
        this.AssociatedObject.PreviewMouseDown -= DataGrid_PreviewMouseDown;
        this.AssociatedObject.MouseDown -= DataGrid_MouseDown;
        base.OnDetaching();
    }

    private void DataGrid_MouseDown(object sender, MouseButtonEventArgs e)
    {
    }

    private void DataGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        DependencyObject obj = (DependencyObject)e.OriginalSource;
        DataGridDetailsPresenter RowsDetails = FindParent<DataGridDetailsPresenter>(obj);
        if (RowsDetails != null)
        {
            //Skip over selection, maybe temporarily removed native selection handler???
        }
    }

    public static T FindParent<T>(DependencyObject child) where T : DependencyObject
    {
        //get parent item
        DependencyObject parentObject = VisualTreeHelper.GetParent(child);

        //we've reached the end of the tree
        if (parentObject == null) return null;

        //check if the parent matches the type we're looking for
        T parent = parentObject as T;
        if (parent != null)
            return parent;
        else
            return FindParent<T>(parentObject);
    }

    private static T GetVisualChild<T>(DependencyObject parent) where T : Visual
    {
        T child = default(T);

        int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < numVisuals; i++)
        {
            Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
            child = v as T;
            if (child == null)
            {
                child = GetVisualChild<T>(v);
            }
            if (child != null)
            {
                break;
            }
        }
        return child;
    }
}

【问题讨论】:

  • 可以将只读属性设为“true”

标签: c# wpf datagrid


【解决方案1】:

很遗憾,使用当前 WPF DataGrid 的实现,无法实现您想要的。

DataGridDetailsPresenter 使用EventManager API 为MouseLeftButtonDownEvent 路由事件注册class event handler

EventManager.RegisterClassHandler(
    typeof(DataGridDetailsPresenter),
    MouseLeftButtonDownEvent,
    new MouseButtonEventHandler(OnAnyMouseLeftButtonDownThunk),
    true);

注意最后一个参数设置为true。它是一个标志,指示侦听器是否想听到已处理的事件。在这种情况下,即使您在任何级别将RoutedEventArgs.Handled 属性设置为true,仍然会调用内部DataGridDetailsPresenter 的事件处理程序。

此事件处理程序负责关注您单击其中的详细信息视图的DataRow。您肯定知道,在 WPF 中,类事件处理程序在实例事件处理程序之前被调用。因此,左键单击详细信息行的演示者时,首先会发生的是包含行(实际上是该行的第一个单元格)的焦点。

还请注意,此行为也管理RowPresenter 内的虚拟项目的实现,因此禁用它可能会导致不必要的 GUI 副作用。

您可以将容器GridIsHitTestVisible 属性设置为false,这将禁用自动行选择行为。但显然,您根本无法处理行详细信息内的任何点击。

【讨论】:

  • 很好的答案,可怕的事实,所以我可以检查点击是否来自行详细信息,如果是,保存现有的选定项目/索引,一旦控件完成其自然行为重新应用选择...或者那里
【解决方案2】:

我发现了另一种可能的解决方法。通过在DataGridRowHeader 中创建一个小的Canvas,然后在下一个子容器中将ClipsToBounds 设置为false。然后它没有标题,但也可以单击而不影响DataGrids 当前选择。绑定到行明细的IsExpanded,看部分。

一个警告是,如果行详细信息包含大量数据,使用下面的代码,可能会非常低效,加载时间和滚动延迟。取决于是否正在使用 virtualization。此数据将与行同时加载,并且可能效率很低,而当控件位于 RowDetailsTemplate 中时,它们会按需加载。可能最好通过User Control 或动态地严格控制此数据的加载。

您可能还需要监听 DataGrid LayoutUpdated 事件来调整细节控件的宽度。

<DataGrid.RowHeaderTemplate>
    <DataTemplate>
        <Grid>
            <Expander Template="{StaticResource StretchyExpanderTemp}"
                      OverridesDefaultStyle="True"
                      Header=""
                      HorizontalAlignment="Right"
                      VerticalAlignment="Top"
                      Expanded="Expander_Expanded" Collapsed="Expander_Collapsed"
                      IsExpanded="{Binding DataContext.IsExpanded, RelativeSource={RelativeSource AncestorType=DataGridRowHeader}}" />

            <Canvas Background="BlueViolet" 
                    Width="5"
                    VerticalAlignment="Top"
                    HorizontalAlignment="Left" 
                    Height="5">
                <TabControl Name="tcAA" 
                            TabStripPlacement="Left" 
                            Margin="20,18,0,0" 
                            Height="185"
                            Width="500"
                            VerticalAlignment="Top"
                            HorizontalAlignment="Left"
                            ItemsSource="{Binding DataContext.AAUDIT, RelativeSource={RelativeSource AncestorType=DataGridRowHeader}}" 
                            SelectedIndex="0" 
                            ClipToBounds="False"
                            Visibility="{Binding DataContext.IsExpanded, RelativeSource={RelativeSource AncestorType=DataGridRowHeader}, Converter={StaticResource bool2VisibilityConverter}}"
                            >
                    ...
                    <TabControl.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <TextBlock Text="{Binding DISPLAY_NAME}" />
                            </Grid>
                        </DataTemplate>
                    </TabControl.ItemTemplate>
                    <TabControl.ContentTemplate>
                        <DataTemplate>
                            ...
                        </DataTemplate>
                    </TabControl.ContentTemplate>
                </TabControl>
            </Canvas>
        </Grid>
    </DataTemplate>
</DataGrid.RowHeaderTemplate>

<DataGrid.RowDetailsTemplate>
    <DataTemplate>
        <Grid Height="185" >
        </Grid>
    </DataTemplate>
</DataGrid.RowDetailsTemplate>

就目前而言,这是我制作的一个简单示例:

【讨论】:

    猜你喜欢
    • 2020-06-30
    • 2021-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多