【问题标题】:Show a Form without stealing focus?显示表单而不窃取焦点?
【发布时间】:2020-10-30 14:07:44
【问题描述】:

我正在使用表单来显示通知(它显示在屏幕的右下方),但是当我显示此表单时,它会从主表单中窃取焦点。有没有办法在不窃取焦点的情况下显示这个“通知”表单?

【问题讨论】:

    标签: c# .net winforms


    【解决方案1】:

    嗯,简单地覆盖 Form.ShowWithoutActivation 还不够吗?

    protected override bool ShowWithoutActivation
    {
      get { return true; }
    }
    

    如果你也不希望用户点击这个通知窗口,你可以覆盖 CreateParams:

    protected override CreateParams CreateParams
    {
      get
      {
        CreateParams baseParams = base.CreateParams;
    
        const int WS_EX_NOACTIVATE = 0x08000000;
        const int WS_EX_TOOLWINDOW = 0x00000080;
        baseParams.ExStyle |= ( int )( WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW );
    
        return baseParams;
      }
    }
    

    【讨论】:

    • ShowWithoutActivation,不敢相信我没找到,浪费了一下午!
    • 我还需要设置form1.Enabled = false 以防止内部控件窃取焦点
    • 如果您确实想要 TopMost,请参阅other answer
    • WS_EX_NOACTIVATEWS_EX_TOOLWINDOW的值分别为0x080000000x00000080
    【解决方案2】:

    PInvoke.netShowWindow方法中窃取:

    private const int SW_SHOWNOACTIVATE = 4;
    private const int HWND_TOPMOST = -1;
    private const uint SWP_NOACTIVATE = 0x0010;
    
    [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
    static extern bool SetWindowPos(
         int hWnd,             // Window handle
         int hWndInsertAfter,  // Placement-order handle
         int X,                // Horizontal position
         int Y,                // Vertical position
         int cx,               // Width
         int cy,               // Height
         uint uFlags);         // Window positioning flags
    
    [DllImport("user32.dll")]
    static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
    
    static void ShowInactiveTopmost(Form frm)
    {
         ShowWindow(frm.Handle, SW_SHOWNOACTIVATE);
         SetWindowPos(frm.Handle.ToInt32(), HWND_TOPMOST,
         frm.Left, frm.Top, frm.Width, frm.Height,
         SWP_NOACTIVATE);
    }
    

    (Alex Lyman 回答了这个问题,我只是通过直接粘贴代码来扩展它。有编辑权限的人可以将它复制到那里并删除它,我只关心它;))

    【讨论】:

    • 我在想,如果他在屏幕左下方显示的表格在另一个线程中,他真的需要吗?
    • 我觉得难以置信的是,我们仍然需要链接到外部 DLL 文件才能与表单进行交互。我们在 .NET 框架版本 4!是时候把它包装起来微软了。
    • 接受的答案不正确。寻找 ShowWithoutActivation
    • 如果您希望表单直接不聚焦,只需在 ShowInactiveTopmost 函数的开头添加 frm.Hide(); 即可。不要忘记:使用 System.Runtime.InteropServices; 让这段代码运行
    • @Talha 此代码与 Load 事件无关。 Load 事件在表单加载时触发,而不是在显示时触发。
    【解决方案3】:

    这对我有用。它提供 TopMost 但没有窃取焦点。

        protected override bool ShowWithoutActivation
        {
           get { return true; }
        }
    
        private const int WS_EX_TOPMOST = 0x00000008;
        protected override CreateParams CreateParams
        {
           get
           {
              CreateParams createParams = base.CreateParams;
              createParams.ExStyle |= WS_EX_TOPMOST;
              return createParams;
           }
        }
    

    记得在 Visual Studio 设计器或其他地方省略设置 TopMost。

    这是从这里被盗、出错、借来的(点击解决方法):

    https://connect.microsoft.com/VisualStudio/feedback/details/401311/showwithoutactivation-is-not-supported-with-topmost

    【讨论】:

    • Topmost + 没有重点的作品,它看起来是所有答案中最干净的。
    • Topmost 自 Windows 8 以来已被弃用,微软在使用它时会惩罚你。效果是在打开最顶层的窗口然后关闭它后,Windows 会将应用程序的其他窗口移至后台。这肯定不是您的应用程序所期望的行为。微软实现了这一点,因为过去许多程序员滥用了非常侵入性的顶层。 Topmost 非常具有攻击性。我从不使用它。
    【解决方案4】:

    如果您愿意使用Win32P/Invoke,那么您可以使用ShowWindow 方法(第一个代码示例正是您想要的)。

    【讨论】:

      【解决方案5】:

      这样做似乎是一种 hack,但它似乎有效:

      this.TopMost = true;  // as a result the form gets thrown to the front
      this.TopMost = false; // but we don't actually want our form to always be on top
      

      编辑:注意,这只会引发一个已经创建的表单而不会窃取焦点。

      【讨论】:

      • 在这里似乎不起作用......可能是因为这个“通知表单”是在另一个线程中打开的?
      • 可能,在这种情况下,您需要执行 this.Invoke() 调用以作为正确的线程再次调用该方法。通常使用来自错误线程的表单会导致抛出异常。
      • 虽然这确实有效,但如前所述,这是一种骇人听闻的方法,并且在某些情况下会导致我出现 BSOD,因此请注意这一点。
      • Topmost 自 Windows 8 以来已被弃用,微软在使用它时会惩罚你。效果是在打开最顶层的窗口然后关闭它后,Windows 会将应用程序的其他窗口移至后台。这肯定不是您的应用程序所期望的行为。微软实现了这一点,因为过去许多程序员滥用了非常侵入性的顶层。 Topmost 非常具有攻击性。我从不使用它。
      【解决方案6】:

      Alex Lyman/TheSoftwareJedi 的答案中来自 pinvoke.net 的示例代码将使窗口成为“最顶层”窗口,这意味着在弹出窗口后您不能将其放在普通窗口后面。鉴于马蒂亚斯对他想用这个做什么的描述,这可能就是他想要的。但是,如果您希望用户能够在弹出窗口后将其置于其他窗口之后,只需在示例中使用 HWND_TOP (0) 而不是 HWND_TOPMOST (-1)。

      【讨论】:

        【解决方案7】:

        在 WPF 中你可以这样解决:

        在窗口中放置这些属性:

        <Window
            x:Class="myApplication.winNotification"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          Title="Notification Popup" Width="300" SizeToContent="Height"
          WindowStyle="None" AllowsTransparency="True" Background="Transparent" ShowInTaskbar="False" Topmost="True" Focusable="False" ShowActivated="False" >
        </Window>
        

        最后一个属性是你需要的 ShowActivated="False"。

        【讨论】:

          【解决方案8】:

          我有类似的东西,我只是显示通知表然后做

          this.Focus();
          

          将焦点重新放在主窗体上。

          【讨论】:

          • 简单但有效!
          【解决方案9】:

          在单独的线程中创建并启动通知表单,并在表单打开后将焦点重置回主表单。让通知表单提供从Form.Shown 事件触发的 OnFormOpened 事件。像这样的:

          private void StartNotfication()
          {
            Thread th = new Thread(new ThreadStart(delegate
            {
              NotificationForm frm = new NotificationForm();
              frm.OnFormOpen += NotificationOpened;
              frm.ShowDialog();
            }));
            th.Name = "NotificationForm";
            th.Start();
          } 
          
          private void NotificationOpened()
          {
             this.Focus(); // Put focus back on the original calling Form
          }
          

          您还可以保留 NotifcationForm 对象的句柄,以便主窗体 (frm.Close()) 以编程方式关闭它。

          缺少一些细节,但希望这能让你朝着正确的方向前进。

          【讨论】:

          • 这仅在您的表单是最初的活动表单时才有效。这违背了这种通知的主要目的。
          • 嗯?这就是通知的目的 - 将其放置并重新获得焦点回到最初的活动表单。
          • 这只会将焦点放在您的应用程序中的一个表单上——如果某个其他程序当时处于活动状态怎么办?显示通知窗口(通常是为了让用户了解您的应用程序状态的更新)只有在他们不观看您的应用程序时才真正有用。
          【解决方案10】:

          您可能需要考虑要显示哪种通知。

          如果让用户知道某个事件是绝对关键的,那么推荐使用 Messagebox.Show 的方式,因为它的性质是阻止任何其他事件进入主窗口,直到用户确认为止。但请注意弹出式盲区。

          如果不是很重要,您可能希望使用其他方式来显示通知,例如窗口底部的工具栏。您写道,您在屏幕的右下角显示通知 - 执行此操作的标准方法是使用 balloon tipsystem tray 图标的组合。

          【讨论】:

          • -气球提示不是一个选项,因为可以禁用-如果您将程序最小化,状态栏可能会被隐藏无论如何感谢您的推荐
          【解决方案11】:

          这很好用。

          见:OpenIcon - MSDNSetForegroundWindow - MSDN

          using System.Runtime.InteropServices;
          
          [DllImport("user32.dll")]
          static extern bool OpenIcon(IntPtr hWnd);
          
          [DllImport("user32.dll")]
          static extern bool SetForegroundWindow(IntPtr hWnd);
          
          public static void ActivateInstance()
          {
              IntPtr hWnd = IntPtr hWnd = Process.GetCurrentProcess().MainWindowHandle;
          
              // Restore the program.
              bool result = OpenIcon(hWnd); 
              // Activate the application.
              result = SetForegroundWindow(hWnd);
          
              // End the current instance of the application.
              //System.Environment.Exit(0);    
          }
          

          【讨论】:

            【解决方案12】:

            也可以仅通过逻辑来处理它,尽管我不得不承认,上面的建议是最优雅的建议,即您最终使用 BringToFront 方法而不实际窃取焦点。

            无论如何,我遇到了这个问题,并通过使用 DateTime 属性解决了这个问题,如果最近已经进行了调用,则不允许进一步的 BringToFront 调用。

            假设有一个核心类“Core”,它处理例如三个表单,“Form1、2 和 3”。每个表单都需要一个 DateTime 属性和一个调用 Core 将窗口置于前面的 Activate 事件:

            internal static DateTime LastBringToFrontTime { get; set; }
            
            private void Form1_Activated(object sender, EventArgs e)
            {
                var eventTime = DateTime.Now;
                if ((eventTime - LastBringToFrontTime).TotalMilliseconds > 500)
                    Core.BringAllToFront(this);
                LastBringToFrontTime = eventTime;
            }
            

            然后在Core Class中创建作品:

            internal static void BringAllToFront(Form inForm)
            {
                Form1.BringToFront();
                Form2.BringToFront();
                Form3.BringToFront();
                inForm.Focus();
            }
            

            附带说明,如果要将最小化的窗口恢复到其原始状态(未最大化),请使用:

            inForm.WindowState = FormWindowState.Normal;
            

            再一次,我知道这只是缺少 BringToFrontWithoutFocus 的补丁解决方案。如果您想避免使用 DLL 文件,这只是一个建议。

            【讨论】:

              【解决方案13】:

              我不知道这是否被认为是 necro-posting,但这是我所做的,因为我无法让它与 user32 的“ShowWindow”和“SetWindowPos”方法一起使用。不,覆盖“ShowWithoutActivation”在这种情况下不起作用,因为新窗口应该始终位于顶部。 无论如何,我创建了一个以表单为参数的辅助方法;调用时,它会显示表单,将其带到最前面并使其成为 TopMost,而不会窃取当前窗口的焦点(显然它确实如此,但用户不会注意到)。

                  [DllImport("user32.dll")]
                  static extern IntPtr GetForegroundWindow();
              
                  [DllImport("user32.dll")]
                  static extern IntPtr SetForegroundWindow(IntPtr hWnd);
              
                  public static void ShowTopmostNoFocus(Form f)
                  {
                      IntPtr activeWin = GetForegroundWindow();
              
                      f.Show();
                      f.BringToFront();
                      f.TopMost = true;
              
                      if (activeWin.ToInt32() > 0)
                      {
                          SetForegroundWindow(activeWin);
                      }
                  }
              

              【讨论】:

                【解决方案14】:

                我知道这听起来可能很愚蠢,但这很有效:

                this.TopMost = true;
                this.TopMost = false;
                this.TopMost = true;
                this.SendToBack();
                

                【讨论】:

                • 如果您将前窗口发送到后面,如果背景窗口与新的前景窗口重叠,则可能不再显示。
                【解决方案15】:

                我需要对我的窗口 TopMost 执行此操作。我实现了上面的 PInvoke 方法,但发现我的 Load 事件没有像上面的 Talha 那样被调用。我终于成功了。也许这会对某人有所帮助。这是我的解决方案:

                        form.Visible = false;
                        form.TopMost = false;
                        ShowWindow(form.Handle, ShowNoActivate);
                        SetWindowPos(form.Handle, HWND_TOPMOST,
                            form.Left, form.Top, form.Width, form.Height,
                            NoActivate);
                        form.Visible = true;    //So that Load event happens
                

                【讨论】:

                  【解决方案16】:

                  你不需要把它弄得这么复杂。

                  a = new Assign_Stock(); 
                  a.MdiParent = this.ParentForm;
                  a.Visible = false;   //hide for a bit.                 
                  a.Show(); //show the form. Invisible form now at the top.
                  this.Focus(); //focus on this form. make old form come to the top.
                  a.Visible = true; //make other form visible now. Behind the main form.
                  

                  【讨论】:

                    【解决方案17】:

                    Github Sample

                    Form.ShowWithoutActivation Property

                    将此添加到您的子表单类中

                        protected override bool ShowWithoutActivation
                            {
                                get { return true; }
                            }
                    

                    工作代码

                    Form2

                       public partial class Form2 : Form
                        {
                            Form3 c;
                            public Form2()
                            {
                                InitializeComponent();
                                 c = new Form3();
                            }
                    
                            private void textchanged(object sender, EventArgs e)
                            {
                    
                    
                                c.ResetText(textBox1.Text.ToString());
                                c.Location = new Point(this.Location.X+150, this.Location.Y);
                                c .Show();
                    
                    //removethis
                    //if mdiparent 2 add this.focus() after show form
                    
                                c.MdiParent = this.MdiParent;
                                c.ResetText(textBox1.Text.ToString());
                                c.Location = new Point(this.Location.X+150, this.Location.Y);
                                c .Show();
                               this.Focus();
                    ////-----------------
                    
                    
                            }
                    
                    
                           
                        }
                    

                    Form3

                       public partial class Form3 : Form
                        {
                            public Form3()
                            {
                                InitializeComponent();
                                //ShowWithoutActivation = false;
                            }
                            protected override bool ShowWithoutActivation
                            {
                                get { return true; }
                            }
                    
                    
                            internal void ResetText(string toString)
                            {
                                label2.Text = toString;
                            }
                    
                         
                        }
                    

                    【讨论】:

                      【解决方案18】:

                      当您使用

                      创建新表单时
                      Form f = new Form();
                      f.ShowDialog();
                      

                      它会窃取焦点,因为您的代码在主窗体关闭之前无法继续在主窗体上执行。

                      例外是使用线程创建一个新表单,然后使用 Form.Show()。确保线程是全局可见的,因为如果你在函数中声明它,一旦你的函数退出,你的线程就会结束并且表单会消失。

                      【讨论】:

                        【解决方案19】:

                        想通了:window.WindowState = WindowState.Minimized;

                        【讨论】:

                          猜你喜欢
                          • 2010-11-01
                          • 1970-01-01
                          • 2023-04-02
                          • 1970-01-01
                          • 1970-01-01
                          • 2016-04-13
                          • 1970-01-01
                          • 2015-08-01
                          相关资源
                          最近更新 更多