【问题标题】:WPF: Make window unresizeable, but keep the frame?WPF:使窗口无法调整大小,但保留框架?
【发布时间】:2010-08-02 09:26:30
【问题描述】:

我有一个没有标题栏的窗口 (WindowStyle == WindowStyle.None)。整个窗户采用 Aero 玻璃效果。当我使窗口无法调整大小时(ResizeMode == ResizeMode.NoResize),玻璃效果消失了,我的控件就挂在半空中。 (本质上,窗口本身会消失,但会保留其内容。)

有没有办法让窗口无法调整大小而不去掉窗框?


我已阅读问题Enable Vista glass effect on a borderless WPF window,但这不是我想要的——我想保留窗口边框。有关我希望窗口外观的示例,请在启用 Aero 的情况下按 Alt+Tab。


为了澄清,我不希望在将鼠标悬停在窗口边框上时显示调整大小的光标。这基本上就是我希望我的窗口看起来的样子:

解决方案不必是严格的 WPF——我很乐意使用 Win32 API 来实现这一点。

【问题讨论】:

    标签: c# wpf windows aero window-resize


    【解决方案1】:

    您可以挂钩 wndproc 并拦截 WM_WINDOWPOSCHANGING 消息。严格来说不是 WPF,但可能是您最好的选择。

    如果你想隐藏调整大小的光标,那么你最好的办法是拦截WM_NCHITTEST。调用 DefWindowProc(获取默认行为),并测试返回值;如果是 HTBOTTOM、HTBOTTOMLEFT、HTBOTTOMRIGHT、HTTOP、HTTOPLEFT 或 HTTOPRIGHT,则将返回值更改为 HTBORDER。

    【讨论】:

    • 您仍然可以调整大小的光标,并且 OP 说(在另一条评论中)这些是不可取的。
    • 工作就像一个魅力!谢谢!享受 275 更多的代表。 :)
    【解决方案2】:

    基于 Eric 的回答。

    public partial class MainWindow : Window
    {
        [DllImport("DwmApi.dll")]
        public static extern int DwmExtendFrameIntoClientArea(
            IntPtr hwnd,
            ref MARGINS pMarInset);
    
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr DefWindowProc(
            IntPtr hWnd,
            int msg,
            IntPtr wParam,
            IntPtr lParam);
    
        private const int WM_NCHITTEST = 0x0084;
        private const int HTBORDER = 18;
        private const int HTBOTTOM = 15;
        private const int HTBOTTOMLEFT = 16;
        private const int HTBOTTOMRIGHT = 17;
        private const int HTLEFT = 10;
        private const int HTRIGHT = 11;
        private const int HTTOP = 12;
        private const int HTTOPLEFT = 13;
        private const int HTTOPRIGHT = 14;
    
        public MainWindow()
        {
            InitializeComponent();
    
            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }
    
        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            try
            {
                // Obtain the window handle for WPF application
                IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle;
                HwndSource mainWindowSrc = HwndSource.FromHwnd(mainWindowPtr);
                mainWindowSrc.CompositionTarget.BackgroundColor = Color.FromArgb(0, 0, 0, 0);
                mainWindowSrc.AddHook(WndProc);
    
                // Set Margins
                MARGINS margins = new MARGINS();
                margins.cxLeftWidth = 10;
                margins.cxRightWidth = 10;
                margins.cyBottomHeight = 10;
                margins.cyTopHeight = 10;
    
                int hr = DwmExtendFrameIntoClientArea(mainWindowSrc.Handle, ref margins);
                //
                if (hr < 0)
                {
                    //DwmExtendFrameIntoClientArea Failed
                }
            }
            // If not Vista, paint background white.
            catch (DllNotFoundException)
            {
                Application.Current.MainWindow.Background = Brushes.White;
            }
        }
    
        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            // Override the window hit test
            // and if the cursor is over a resize border,
            // return a standard border result instead.
            if (msg == WM_NCHITTEST)
            {
                handled = true;
                var htLocation = DefWindowProc(hwnd, msg, wParam, lParam).ToInt32();
                switch (htLocation)
                {
                    case HTBOTTOM:
                    case HTBOTTOMLEFT:
                    case HTBOTTOMRIGHT:
                    case HTLEFT:
                    case HTRIGHT:
                    case HTTOP:
                    case HTTOPLEFT:
                    case HTTOPRIGHT:
                        htLocation = HTBORDER;
                        break;
                }
    
                return new IntPtr(htLocation);
            }
    
            return IntPtr.Zero;
        }
    
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct MARGINS
    {
        public int cxLeftWidth;      // width of left border that retains its size
        public int cxRightWidth;     // width of right border that retains its size
        public int cyTopHeight;      // height of top border that retains its size
        public int cyBottomHeight;   // height of bottom border that retains its size
    };
    
    <Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="150" Width="200" 
        Background="Transparent"
        WindowStyle="None"
        ResizeMode="CanResize"
    >
        <Grid Background="White" Margin="10,10,10,10">
            <Button Content="Go Away" Click="Button_Click" Height="20" Width="100" />
        </Grid>
    </Window>
    

    【讨论】:

    • 公平起见,我将回答 Eric,因为他首先发布了这个想法,但感谢您提供的工作示例。 :)
    • 这就是我提到它的原因。我只是想看看我能不能让它工作。
    • 不应该在所有情况下都调用 DefWindowProc 吗?在此代码中,如果 msg 为 WM_NCHITTEST,则仅调用原始函数。我想要正确地进行覆盖,任何其他消息都应该简单地返回原始函数给出的结果。
    • 这不太行。白色边框被添加到窗口的内框。如果您使用的是白色背景,那很好,否则它将无法正常工作。
    • @Ed S. 在 MainWindow_Loaded 事件处理程序中,将所有边距从 10 更改为 12 会将边框移动到 WPF 窗口区域下方,从而有效地隐藏它。
    【解决方案3】:

    实现此目的的一种骇人听闻的方法是设置 MinWidth/MaxWidth 和 MinHeight/MaxHeight 属性以有效地使其无法调整大小。当然,问题是你仍然会在边框上获得调整大小的光标。

    【讨论】:

    • 好吧,关键是要摆脱调整大小的光标,不幸的是,这无济于事。
    【解决方案4】:

    你为什么不直接为窗口创建这个窗口边框呢? 它使用偏移量来设置窗口的颜色。 所以,一个简单的方法就是在你的窗口周围包裹一个完整的边框,然后你就可以得到自己的颜色!

    【讨论】:

    • 我的意思是把它包裹在一个窗口周围,我的意思是基本上包裹
    • 但是我不能让它与用户当前的主题相匹配。我可以制作一个看起来与默认 Aero 主题完全一样的自定义边框,但如果用户自定义了它(或正在使用不同的主题),则窗口将不匹配。
    • 我不会放弃这个答案,但我可能最终不得不这样做。
    • 好吧...如果您想要自定义颜色。为什么不绑定偏移量并让用户选择自己的颜色?如果您使用的是 MVVM 方法,在您的视图模型中设置一个带有颜色代码的字符串并将其绑定到偏移量中,那不工作吗?
    • 如果您想根据击键进行更改。创建一个执行此操作的属性: public void OnKeyChanged (object sender, keyboardEventArgs e) { if(e.Key = Tab && e.Key = Alt) { PropertyColor = "White" } else PropertyColor = Blue (但使用颜色代码) }
    猜你喜欢
    • 2013-07-12
    • 2011-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-19
    • 2021-06-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多