在 WPF 中,使用 Popup 控件,能夠設置 StaysOpen 屬性來控制是否在 Popup 失去焦點時,也就是點擊界面空白處,自動收起 Popup 控件。但若是有兩個窗口,在設置 Popup 控件的 StaysOpen 屬性爲 false 那麼將會吃掉在點擊其餘窗口的第一次交互,如鼠標點擊或觸摸點擊時將不會讓本進程的其餘窗口 Activate 激活php
在 WPF 中,經過 Popup 控件能夠方便設置浮出的窗口,本質上 Popup 控件也是一個窗口,只是這是一個特殊的窗口。可是在使用 Popup 控件時,若是經過設置 Popup 控件的 StaysOpen 屬性爲 false 的方式讓 Popup 在點擊非 Popup 範圍內,包括點擊窗口其餘空白部分,或者點擊其餘應用程序或桌面等,自動收起。那麼 Popup 將會在點擊本進程內的其餘窗口時,點擊的交互被 Popup 吃掉,而讓其餘窗口收不到一次交互git
行爲以下:github
假定有兩個窗口,其中一個是 MainWindows 主窗口,另外一個是用來承載 Popup 的 Window1 窗口。 其中 Windows1 窗口有一個按鈕,點擊按鈕時將會彈出一個 Popup 控件,代碼過於簡單,我就不將全部代碼所有寫在博客。全部代碼放在 github 和 gitee 歡迎小夥伴訪問less
如下是 Windows1 的界面,有一個按鈕,和一個 Popup 控件,點擊按鈕自動彈出 Popup 控件ide
<Grid> <Button x:Name="OpenPopupButton" HorizontalAlignment="Center" VerticalAlignment="Center" Content="Open Popup" Click="OpenPopupButton_OnClick"></Button> <Popup x:Name="Popup" StaysOpen="False" PlacementTarget="{x:Reference OpenPopupButton}"> <Grid Background="Gray" Width="100" Height="100"> <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">The popup</TextBlock> </Grid> </Popup> </Grid>
如下是 Windows1 點擊按鈕的代碼工具
private void OpenPopupButton_OnClick(object sender, RoutedEventArgs e) { Popup.IsOpen = true; }
在 MainWindow 裏,在 Loaded 事件裏面彈出 Windows1 請看代碼ui
public MainWindow() { InitializeComponent(); Loaded += MainWindow_Loaded; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { var window1 = new Window1(); window1.Show(); }
請運行代碼,能夠看到打開兩個窗口,此時若是點擊 MainWindows 那麼可讓 MainWindows 獲取焦點。接下來請點擊 Window1 的空白,而後點擊 Open Popup
按鈕,此時將會彈出 Popup 控件。再點擊 MainWindows 的空白,能夠看到 MainWindows 只是獲取到鼠標按下和擡起事件,可是沒有被激活沒有獲取到焦點,依然焦點是 Windows1 窗口this
在 MainWindows 上添加一些代碼,這樣能夠方便在 VisualStudio 的輸出窗口裏面,看到窗口的各個事件rest
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); Loaded += MainWindow_Loaded; MouseDown += MainWindow_MouseDown; MouseUp += MainWindow_MouseUp; Activated += MainWindow_Activated; Deactivated += MainWindow_Deactivated; LostFocus += MainWindow_LostFocus; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { var window1 = new Window1(); window1.Show(); } private void MainWindow_MouseUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine($"MainWindow_MouseUp"); } private void MainWindow_MouseDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine($"MainWindow_MouseDown"); } private void MainWindow_Activated(object sender, EventArgs e) { Debug.WriteLine($"MainWindow_Activated"); } private void MainWindow_Deactivated(object sender, EventArgs e) { Debug.WriteLine($"MainWindow_Deactivated"); } private void MainWindow_LostFocus(object sender, RoutedEventArgs e) { Debug.WriteLine($"MainWindow_LostFocus"); } }
下面來執行如下兩個不一樣的動做,瞭解一下彈出 Popup 對進程內的其餘窗口的行爲code
動做1的步驟:
此時能夠看到 VisualStudio 輸出的內容以下
MainWindow_Activated MainWindow_Deactivated MainWindow_Activated MainWindow_MouseDown MainWindow_MouseUp
第一次 MainWindow_Activated 和 MainWindow_Deactivated 是在 MainWindows 的 Loaded 彈出 Window1 而激活和失去焦點的
第二次的 MainWindow_Activated 和鼠標按下和擡起是在點擊 MainWindow 的空白,這是符合預期的
動做2的步驟:
此時能夠看到 VisualStudio 輸出的內容以下
MainWindow_Activated MainWindow_Deactivated MainWindow_MouseDown MainWindow_MouseUp
對比能夠了解,在點擊 Window1 的 Open Popup 按鈕彈出 Popup 控件以後,下一次點擊 MainWindow 是不會激活 MainWindow 只是收到鼠標的按下和擡起
那爲何 Popup 會影響進程的其餘窗口的行爲?下面來閱讀 Popup 的源代碼
在 Popup 的 OnLostMouseCapture 方法裏面,觸發的定義以下
static Popup() { EventManager.RegisterClassHandler(typeof(Popup), Mouse.LostMouseCaptureEvent, new MouseEventHandler(OnLostMouseCapture)); // 忽略其餘代碼 } private static void OnLostMouseCapture(object sender, MouseEventArgs e) { Popup popup = sender as Popup; if (!popup.StaysOpen) { PopupRoot root = popup._popupRoot.Value; // Reestablish capture if an element within us lost capture // (hence we receive the LostCapture routed event) and capture // is not being acquired anywhere else. // // Note we do not reestablish capture if we are losing capture // ourselves. bool reestablishCapture = e.OriginalSource != root && Mouse.Captured == null && MS.Win32.SafeNativeMethods.GetCapture() == IntPtr.Zero; if(reestablishCapture) { popup.EstablishPopupCapture(); e.Handled = true; } else { // 忽略其餘代碼 } } }
在點擊 MainWindow 的空白,將會觸發到 Popup 的 OnLostMouseCapture 方法,接着進入 EstablishPopupCapture 方法
private void EstablishPopupCapture(bool isRestoringCapture=false) { if (!_cacheValid[(int)CacheBits.CaptureEngaged] && (_popupRoot.Value != null) && (!StaysOpen)) { IInputElement capturedElement = Mouse.Captured; PopupRoot parentPopupRoot = capturedElement as PopupRoot; if (parentPopupRoot != null) { if (isRestoringCapture) { // if the other PopupRoot is restoring capture back to this // popup, ignore mouse button events until both buttons have been // released. Otherwise a mouse click outside a chain of // "nested" popups would dismiss two of them - one on MouseDown // and another on MouseUp. if (Mouse.LeftButton != MouseButtonState.Released || Mouse.RightButton != MouseButtonState.Released) { _cacheValid[(int)CacheBits.IsIgnoringMouseEvents] = true; } } else { // this is a "nested" popup, invoked while another popup is open. // We need to restore capture to the previous popup root when // we're done ParentPopupRootField.SetValue(this, parentPopupRoot); } // in either case, taking capture away from the other PopupRoot is OK. capturedElement = null; } if (capturedElement == null) { // When the mouse is not already captured, we will consider the following: // In all cases but Modeless, we want the popup and subtree to receive // mouse events and prevent other elements from receiving those messages. Mouse.Capture(_popupRoot.Value, CaptureMode.SubTree); _cacheValid[(int)CacheBits.CaptureEngaged] = true; } } }
在 EstablishPopupCapture 方法裏面從新調用了 Mouse.Capture 將會讓本進程內的其餘窗口沒有被激活
以上是大琛告訴個人,我只是記錄的工具人
我搭建了本身的博客 https://blog.lindexi.com/ 歡迎你們訪問,裏面有不少新的博客。只有在我看到博客寫成熟以後纔會放在csdn或博客園,可是一旦發佈了就再也不更新
若是在博客看到有任何不懂的,歡迎交流,我搭建了 dotnet 職業技術學院 歡迎你們加入
本做品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、從新發布,但務必保留文章署名林德熙,不得用於商業目的,基於本文修改後的做品務必以相同的許可發佈。若有任何疑問,請與我聯繫。