簡體   English   中英

顯示表單而不竊取焦點?

[英]Show a Form without stealing focus?

我正在使用表單來顯示通知(它顯示在屏幕的右下角),但是當我顯示此表單時,它會從主表單中竊取焦點。 有沒有辦法在不竊取焦點的情況下顯示這種“通知”表單?

嗯,簡單地覆蓋 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;
  }
}

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);
}

(亞歷克斯萊曼回答了這個問題,我只是通過直接粘貼代碼來擴展它。有編輯權限的人可以將它復制到那里並刪除它,我只關心它;))

這對我有用。 它提供 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

如果您願意使用Win32 P/Invoke ,那么您可以使用ShowWindow方法(第一個代碼示例完全符合您的要求)。

這樣做似乎是一種 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

編輯:注意,這只會引發一個已經創建的表單而不會竊取焦點。

Alex Lyman/TheSoftwareJedi 的答案中來自 pinvoke.net 的示例代碼將使窗口成為“最頂層”窗口,這意味着在彈出窗口后您不能將其放在普通窗口后面。 鑒於馬蒂亞斯對他想用這個做什么的描述,這可能就是他想要的。 但是,如果您希望用戶能夠在彈出窗口后將其放在其他窗口后面,只需在示例中使用 HWND_TOP (0) 而不是 HWND_TOPMOST (-1)。

在 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" 的屬性。

我有類似的東西,我只是顯示通知表然后做

this.Focus();

將焦點重新放在主窗體上。

您可能需要考慮要顯示什么樣的通知。

如果讓用戶知道某個事件是絕對關鍵的,那么推薦使用 Messagebox.Show 的方式,因為它的性質是阻止任何其他事件進入主窗口,直到用戶確認為止。 不過,請注意彈出式盲區。

如果它不太重要,您可能希望使用另一種方式來顯示通知,例如窗口底部的工具欄。 您寫道,您在屏幕的右下角顯示通知 - 執行此操作的標准方法是使用帶有系統托盤圖標組合的氣球提示

在單獨的線程中創建並啟動通知表單,並在表單打開后將焦點重置回主表單。 讓通知表單提供從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 對象的句柄,以便它可以由主 Form ( frm.Close() ) 以編程方式關閉。

缺少一些細節,但希望這會讓你朝着正確的方向前進。

這很好用。

請參閱: 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);    
}

也可以僅通過邏輯來處理它,盡管我不得不承認,上面的建議是最優雅的建議,即您最終使用 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 文件,這只是一個建議。

我不知道這是否被認為是 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);
        }
    }

我知道這聽起來可能很愚蠢,但這有效:

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

我需要用我的窗口 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

你不需要讓它變得如此復雜。

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.

在此處輸入圖像描述

Github 示例

Form.ShowWithoutActivation 屬性

將此添加到您的子表單類中

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

工作代碼

表格2

   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();
////-----------------


        }


       
    }

表格3

   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;
        }

     
    }

當您使用創建新表單時

Form f = new Form();
f.ShowDialog();

它竊取了焦點,因為在關閉此表單之前,您的代碼無法繼續在主表單上執行。

例外是使用線程創建一個新表單,然后使用 Form.Show()。 確保線程是全局可見的,因為如果你在一個函數中聲明它,一旦你的函數退出,你的線程將結束並且表單將消失。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM