如何阻止DataGridRow选择是否在RowDetails中单击鼠标

时间:2017-09-25 00:44:27

标签: c# wpf datagrid

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

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

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

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

是否有办法在Grid“DetailsContainer”级别启动PreviewMouseDown的隧道,并在Grid“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;
    }
}

2 个答案:

答案 0 :(得分:1)

不幸的是,使用当前的WPF DataGrid实现,无法实现您想要的目标。

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

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

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

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

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

您可以将容器IsHitTestVisible的{​​{1}}属性设置为Grid,这将禁用自动行选择行为。但很明显,你根本无法处理行详细信息中的任何点击。

答案 1 :(得分:0)

我发现了另一种可能的解决方法。在Canvas内创建一个小的DataGridRowHeader,然后在下一个子容器设置ClipsToBoundsfalse中创建。然后它可以没有标题,但也可以点击它而不影响DataGrid当前选择。它与行详细信息的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>

现在,这是我生产的一个简单示例: enter image description here