在 WPF Datagrid 中未选定的行中未触发单击事件

发布于 2024-12-20 14:03:21 字数 2116 浏览 4 评论 0原文

我有一个像这样的 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-Event ,它可以工作,但不是我想要的,因为那时按钮不遵循其通常的图形单击行为。

我在这里缺少什么?如何通过单击一次来获取单击事件,无论是否选择该行?

I have a DataGrid (WPF 4) like this:

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

The issue I experience is, that a button of the DataGridTemplateColumn does not fire the click-event, if its row is not selected. So I have to click a button twice, one time to select its row and then to raise the click event.
I read about similiar problems with checkbox-columns, but there the suggestion was obviously to use a template column.
I tested to use the PreviewMouseDown-Event of the button, which works, but is not what I want, since then the button does not follow its usual grpahical click behaviour.

What am I missing here? How can I get that click event by just clicking once, regardless of whether the row was selected or not?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

我是有多爱你 2024-12-27 14:03:21

基本上,除了使用 TemplateColumn 并自己管理每个 mouseEvent 之外,您没有其他解决方案。

解释:

click = mouseDown + MouseUp,正确。
所以你的按钮需要能够获取 mouseDown + MouseUp 事件。

但是...

默认情况下,wpf 的 DataGrid 的行处理 MouseDown 事件,以便选择您执行 mouseDown 的行(确认:mouseDown< /code> 在单元格上并按住 mouseButton,您将看到在释放按钮之前该行已被选中)。

因此基本上,MouseDownEvent 在到达按钮之前就被处理,从而阻止您在按钮上使用 Click 事件

Microsoft 在他们的文档中告诉我们,在这种情况下,我们应该转向 Preview 类型的事件,但这不适用于点击事件,因为您无法拥有 previewClickEvent

所以我能看到的唯一解决方案你要听双方的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:这并不完整,您还应该注意用户在按钮上执行 mouseDown 但在执行 mouseup 之前将鼠标移出按钮的情况(在这种情况下你应重置 mouseLeftButtonDownOnMyButton 标志)。最好的方法可能是在常规 mouseUpEvent(例如在窗口级别)而不是按钮的事件中重置标志。

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

basically, you have no solution except using a TemplateColumn and managing every single mouseEvent yourself.

Explanation :

click = mouseDown + MouseUp, right.
so your button needs to be able to get the mouseDown + the MouseUp event.

BUT...

by default, wpf's DataGrid has its rows handling the MouseDown Event so as to select the one you do the mouseDown on (to confirm: mouseDown on a cell and hold the mouseButton down, you'll see that the row is selected BEFORE you release the button).

So basically, the MouseDownEvent is Handled before it reaches the button, preventing you to be able to use the Click event on the button

Microsoft tell us in their doc that in such cases, we should turn to the Preview kind of event, but this cannot apply to click event since there is no way you could have a previewClickEvent

So the only solution I can see for you is to listen to both PreviewMouseDown and PreviewMouseUp on your button and simulating a click from them yourself

something a bit like this :

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;

(of course, you'd need to translate that in your xaml template)

NB: this is not complete, you should also take care of the cases when the user does a mouseDown on the button but moves the mouse out of the button before doing the mouseup (in wich case you should reset the mouseLeftButtonDownOnMyButton flag). The best way would probably be to reset the flag in a general mouseUpEvent (on the window level for instance) instead of in the button's one.

Edit: the above code lets you manage the Click event as well and have only one code for both the real and simulated click events (hence the use of the RaiseEvent method), but if you don't need this, you could directly put your code in the PreviewMouseUp section as well of course.

心头的小情儿 2024-12-27 14:03:21

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

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

}

Another solution would be to create your own button. One advantage of this: you do not have to hook up the events for every button.

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

}
寂寞花火° 2024-12-27 14:03:21

这可能不适合您,但设置 IsReadonly=true 为我解决了这个问题。

This may not be an option for you, but setting the to IsReadonly=true solved that issue for me.

瑾夏年华 2024-12-27 14:03:21

我意识到这个问题很老了,但我签订了一份使用 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。干杯。

I realize this question is old but I'm on a 10 year contract that uses WPF and XAML and I ran into the same issue where users had to click a row in the datagrid twice to activate the mouse button down event. This is because the datagrid consumes the first click to select the row and the second click to trigger the event. I found a much simpler solution that works for my application:

        PreviewMouseLeftButtonDown="MainDataGrid_OnMouseLeftButtonDown"

This triggers the mouse left button down event on the first click. This is how it looks in the datagrid setup:

     <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">

I used that instead of MouseLeftButtonDown. Cheers.

丢了幸福的猪 2024-12-27 14:03:21

我遇到了同样的问题 - 我在模板列上使用图像并在 MouseDown 上发生了事件,但我必须单击两次。所以我在构造函数上调用了事件处理程序,它对我有用。

你可以试试这个:

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

I had the same issue - I was using Image on Template Column and had event on MouseDown, but I had to click twice. So I called event handler on constructor and it worked for me.

You can try this:

constructor() {
    datagridname.AddHandler(Button.ClickEvent, new RoutedEventHandler(Button_Click), true);
}
余厌 2024-12-27 14:03:21

为时已晚,但对其他人来说却不然。

我也遇到了同样的问题,花了近 6 个小时才找到解决方法。我不想让别人再浪费时间。看完所有可能的答案后。这就是我修复它的方法:

我将一个方法绑定到 ma​​in/parent 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;
    }
}

It's too late but not for others.

I have been facing the same issue and spent almost 6 hours to find a workaround. I don't want someone else to waste their time again. After seeing all the possible answers. This is how I fixed it:

I bounded a method to the main/parent DataGrid's Loaded event. Method definition is:

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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文