要用popup控件来解决一些问题。就此带来了一批问题。

问题一、 在popup外任意位置点击时要能关闭popup,这个本来简单,只要加上StaysOpen=false就可以了。但我的popup中有个OpenFileDialog控件,这个控件窗口一打开,popup就被关闭了。

解决:

在btnOpenFile按钮的PreviewMouseLeftButtonDown事件里添加了

((Popup)((FrameworkElement)this.Parent).Parent).StaysOpen = true;

 

问题二、 StaysOpen设置为True后,点击按钮变成了这样。popup是TopMost的,挡在了文件窗口前面。

解决:

无法简单解决这个问题,只好采用github上的一个NonTopmostPopup自定义控件取代Popup。

为了在关闭文件选择窗口后popup仍然能够在鼠标点击App任意位置时被关闭,在btnOpenFile按钮的Click事件里添加了

((Popup)((FrameworkElement)this.Parent).Parent).StaysOpen = false;

 

问题三、上述做法的结果是,如果双击文件名时的鼠标位置在popup区域以外,popup还是会直接关闭。因为双击位置的主窗口控件虽然被文件对话框遮挡,但仍会触发MouseLeftButtonUp(或MouseUp)事件,这可能算是文件对话框的一个bug。我猜是因为双击实际上是在第二次点击的MouseDown事件发生后就生效,对话框就关闭了,再往后还有MouseUp事件,却已经处于没有遮挡的裸奔状态了,当然控件们纷纷响应。

没办法,想workaround吧。我的做法是取消按钮Click事件StaysOpen=false,改在主窗口的MouseLeftButtonDown中加下面的代码遍历控件,遇到popup就设置StaysOpen=false。

复制代码
        private void TheShell_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            FindPopupAndClose(TheShell);
        }

        // Enumerate all the descendants of the visual object.
        static public void FindPopupAndClose(Visual myVisual)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)
            {
                // Retrieve child visual at specified index value.
                Visual childVisual = (Visual)VisualTreeHelper.GetChild(myVisual, i);

                // Do processing of the child visual object.
                if (childVisual is Popup)
                    ((Popup)childVisual).StaysOpen = false;
                // Enumerate children of the child visual object.
                FindPopupAndClose(childVisual);
            }
        }
复制代码

问题四、一些情况下,我需要popup StaysOpen=false,并且能跟随主窗口移动而移动(默认情况popup是不动的)。

解决:

参考StackOverFlow上的帖子,在codebehind加下面事件:

复制代码
public partial class View1 : UserControl
{
    // Constructor
    public View1()
    {
        InitializeComponent();

        // Window.GetWindow() will return Null if you try to call it here!             

        // Wire up the Loaded handler instead
        this.Loaded += new RoutedEventHandler(View1_Loaded);
    }

    /// Provides a way to "dock" the Popup control to the Window
    ///  so that the popup "sticks" to the window while the window is dragged around.
    void View1_Loaded(object sender, RoutedEventArgs e)
    {
        Window w = Window.GetWindow(popupTarget);
        // w should not be Null now!
        if (null != w)
        {
            w.LocationChanged += delegate(object sender2, EventArgs args)
            {
                var offset = myPopup.HorizontalOffset;
                // "bump" the offset to cause the popup to reposition itself
                //   on its own
                myPopup.HorizontalOffset = offset + 1;
                myPopup.HorizontalOffset = offset;
            };
            // Also handle the window being resized (so the popup's position stays
            //  relative to its target element if the target element moves upon 
            //  window resize)
            w.SizeChanged += delegate(object sender3, SizeChangedEventArgs e2)
            {
                var offset = myPopup.HorizontalOffset;
                myPopup.HorizontalOffset = offset + 1;
                myPopup.HorizontalOffset = offset;
            };
        }
    }
}