Home > Enterprise >  How to allow an event to bubble
How to allow an event to bubble

Time:10-03

I have a ToggleButton that I have set the template to a new ControlTemplate I have a behavior that I have added to this ToggleButton.

   <ControlTemplate TargetType="ToggleButton" x:Key="IconToggleButton">
        <Border
            Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}"
            CornerRadius="15 0 0 15"
            Height="{TemplateBinding Height}"
            MinHeight="{TemplateBinding MinHeight}"
            VerticalAlignment="{TemplateBinding VerticalAlignment}"
            Width="{TemplateBinding Width}">
            <Grid>
                <TextBlock
                    FontFamily="{TemplateBinding FontFamily}"
                    Foreground="{StaticResource White}"
                    HorizontalAlignment="Center"
                    Text="{TemplateBinding Content}"
                    VerticalAlignment="Center" />
            </Grid>
        </Border>
    </ControlTemplate>

   <ToggleButton
        Background="{StaticResource LightGray}"
        BorderBrush="{StaticResource LightGray}"
        BorderThickness="1"
        Content="{x:Static constants:FontAwesomeIcons.Bars}"
        FontFamily="{StaticResource FontAwesomeSolid}"
        FontSize="19"
        HorizontalAlignment="Center"
        MinHeight="35"
        MouseLeftButtonDown="UIElement_OnMouseDown"
        Template="{StaticResource IconToggleButton}"
        VerticalAlignment="Top"
        Width="38"
        x:Name="ToggleButton">
        <b:Interaction.Behaviors>
            <b:MouseDragElementBehavior ConstrainToParentBounds="True" />
        </b:Interaction.Behaviors>
    </ToggleButton>



    private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
    {
        Debug.WriteLine(sender.GetType().Name);
        e.Handled = false;
     }

My issue is that because the ToggleButton is handling the event The behavior actually never fires.

enter image description here

So, on to my question: How can I get the behavior to catch this MouseLeftButtonDownEvent.

CodePudding user response:

The MouseDragElementBehavior does handle the bubbling version of the mouse input events. And those events are marked as handled by the Button as this event is replaced with the Button.Click event. This means the UIElement.MouseLeftButtonDown and the UIElement.MouseDown events are swallowed by the Button. Only the tunneling versions of those events are allowed to traverse.

To enable drag for a Button, or any control that marks the bubbling mouse events as handled, with the help of the MouseDragElementBehavior, you must control this behavior manually.

To achieve this, register a handler for the tunneling UIElement.PreviewMouseLeftButtonDown event:

<ToggleButton PreviewMouseLeftButtonDown="OnDraggableElementLeftButtonDown">
  <b:Interaction.Behaviors>
    <b:MouseDragElementBehavior ConstrainToParentBounds="True" />
  </b:Interaction.Behaviors>
</ToggleButton>
private Point DragStartPosition { get; set; }

private void OnDraggableElementLeftButtonDown(object sender, MouseButtonEventArgs e)
{
  var draggableElement = sender as UIElement;

  this.DragStartPosition = e.GetPosition(this);
  draggableElement.PreviewMouseMove  = OnDraggingElementMouseMove;
  draggableElement.PreviewMouseLeftButtonUp  = OnDraggedElementLeftButtonUp;
}

protected void OnDraggingElementMouseMove(object sender,  MouseEventArgs e)
{
  var draggingElement = sender as UIElement;

  Microsoft.Xaml.Behaviors.Layout.MouseDragElementBehavior dragMoveBehavior = Microsoft.Xaml.Behaviors.Interaction.GetBehaviors(draggingElement)
    .OfType<Microsoft.Xaml.Behaviors.Layout.MouseDragElementBehavior>()
    .First();
  Point initialDraggableElementPosition = draggingElement.TranslatePoint(new Point(0, 0), this);
  if (double.IsNaN(dragMoveBehavior.X))
  {
    dragMoveBehavior.X = initialDraggableElementPosition.X;
  }

  if (double.IsNaN(dragMoveBehavior.Y))
  {
    dragMoveBehavior.Y = initialDraggableElementPosition.Y;
  }

  Point mousePosition = e.GetPosition(this);
  dragMoveBehavior.X  = mousePosition.X - this.DragStartPosition.X;
  dragMoveBehavior.Y  = mousePosition.Y - this.DragStartPosition.Y;

  this.DragStartPosition = new Point(mousePosition.X, mousePosition.Y);
}

private void OnDraggedElementLeftButtonUp(object sender, MouseButtonEventArgs e)
{
  var draggedElement = sender as UIElement;

  draggedElement.PreviewMouseMove -= OnDraggingElementMouseMove;
  draggedElement.PreviewMouseLeftButtonUp -= OnDraggedElementLeftButtonUp;
}

To add convenience, you probably would like to wrap the MouseDragElementBehavior into a custom Behavior or attached behavior. You then use this behavior to enable element drag. This custom behavior will internally delegate the drag operation to the wrapped MouseDragElementBehavior using the above code.

CodePudding user response:

If correctly understood, then you need a subscription to has handled event. In XAML (by default), you cannot make such a subscription. Need to use Sharp code in Behavior or Code Behind. Example:

    public MainWindow()
    {
        InitializeComponent();

        bttn.AddHandler(
            UIElement.MouseLeftButtonDownEvent,
            (MouseButtonEventHandler)OnMouseLeftButtonDownForHandled,
            true); // "True" - for "catching" all events, including handled ones.
        }

    private void onm ouseLeftButtonDownForHandled(object sender, MouseButtonEventArgs e)
    {
        Debug.WriteLine($"The \"{e.RoutedEvent}\" happened");
    }
  • Related