WPF – Làm Sao Để Show Menu Mặc Định Của Hệ Thống Windows Tại Vị Trí Khác

SystemMenuPhysicalCoordinates

Tiêu đề có vẻ khó hình dung – thật sự tôi cũng không biết diễn đạt thế nào; hôm nay tôi sẽ mang đến cho các bạn một cách để giải quyết vấn đề mà tôi gặp phải và tôi nghĩ cũng nhiều người có cùng thắc mắc như tôi. Vấn đề nảy sinh nhiều khi bạn làm một ứng dụng WPF, bạn muốn làm một Window cho riêng mình, không có phần Border hay Style mặc định của Windows, bạn chọn cách ẩn hết những thứ mặc định đi sau đó thiết kế lại; và một số thứ bạn ẩn nó đi rồi không thể tìm thấy được khi cần thiết. Một trong những thứ mất đi nhưng cần thiết đó là một số SystemMenu, nó chính là phần menu ở góc trái trên, xuất hiện khi bạn nhấn vào logo/icon của window; Vậy làm thế nào hiện nó lại mà vẫn giữ được việc ẩn những thứ không cần thiết.

SystemMenuPhysical tuanphamdg


I> SystemMenu-Physical-Coordinates là gì?

Trong một số trường hợp SystemMenu ở góc 10h thật sự phát huy tác dụng và riêng tôi một phần mềm chuyên nghiệp không nên bỏ qua nó nếu đặc thù lĩnh vực vẫn cho phép. Đến với một ứng dụng WPF thông thường chúng ta sẽ thấy nó như thế này

SystemMenuPhysical tuanpham wwpf

Nhưng với tôi nó có vẻ không ổn, tôi muốn ẩn hết các đường Border mặc định kia đi, các Button góc 2h cũng không cần thiết vì Style này không đồng nhất với mẫu Design của tôi -> Tôi bỏ nó ra khỏi ứng dụng của mình và design thêm một chút
Window.XAML

<Window x:Class="ShowSystemMenuPhysicalCoordinates.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" 
        Icon="Home.ico" 
        WindowStartupLocation="CenterScreen" 
        WindowStyle="None" 
        AllowsTransparency="True">

<!-- Do something-->
</Window>

Và tôi được kết quả thế này

SystemMenuPhysical tuanpham wpf

Chú ý rằng cái logo kia chỉ là một Icon tôi vừa đặt nó ở đó, khi tôi Click chuột vào lẽ dĩ nhiên vẫn vắng bóng cái Menu thường thấy ở Windows. Nhưng tôi muốn hiển thị cái menu thường thấy ấy ngay dưới vị trí Icon khi tôi Click chuột; Đơn giản chúng ta chỉ cần bắc sự kiện Logo_OnMouseLeftButtonDown(sender, e).
Window.CS

 
private void Logo_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 
{ 
      // Sẽ xử lý việc hiện SysMenu ở đây 
} 

II> Cách lấy và Show SystemMenu Physical-Coordinates

Trước tiên chúng ta cần Import một số thư viện WinAPI cần thiết, tôi tạo Class sau
UnsafeNativeMethods.CS

internal class UnsafeNativeMethods
    {
        /// <devdoc>http://msdn.microsoft.com/en-us/library/windows/desktop/ms633528(v=vs.85).aspx</devdoc>
        [DllImport("user32", CharSet = CharSet.Auto, ExactSpelling = true)]
        // Kiểm tra một obj có phải là Windows?
        internal static extern bool IsWindow([In] [Optional] IntPtr hWnd);

        /// <devdoc>http://msdn.microsoft.com/en-us/library/windows/desktop/ms647985(v=vs.85).aspx</devdoc>
        [DllImport("user32")]
        // Get SysMenu mặc định
        internal static extern IntPtr GetSystemMenu([In] IntPtr hWnd, [In] bool bRevert);

        /// <devdoc>http://msdn.microsoft.com/en-us/library/windows/desktop/ms648003(v=vs.85).aspx</devdoc>
        [DllImport("user32")]
        // Show SysMenu trên Window
        internal static extern uint TrackPopupMenuEx([In] IntPtr hmenu, [In] uint fuFlags, [In] int x, [In] int y,
            [In] IntPtr hwnd, [In] [Optional] IntPtr lptpm);

        /// <devdoc>http://msdn.microsoft.com/en-us/library/windows/desktop/ms644944(v=vs.85).aspx</devdoc>
        [DllImport("user32", EntryPoint = "PostMessage", SetLastError = true)]
        // PostMessage cho hệ thống Windows về hành vi của chúng ta
        private static extern bool _PostMessage([In] [Optional] IntPtr hWnd, [In] uint Msg, [In] IntPtr wParam,
            [In] IntPtr lParam);

        internal static void PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam)
        {
            if (!_PostMessage(hWnd, Msg, wParam, lParam))
            {
                throw new Win32Exception();
            }
        }
    }

Xong phần NativeMethods chúng ta tạo static class sau
SystemMenuPhysicalCoordinates.CS

internal static class SystemMenuPhysicalCoordinates
    {
        internal const uint SYSCOMMAND = 0x0112;
        internal const uint TPM_LEFTBUTTON = 0x0;
        internal const uint TPM_RETURNCMD = 0x0100;

        /// <summary>
        /// Show SysMenu on Cureent Window
        /// </summary>
        /// <param name="window">Window bạn muốn hiển thị</param>
        /// <param name="physicalScreenLocation">Vị trí muốn hiển thị menu</param>
        internal static void Show(Window window, Point physicalScreenLocation)
        {
            if (window == null) return;

            IntPtr hwnd = new WindowInteropHelper(window).Handle;
            if (hwnd == IntPtr.Zero || !UnsafeNativeMethods.IsWindow(hwnd))
                return;

            // Get Menu
            IntPtr hmenu = UnsafeNativeMethods.GetSystemMenu(hwnd, false);

            // Show menu
            uint cmd = UnsafeNativeMethods.TrackPopupMenuEx(hmenu, TPM_LEFTBUTTON | TPM_RETURNCMD,
                (int) physicalScreenLocation.X, (int) physicalScreenLocation.Y, hwnd, IntPtr.Zero);
            if (0 != cmd)
                UnsafeNativeMethods.PostMessage(hwnd, SYSCOMMAND, new IntPtr(cmd), IntPtr.Zero);
        }
    }

Thế là xong phần cở bản, chúng ta quay lại Handle cho Logo_OnMouseLeftButtonDown

private void Logo_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            SystemMenuPhysicalCoordinates.Show(this, PointToScreen(new Point(0,20))); 
        }

Lưu ý: chúng ta cần tính toán chính xác vị trí muốn Show menu trước khi gọi hàm SystemMenuPhysicalCoordinates.Show(this, PointToScreen(new Point(0,20))). Tạo độ trên có nghĩa là X_menu trùng với X của điểm TRÊN-TRÁI Window, Y_menu nằm dưới, cách Y của điểm TRÊN-TRÁI Window 20px.

SystemMenuPhysical tuanpham wpf tuanphamdg

Các bạn có thể phát triển rộng hơn bằng cách đóng gói thành một Behavior độc lập để Attaching khi cần thiết. Tải source tại đây, chúc các bạn thành công!

Phạm Tuân WPF.

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất /  Thay đổi )

Google photo

Bạn đang bình luận bằng tài khoản Google Đăng xuất /  Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất /  Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất /  Thay đổi )

Connecting to %s