quadroid
quadroid

Reputation: 8940

Popup inside ToolBarOverflow Panel does not work properly

I have a ToolBar in my application which causes Problems. I have "DropDown" Buttons inside the ToolBar (ToggleButton + Popup) Those DropDowns work properly if they are on the Visible Part of the ToolBar, they do not work properly if they are located in the ToolBarOverflowPanel.

If i Open a DropDown in the ToolBarOverflowPanel the Popup does not seem to receive focus. There are still hover effects (opposing to the behaviour of the same Popup in the visible toolbar which seems to consume all Mouse Events) and i can still click any other DropDown which opens the next Popup while the initial one stays open.

Here you can see the wrong behaviour

The following code is a full working sample to reproduce the behaviour.

<Window x:Class="ToolbarProblem.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="350"
    Width="525">
<StackPanel>
    <ToolBar ItemsSource="{Binding Items}">
        <ToolBar.ItemTemplate>
            <DataTemplate >
                <StackPanel VerticalAlignment="Center"
                            Orientation="Horizontal">
                    <ToggleButton Name="ToggleButton"
                                  ToolTip="ToolTip"
                                  IsChecked="{Binding ElementName=ContextActionPopup, Path=IsOpen, Mode=TwoWay}"
                                  ClickMode="Release">
                        <TextBlock Text="ICON"/>
                    </ToggleButton>
                    <Popup Name="ContextActionPopup"
                           StaysOpen="False">
                        <Border x:Name="Border" 
                                Background="White"
                                Padding="1"
                                Visibility="Visible">
                            <TextBlock Text="Content" />
                        </Border>
                    </Popup>
                </StackPanel>
            </DataTemplate>
        </ToolBar.ItemTemplate>
    </ToolBar>
</StackPanel>

namespace ToolbarProblem
{
    using System.Collections.Generic;

    public partial class MainWindow
    {
        public MainWindow()
        {
            this.InitializeComponent();
            this.DataContext = new MainWindowViewModel();
        }
    }

    public class MainWindowViewModel
    {
        public List<object> Items { get; } = new List<object>
        {
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object(),
            new object()
        };
    }
}

I did tried the following approaches without any success:

Upvotes: 0

Views: 405

Answers (1)

Il Vic
Il Vic

Reputation: 5666

I am afraid that your XAML will never work properly. You can find the reason in the Popup control code. If you set the StaysOpen property to false, the Popup when opening, will call this private method (just use ILSpy to inspect):

private void EstablishPopupCapture()
{
    if (!this._cacheValid[1] && this._popupRoot.Value != null && !this.StaysOpen && Mouse.Captured == null)
    {
        Mouse.Capture(this._popupRoot.Value, CaptureMode.SubTree);
        this._cacheValid[1] = true;
    }
}

So if there is no other control that is capturing mouse events (i.e. Mouse.Captured == null) your popup will capture them

to determine when one of these events occurs outside the Popup control.

as MSDN remarks. Please, note that Capture method and Captured properties are static, so just one control at a time can capture by using Mouse class.

Now just take a look to the Toolbar default style: you will find that its Popup control, named "OverflowPopup", has its StaysOpen property set to false.

So when you click on the overflow thumb, the "OverflowPopup" calls the EstablishPopupCapture while it is opening. In this case Mouse.Captured is null so it can capture mouse events. After a while you click on a ToggleButton which is inside the "OverflowPopup" (so it will continue to stay open). Now your Popup - while opening - calls EstablishPopupCapture, but this time Mouse.Captured is not null. So it is not able to listen to mouse events by using Mouse class.

I guess you should consider changing your XAML or adopting a different solution (for example you can write your own template for your Toolbars in order to set OverflowPopup's StaysOpen property to true).

This is a simplified explanation, but the fact of the matter is that it is not possible to have two or more opened popups with StaysOpen set to false: they simply cannot work as expected. Just try to run this XAML:

<StackPanel Orientation="Horizontal">
    <Button Content="I am button 1" Name="btn1" Height="20" />
    <Button Content="I am button 2" Name="btn2" Height="20" />
    <Popup StaysOpen="False" IsOpen="True" PlacementTarget="{Binding ElementName=btn1}">
        <TextBlock Text="Popup1" Margin="6" Background="Red" />
    </Popup>
    <Popup StaysOpen="False" IsOpen="True" PlacementTarget="{Binding ElementName=btn2}">
        <TextBlock Text="Popup2" Margin="6" Background="Red" />
    </Popup>
</StackPanel>

And you will see that Popup2 will not work as it should do.

Upvotes: 2

Related Questions