Search code examples
c#xamluwpflickerflyout

UWP - PointerEntered/PointerExited to simulate mousehover on grid rows


I have already checked Simulating a mousehover effect on a fontIcon in uwp. But I am facing a different, "flickering" issue.

I have a Grid inside a ScrollViewer, which is a child of a PivotItem control. The grid is empty at the beginning and then programmatically populated.

<PivotItem>
    <ScrollViewer x:Name="MyScrollBar" >
        <Grid Name="MyGrid">
            <Grid.RowDefinitions>
                 <RowDefinition Height="Auto"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                 <ColumnDefinition Width="*"></ColumnDefinition>
                 <ColumnDefinition Width="*"></ColumnDefinition>
                 <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
         </Grid> 
    </ScrollViewer>
</PivotItem>

The columns are then filled with 3 TextBlocks each. My goal is to show a Flyout with additional details when hovering over a row (not a single TextBlock). To achieve this, I define for each row an additional transparent Rectangle whose ColumnSpan is 3, and add it as the last child of each row. I then give it a Flyout as follows:

Rectangle rect = new Rectangle();
rect.Opacity = 0;
rect.SetValue(Grid.RowProperty, r);
rect.SetValue(Grid.ColumnSpanProperty, 3);
Flyout fl = new Flyout();
Grid flGrid = new Grid();
TextBlock flTb1 = new TextBlock();
flTb1.Text = details.Name;
flGrid.Children.Add(flTb1);
fl.Content = flGrid;
rect.SetValue(FlyoutBase.AttachedFlyoutProperty, fl);
rect.PointerEntered += Rect_PointerEntered;
rect.PointerExited += Rect_PointerExited;
rect.Margin = new Thickness(2);

Here the PointerEntered and PointerExited event handlers:

private void Rect_PointerEntered(object sender, PointerRoutedEventArgs e)
{
    ((Flyout)(((Rectangle)sender).GetValue(FlyoutBase.AttachedFlyoutProperty))).ShowAt((Rectangle)sender);
}

private void Rect_PointerExited(object sender, PointerRoutedEventArgs e)
{
    ((Flyout)(((Rectangle)sender).GetValue(FlyoutBase.AttachedFlyoutProperty))).Hide();
}

As the pointer enters the hit zone of a grid row, suddenly the Flyout appears and disappears. And so it does whenever the pointer moves, even over the same row (I don't want the Flyout to disappear when moving the pointer over the same row). The result is a flickering flyout. It seemes to me that the PointerEntered/PointerExited events are both fired at any point hovered.

What I have already tried:

  1. Handle the PointerEntered only (I know this is not best practice, but I tried)
  2. Set PointerEntered.Handled to false (I thought that maybe the other controls in the grid were impacting the behavior)
  3. Handle the PointerMoved only
  4. Stored the "hovered" and "hovering" Rectangle(s) to recognize when the pointer is above the same/another row
  5. A lot of other attempts I don't even remember... always getting the same flickering results.

Could anyone point me in the right direction? Thank you in advance!


Solution

  • It seemes to me that the PointerEntered/PointerExited events are both fired at any point hovered.

    It seems like once the Flyout appears the PointerExited will triggered even the mouse didn't leave the hit test area. Without the Flyout the Pointer events can work as you thought about, for this you can test the official sample.

    Handle the PointerEntered only (I know this is not best practice, but I tried)

    This way should be able worked since without PointerExited event the Flyout will not be hidden. I also tested it the Flyout will not disappear until click on another space. But as you said this may not be a good practice.

     private void Rect_PointerEntered(object sender, PointerRoutedEventArgs e)
     {
        ((Flyout)(((Rectangle)sender).GetValue(FlyoutBase.AttachedFlyoutProperty))).ShowAt((Rectangle)sender);
     }
    
     private void Rect_PointerExited(object sender, PointerRoutedEventArgs e)
     {
         //((Flyout)(((Rectangle)sender).GetValue(FlyoutBase.AttachedFlyoutProperty))).Hide();
     }
    

    A another way you can try to use ToolTip. You even don't need Pointer events, when you hovering the row, it will show, and when you move out it will disappear. It will not be flickering. Although after a time it will disappear but I think it is enough for you showing details.

     Rectangle rect = new Rectangle();
     rect.Opacity = 0.3;
     rect.SetValue(Grid.RowProperty, r);
     rect.SetValue(Grid.ColumnSpanProperty, 3);
     rect.Fill = new SolidColorBrush(Colors.Azure);  
     Grid flGrid = new Grid();
     TextBlock flTb1 = new TextBlock();
     flTb1.Text = "testname"+r;
     flGrid.Children.Add(flTb1); 
     rect.SetValue(ToolTipService.ToolTipProperty, flGrid); 
     rect.Margin = new Thickness(2);
     MyGrid.Children.Add(rect);