wpf popup doesn't close automatically when datagrid inside popup captures the mouse

2.9k Views Asked by At

I have a popup with StaysOpen=False so I want to close it by clicking anywhere outside of popup. Inside a popup I have a DataGrid. If I open popup and then click somewhere else the popup will be closed. But it won't happen if before clicking outside of popup I will click on column header in DataGrid. Test XAML:

<Window x:Class="Test.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="Black">
<Grid>
    <ToggleButton x:Name="btn" VerticalAlignment="Top">Open</ToggleButton>
    <Popup StaysOpen="False" IsOpen="{Binding IsChecked, ElementName=btn}" > 
        <DataGrid Width="150" Height="150">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Column" />
            </DataGrid.Columns>
        </DataGrid>
    </Popup>
</Grid>
</Window>

I think that it happens because column header captures the mouse on click and popup doesn't receive mouse events anymore. I've tried to add a handler on LostMouseCapture event in order to capture mouse back by popup but it doesn't seem to work that easy. Any ideas?

4

There are 4 best solutions below

0
On

I think you've stumbled onto just a plain old bug. I've reproduced this and could not find a reasonable way to get it working. I think you should file a bug with Microsoft. It seems like a component that captures the mouse and the uncaptures it doesn't restore the capture to the originally capturing component.

0
On

I had a similar problem recently altough not exactly the same, and it was in Silverlight. I hacked my way through it by searching the required control (in your case the popup I guess) with the GetTemplatedParent function, in the required event handler of the 'misbehaving' control, and programatically do what I wanted to do on it.

This is not a nice solution, and doesn't solve all the problems, but you can give it a try. Be sure you comment what have you done, because it can turn into a mess.

0
On

i had the same problem,and did something like this:

 private void YourDataGrid_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
         YourDataGrid.CaptureMouse();
         YourDataGrid.ReleaseMouseCapture();
    }

but i'm looking for something better yet...

0
On

Maybe it will help. Attached behavior:

public class DataGridColumnHeaderReleaseMouseCaptureBehavior {
    public static DataGrid GetReleaseDGCHeaderBehavior(DependencyObject obj) {
        return (DataGrid)obj.GetValue(ReleaseDGCHeaderBehaviorProperty);
    }

    public static void SetReleaseDGCHeaderBehavior(DependencyObject obj, Boolean value) {
        obj.SetValue(ReleaseDGCHeaderBehaviorProperty, value);
    }

    public static readonly DependencyProperty ReleaseDGCHeaderBehaviorProperty =
        DependencyProperty.RegisterAttached("ReleaseDGCHeaderBehavior",
            typeof(DataGrid),
            typeof(DataGridColumnHeaderReleaseMouseCaptureBehavior),
            new UIPropertyMetadata(default(DataGrid), OnReleaseDGCHeaderBehaviorPropertyChanged));

    private static Popup _popup;

    private static void OnReleaseDGCHeaderBehaviorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        var oldGrid = (DataGrid)e.OldValue;
        if (oldGrid != null)
            oldGrid.MouseLeave -= OnMouseLeave;
        var refSender = d as Popup;
        _popup = refSender;
        if (refSender != null) {
            var refGrid = e.NewValue as DataGrid;
            if (refGrid != null) {
                refGrid.MouseLeave += OnMouseLeave;
            }
        }
    }
    static void OnMouseLeave(object sender, MouseEventArgs args) {
        if (_popup != null)
            typeof(Popup).GetMethod("EstablishPopupCapture", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Invoke(_popup, null);
    }
}

XAML:

<Popup x:Name="popup"
bhvrs:DataGridColumnHeaderReleaseMouseCaptureBehavior.ReleaseDGCHeaderBehavior="{Binding ElementName=dataGrid}">
  <DataGrid x:Name="dataGrid"/>
</Popup>