7
votes

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?

6

6 Answers

8
votes

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.

0
votes

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);
}
0
votes

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

}
0
votes

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

0
votes

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.

0
votes

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