WPF数据表格中未选择行时,点击事件未触发

8

我有一个像这样的DataGrid (WPF 4):

<DataGrid   Margin="0,0,0,5" VerticalAlignment="Top" Height="192" 
                            BorderBrush="#aaa" Background="White" 
                            HorizontalAlignment="Left" 
                            ItemsSource="{Binding Namen, Mode=OneWay}" 
                            ScrollViewer.VerticalScrollBarVisibility="Visible"
                            AutoGenerateColumns="False" 
                            ColumnHeaderHeight="24" 
                            SelectionChanged="DataGridAuslaendischeAlteNamen_SelectionChanged">
                    <DataGrid.Columns>
                        <DataGridTextColumn Width="*" Header="Namenseintrag" Binding="{Binding DisplayName, Mode=OneWay}" />
                        <DataGridTextColumn Width="75" Header="gültig von" Binding="{Binding GueltigAb, StringFormat=d, Mode=OneWay}" />
                        <DataGridTextColumn Width="75" Header="gültig bis" Binding="{Binding GueltigBis, StringFormat=d., Mode=OneWay}" />
                        <DataGridTemplateColumn Width="20" IsReadOnly="True">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <Button Style="{DynamicResource CaratRemoveButton}" 
                                            Click="Button_Click" CommandParameter="{Binding}" 
                                            PreviewMouseDown="Button_PreviewMouseDown" 
                                            />
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                </DataGrid.Columns>
                </DataGrid>

我遇到的问题是,如果未选择DataGridTemplateColumn行,则该行上的按钮不会触发单击事件。因此,我必须点击两次按钮,一次选择其行,然后再引发单击事件。 我读到了类似于复选框列的问题,但显然建议使用模板列。 我尝试使用按钮的PreviewMouseDown-事件,它有效,但并非我想要的,因为按钮无法遵循其常规的图形单击行为。
我错过了什么?如何使单击事件只需单击一次即可获取,无论是否选择了行?
6个回答

8
基本上除了使用TemplateColumn并自行处理每个鼠标事件之外,您没有其他解决方案。
解释:
点击 = 鼠标按下 + MouseUp,对吧。 因此,您的按钮需要能够获取MouseDown + MouseUp事件。
但是......
默认情况下,wpf的DataGrid有其行处理MouseDown事件以选择你在MouseDown上的那一行(确认:在单元格上MouseDown并按住鼠标按钮,您会看到在释放按钮之前选择了该行)。
因此,基本上在到达按钮之前就已经处理了MouseDown事件,这将阻止您能够在按钮上使用Click事件。
微软在他们的文档中告诉我们,在这种情况下,我们应该转向预览类型的事件,但是这不能应用于click事件,因为您无法拥有预览Click事件。
因此,我唯一能看到的解决方案是在您的按钮上同时侦听PreviewMouseDown和PreviewMouseUp,并自己模拟单击事件。
像这样的东西:
Button myButton = new Button();
bool mouseLeftButtonDownOnMyButton;
myButton.PreviewMouseLeftButtonDown += (s, e) => { mouseLeftButtonDownOnMyButton = true; };
myButton.PreviewMouseLeftButtonUp += (s, e) => {
    if (mouseLeftButtonDownOnMyButton)
        myButton.RaiseEvent( new RoutedEventArgs(Button.ClickEvent,myButton));
        mouseLeftButtonDownOnMyButton = false;
    };
myButton.Click += myButtonCLickHandler;

(当然,您需要在xaml模板中翻译)

NB:这不是完整的代码,您还需要处理这样一种情况:用户在按下按钮时单击鼠标,但在释放鼠标之前将鼠标移出了按钮(在这种情况下,您应该重置mouseLeftButtonDownOnMyButton标志)。最好的方法可能是在通用的鼠标抬起事件(例如在窗口级别上)中重置标志,而不是在按钮事件中重置标志。

编辑:上面的代码还允许您管理Click事件,并且只有一个代码可以处理真实和模拟的点击事件(因此使用了RaiseEvent方法),但如果您不需要此功能,则当然也可以直接在PreviewMouseUp部分中放置您的代码。


1

另一个解决方案是创建自己的按钮。其中一个优点是:您不必为每个按钮连接事件。

public class FireOnPreviewButton : Button
{

    #region Constructor

    public FireOnPreviewButton()
    {
        PreviewMouseLeftButtonDown += OnLeftMouseButtonDownPreview;
    }

    #endregion

    #region Event Handler

    private void OnLeftMouseButtonDownPreview(object sender, MouseButtonEventArgs e)
    {
        // Prevent the event from going further
        e.Handled = true;

        // Invoke a click event on the button
        var peer = new ButtonAutomationPeer(this);
        var invokeProv = peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider;
        if (invokeProv != null) invokeProv.Invoke();
    }

    #endregion

}

1

我知道这个问题有点老,但是我签了一个使用WPF和XAML的10年合同,我遇到了相同的问题,用户必须点击数据网格中的一行两次才能激活鼠标按下事件。这是因为数据网格消耗第一次点击来选择行,并消耗第二次点击来触发事件。我找到了一个更简单的解决方案,适用于我的应用程序:

        PreviewMouseLeftButtonDown="MainDataGrid_OnMouseLeftButtonDown"

这将在第一次点击时触发鼠标左键按下事件。在数据网格设置中,它的样子是这样的:
     <DataGrid Name="MainDataGrid" IsSynchronizedWithCurrentItem="True" SelectionMode="Extended" SelectionUnit="FullRow" 
         ...removed other options for brevity
         PreviewMouseLeftButtonDown="MainDataGrid_OnMouseLeftButtonDown"
         ...removed other options for brevity
         RowHeight="30" BorderThickness="0" Background="#99F0F0F0"  Grid.Column="{Binding GridColumn, Mode=TwoWay}" Grid.Row="1">

我使用了这个而不是MouseLeftButtonDown。谢谢。


0

这可能不是您的选择,但将 IsReadonly=true 设置为 true 可以解决我的问题。


0

我曾经遇到过同样的问题 - 我在模板列上使用了图像,并在MouseDown事件上进行了处理,但是我必须点击两次。所以我在构造函数中调用了事件处理程序,这对我很有效。

你可以尝试这个方法:

constructor() {
    datagridname.AddHandler(Button.ClickEvent, new RoutedEventHandler(Button_Click), true);
}

0

现在已经晚了,但对其他人来说并非如此。

我一直面临着同样的问题,并花了近6个小时来寻找解决方法。我不希望别人再浪费时间。在查看了所有可能的答案后。这是我的解决办法:

我将一个方法绑定到主/父 DataGrid 的 Loaded 事件上。方法的定义如下:

private void FrameworkElement_OnLoaded(object sender, RoutedEventArgs e)
{
    var grid = sender as DataGrid;
    if (grid != null && grid.CurrentItem == null && grid.SelectedItem != null)
    {
        grid.CurrentItem = grid.SelectedItem;
    }
    else if (grid != null) //Fix when IsSynchronizedWithCurrentItem=True
    {
        var selectedItem = grid.SelectedItem;
        grid.CurrentItem = null;
        grid.SelectedItem = null;
        grid.CurrentItem = selectedItem;
        grid.SelectedItem = selectedItem;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接