WPF 无边框圆角阴影窗体 | WPF 只允许当前程序打开一个并激活先前打开的窗体

【WPF 无边框圆角阴影窗体】

XAML:

<Window x:Class="MyApps.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="我的应用"
        Height="720"
        Width="1280"
        WindowStyle="None"
        MinHeight="720"
        MinWidth="1280"
        MaxHeight="{x:Static SystemParameters.MaximizedPrimaryScreenHeight}"
        MaxWidth="{x:Static SystemParameters.MaximizedPrimaryScreenWidth}"
        WindowStartupLocation="CenterScreen"
        ContentRendered="Window_ContentRendered"
        StateChanged="Window_StateChanged">

    <WindowChrome.WindowChrome>
        <WindowChrome GlassFrameThickness="1" CaptionHeight="47" UseAeroCaptionButtons="False"/>
    </WindowChrome.WindowChrome>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="180"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Border Background="White" Grid.ColumnSpan="2" Grid.RowSpan="2"/>

        <Border Grid.Column="0" Grid.RowSpan="2">
            <Border.Background>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="#FF3C14C8"/>
                    <GradientStop Color="#FFB91EC8" Offset="1"/>
                </LinearGradientBrush>
            </Border.Background>
            <Grid>
                <TextBlock Text="我的应用" Margin="0,15,0,0" Foreground="White" FontFamily="Microsoft Yahei UI" FontSize="15" FontWeight="DemiBold" HorizontalAlignment="Center" VerticalAlignment="Top">
                    <TextBlock.LayoutTransform>
                        <!--文本旋转角度-->
                        <RotateTransform Angle="0"/>
                    </TextBlock.LayoutTransform>
                </TextBlock>
            </Grid>
        </Border>
    </Grid>

</Window>

CS:

namespace MyApps
{
    #region Window 窗体事件顺序
    //1.Window.Initialized
    //2.Window.Activatied
    //3.Window.Loaded
    //4.Window.ContentRendered
    //5.Window.DeActivatied
    //6.Window.Closing
    //7.Window.UnLoad
    //8.Window.Closed
    #endregion

    public partial class MainWindow : System.Windows.Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_StateChanged(object sender, System.EventArgs e)
        {
            // 使用WindowChrome属性,窗体最大化后四周会超出屏幕边缘7pixels。
            // 以下代码为修正窗体最大化后溢出屏幕边缘的问题。
            if (WindowState == System.Windows.WindowState.Maximized)
                BorderThickness = new(7);
            else if (WindowState == System.Windows.WindowState.Normal)
                BorderThickness = new(0);
        }

        private void Window_ContentRendered(object sender, System.EventArgs e)
        {

        }
    }
}

【WPF 只允许当前程序打开一个并激活先前打开的窗体】

namespace MyApps
{
    public partial class App : System.Windows.Application
    {
        /// <summary> 
		/// 该设置窗口的显示状态,而无需等待操作完成。 
		/// </summary> 
		/// <param name="hWnd">窗口的句柄。</param> 
		/// <param name="cmdShow">控制窗口的显示方式。 有关可能值的列表,请参阅 ShowWindow 函数的说明。</param> 
		/// <returns>如果操作已成功启动,则返回值为非零值。</returns> 
		[System.Runtime.InteropServices.DllImport("User32.dll")]
        private static extern bool ShowWindowAsync(nint hWnd, int cmdShow);
        /// <summary> 
        /// 将创建指定窗口的线程引入前台并激活窗口。 键盘输入将定向到窗口,并为用户更改各种视觉提示。 系统为创建前台窗口的线程分配的优先级略高于其他线程的优先级。
        /// </summary> 
        /// <param name="hWnd">应激活并带到前台的窗口的句柄。</param> 
        /// <returns>如果将窗口带到前台,则返回值为非零值。如果未将窗口带到前台,则返回值为零。</returns> 
        [System.Runtime.InteropServices.DllImport("User32.dll")]
        private static extern bool SetForegroundWindow(nint hWnd);

        /// <summary>
        /// 检索其类名和窗口名称与指定字符串匹配的顶级窗口的句柄。 此函数不搜索子窗口。 此函数不执行区分大小写的搜索。若要从指定的子窗口开始搜索子窗口,请使用 FindWindowEx 函数。
        /// </summary>
        /// <param name="lpClassName">
        /// 由先前调用 RegisterClass 或 RegisterClassEx 函数创建的类名或类原子。 原子必须位于 lpClassName 的低序字中;高序字必须为零。如果 lpClassName 指向字符串,则指定窗口类名。 类名可以是使用 RegisterClass 或 RegisterClassEx 注册的任何名称,也可以是任何预定义的控件类名称。如果 lpClassName 为 NULL,它将查找其标题与 lpWindowName 参数匹配的任何窗口。</param>
        /// <param name="lpWindowName">窗口名称 (窗口标题) 。 如果此参数为 NULL,则所有窗口名称都匹配。</param>
        /// <returns>如果函数成功,则返回值是具有指定类名称和窗口名称的窗口的句柄。如果函数失败,则返回值为 NULL。 要获得更多的错误信息,请调用 GetLastError。</returns>
        [System.Runtime.InteropServices.DllImport("User32.dll")]
        private static extern nint FindWindow(string? lpClassName, string? lpWindowName);

        /// <summary>
        /// 检索创建指定窗口的线程的标识符,以及创建该窗口的进程(可选)的标识符。
        /// </summary>
        /// <param name="hWnd">窗口的句柄。</param>
        /// <param name="processId">指向接收进程标识符的变量的指针。 如果此参数不为 NULL, 则 GetWindowThreadProcessId 会将进程的标识符复制到变量;否则,它不会。 如果函数失败,则变量的值保持不变。</param>
        /// <returns></returns>
        [System.Runtime.InteropServices.DllImport("User32.dll")]
        private static extern int GetWindowThreadProcessId(nint hWnd, out int processId);

        private const int SW_SHOWNOACTIVATE = 4;

        /// <summary>
        /// 激活并显示已打开的窗体。
        /// </summary>
        /// <param name="process">先前已打开的进程。</param>
        private static void ActivateAndShowWindow(System.Diagnostics.Process process)
        {
            _ = ShowWindowAsync(process.MainWindowHandle, SW_SHOWNOACTIVATE);
            _ = SetForegroundWindow(process.MainWindowHandle);
        }

        /// <summary>
        /// 获取当前运行程序的进程。
        /// </summary>
        /// <returns>如果匹配到先前已经打开了此程序,则返回先前程序的进程;否则返回空。</returns>
        private System.Diagnostics.Process? GetCurrentProcesses()
        {
            using System.Diagnostics.Process current = System.Diagnostics.Process.GetCurrentProcess();
            System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcessesByName(current.ProcessName);
            if (processes.Length > 0)
            {
                foreach (System.Diagnostics.Process process in processes)
                {
                    if (process.Id != current.Id)
                    {
                        nint hWnd = process.MainWindowHandle;
                        if (hWnd.ToInt32() == 0)
                        {
                            hWnd = FindWindow(null, MainWindow?.Title);
                            _ = GetWindowThreadProcessId(hWnd, out int id);
                            if (id == process.Id)
                                return process;
                        }
                        else
                        {
                            if (current.MainModule != null)
                            {
                                if (System.Reflection.Assembly.GetExecutingAssembly().Location.Replace("/", @"\") == current.MainModule.FileName.Replace(".exe", ".dll"))
                                    return process;
                            }
                        }
                    }
                }
            }
            return null;
        }

        public App()
        {
            Startup += new System.Windows.StartupEventHandler(App_Startup);
        }

        private void App_Startup(object? sender, System.Windows.StartupEventArgs e)
        {
            // 以下注释段代码在实际使用中发现存在异常情况。
            // 当在界面添加控件后,例如Button。在对Button进行Click操作后再次打开一个本程序时,createdNew可能会返回True。
            // 该情况导致Mutex互斥锁无效,新打开的窗体会显示出来。
            
            //_ = new System.Threading.Mutex(true, System.Diagnostics.Process.GetCurrentProcess().ProcessName, out bool createdNew);
            //if (!createdNew)
            //{
            //    using (System.Diagnostics.Process? process = GetCurrentProcesses())
            //    {
            //        if (process != null)
            //            ActiveAndShowWindow(process);
            //    }

            //    System.Environment.Exit(0);
            //}

            // 如出现上述情况,建议直接使用下面的代码段。

            using System.Diagnostics.Process? process = GetCurrentProcesses();
            if (process != null)
            {
                ActivateAndShowWindow(process);
                System.Environment.Exit(0);
            }
        }
    }
}

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值