Hiển thị một hình thức mà không ăn cắp tập trung?


140

Tôi đang sử dụng Biểu mẫu để hiển thị thông báo (nó xuất hiện ở dưới cùng bên phải của màn hình), nhưng khi tôi hiển thị biểu mẫu này, nó sẽ đánh cắp tiêu điểm từ Biểu mẫu chính. Có cách nào để hiển thị biểu mẫu "thông báo" này mà không lấy cắp trọng tâm không?

Câu trả lời:


165

Hmmm, không chỉ đơn giản là ghi đè Form.ShowWithoutActivation đủ?

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

Và nếu bạn không muốn người dùng nhấp vào cửa sổ thông báo này, bạn có thể ghi đè CreatParams:

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

3
ShowWithoutActivation, không thể tin rằng tôi đã không tìm thấy nó, lãng phí cả một buổi chiều!
Deerchao

2
Tôi cũng cần phải thiết lập form1.Enabled = falseđể ngăn chặn các kiểm soát bên trong lấy cắp tiêu điểm
Jader Dias

23
Và rời khỏi TopMost.
mklein

4
Và nếu bạn muốn TopMost, hãy xem câu trả lời khác .
Roman Starkov

2
Các giá trị của WS_EX_NOACTIVATEWS_EX_TOOLWINDOW0x080000000x00000080tương ứng.
Juan

69

Bị đánh cắp từ phương thức ShowWindow của PInvoke.net :

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 đã trả lời điều này, tôi chỉ mở rộng nó bằng cách dán trực tiếp mã. Ai đó có quyền chỉnh sửa có thể sao chép nó ở đó và xóa nó cho tất cả những gì tôi quan tâm;))


Tôi đã tự hỏi, anh ta có thực sự cần điều đó không nếu hình thức anh ta hiển thị ở phía dưới bên trái màn hình của anh ta ở một chủ đề khác?
Patrick Desjardins

50
Tôi thấy không thể tin được rằng chúng ta vẫn cần liên kết đến các tệp DLL bên ngoài để tương tác với các biểu mẫu. Chúng tôi đang ở .NET framework phiên bản 4 !! Thời gian để bọc nó Microsoft.
Maltrap

9
Câu trả lời được chấp nhận là không chính xác. Tìm kiếm ShowWithoutActivation
mklein

Chỉ cần thêm frm.Hide (); ở đầu hàm ShowInactiveTopest nếu bạn muốn biểu mẫu của mình không tập trung trực tiếp. Đừng quên: sử dụng System.R.78.InteropService; để chạy mã này
Zitun

1
@Talha Mã này không liên quan gì đến sự kiện Load. Sự kiện Load kích hoạt khi biểu mẫu đang được tải, không phải khi nó được hiển thị.
TheSoftwareJedi


12

Đây là những gì làm việc cho tôi. Nó cung cấp TopMost nhưng không lấy nét tập trung.

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

Nhớ bỏ qua cài đặt TopMost trong trình thiết kế Visual Studio hoặc ở nơi khác.

Điều này bị đánh cắp, lỗi, mượn, từ đây (bấm vào Workaround):

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


1
Công việc hàng đầu + không tập trung, và có vẻ như là sạch nhất trong tất cả các câu trả lời.
feos

Trên cùng là không được chấp nhận kể từ Windows 8, nơi Microsoft trừng phạt bạn khi sử dụng nó. Hiệu quả là sau khi mở một cửa sổ trên cùng và sau đó đóng nó, Windows sẽ di chuyển các cửa sổ khác của ứng dụng của bạn sang nền. Đây chắc chắn không phải là hành vi mong muốn cho ứng dụng của bạn. Microsoft đã thực hiện điều này bởi vì trong quá khứ, nhiều lập trình viên đã lạm dụng đỉnh cao rất khó xâm nhập. Trên cùng là rất tích cực. Tôi không bao giờ sử dụng nó.
Elmue

9

Làm điều này có vẻ như một hack, nhưng nó dường như hoạt động:

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

Chỉnh sửa: Lưu ý, điều này chỉ làm tăng một hình thức đã được tạo mà không lấy cắp trọng tâm.


dường như không hoạt động ở đây ... có thể là do "mẫu thông báo" này được mở trong một chủ đề khác?
Matías

1
Có lẽ, trong trường hợp đó, bạn cần thực hiện một cuộc gọi này. Nhập vào () để gọi lại phương thức như là luồng đúng. Nói chung làm việc với các hình thức từ các chủ đề sai gây ra một ngoại lệ được ném ra mặc dù.
Matthew Scharley

Mặc dù điều này không hoạt động, nó là một phương pháp hacky như đã đề cập và đã gây ra BSOD cho tôi trong một số điều kiện nhất định, vì vậy hãy cẩn thận với điều này.
Raphael Smit

Trên cùng là không được chấp nhận kể từ Windows 8, nơi Microsoft trừng phạt bạn khi sử dụng nó. Hiệu quả là sau khi mở một cửa sổ trên cùng và sau đó đóng nó, Windows sẽ di chuyển các cửa sổ khác của ứng dụng của bạn sang nền. Đây chắc chắn không phải là hành vi mong muốn cho ứng dụng của bạn. Microsoft đã thực hiện điều này bởi vì trong quá khứ, nhiều lập trình viên đã lạm dụng đỉnh cao rất khó xâm nhập. Trên cùng là rất tích cực. Tôi không bao giờ sử dụng nó.
Elmue

9

Mã mẫu từ pinvoke.net trong câu trả lời của Alex Lyman / TheSoftwareJedi's sẽ biến cửa sổ thành cửa sổ "trên cùng", nghĩa là bạn không thể đặt nó phía sau các cửa sổ bình thường sau khi nó bật lên. Đưa ra mô tả của Matias về những gì anh ta muốn sử dụng này cho, đó có thể là những gì anh ta muốn. Nhưng nếu bạn muốn người dùng có thể đặt cửa sổ của bạn phía sau các cửa sổ khác sau khi bạn bật nó lên, chỉ cần sử dụng HWND_TOP (0) thay vì HWND_TOPMOST (-1) trong mẫu.


6

Trong WPF bạn có thể giải quyết nó như thế này:

Trong cửa sổ đặt các thuộc tính này:

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

Thuộc tính cuối cùng là thuộc tính bạn cần ShowActivated = "Sai".


4

Tôi có một cái gì đó tương tự, và tôi chỉ cần hiển thị mẫu thông báo và sau đó làm

this.Focus();

để đưa sự tập trung trở lại vào hình thức chính.


Đơn giản mà hiệu quả!
Tom

3

Tạo và bắt đầu Biểu mẫu thông báo trong một chuỗi riêng biệt và đặt lại tiêu điểm trở lại biểu mẫu chính của bạn sau khi Biểu mẫu mở ra. Có Mẫu thông báo cung cấp một sự kiện OnFormOpened được kích hoạt từ Form.Shownsự kiện này. Một cái gì đó như thế này:

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
}

Bạn cũng có thể giữ tay cầm đối tượng NotifcationForm của mình để nó có thể được đóng theo chương trình bởi Biểu mẫu chính (frm.Close() ).

Một số chi tiết bị thiếu, nhưng hy vọng điều này sẽ giúp bạn đi đúng hướng.


Điều này sẽ chỉ hoạt động nếu biểu mẫu của bạn là hình thức hoạt động ban đầu. Đó là loại đi ngược lại mục đích chính của loại thông báo này.
Alex Lyman

1
Huh? Đó là mục đích của thông báo - để đưa nó lên và lấy lại sự tập trung trở lại hình thức hoạt động ban đầu.
Bob Nadler

2
Điều này chỉ tập trung vào một biểu mẫu trong ứng dụng của bạn - điều gì xảy ra nếu một số chương trình khác đang hoạt động vào thời điểm đó? Hiển thị cửa sổ thông báo (thường là để cung cấp cho người dùng cập nhật về trạng thái của ứng dụng của bạn) chỉ thực sự hữu ích khi họ không xem ứng dụng của bạn.
Alex Lyman

3

Bạn có thể muốn xem xét loại thông báo nào bạn muốn hiển thị.

Nếu nó cực kỳ quan trọng để cho người dùng biết về một số sự kiện, sử dụng Messagebox.Show sẽ là cách được đề xuất, do bản chất của nó là chặn bất kỳ sự kiện nào khác vào cửa sổ chính, cho đến khi người dùng xác nhận nó. Hãy nhận biết về mù pop-up, mặc dù.

Nếu nó ít quan trọng hơn, bạn có thể muốn sử dụng một cách khác để hiển thị thông báo, chẳng hạn như thanh công cụ ở dưới cùng của cửa sổ. Bạn đã viết, rằng bạn hiển thị các thông báo ở phía dưới bên phải của màn hình - cách tiêu chuẩn để thực hiện việc này sẽ là sử dụng đầu bóng bay với sự kết hợp của biểu tượng khay hệ thống .


2
- Mẹo bóng bay không phải là một tùy chọn vì có thể bị vô hiệu hóa - Thanh trạng thái có thể bị ẩn nếu bạn đã thu nhỏ chương trình Dù sao cũng cảm ơn bạn đã giới thiệu
Matías

3

Điều này hoạt động tốt.

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

1

Bạn cũng có thể xử lý nó bằng logic một mình, mặc dù tôi phải thừa nhận rằng các đề xuất ở trên nơi bạn kết thúc bằng phương thức BringToFront mà không thực sự đánh cắp trọng tâm là cách thanh lịch nhất.

Dù sao đi nữa, tôi đã gặp phải vấn đề này và giải quyết nó bằng cách sử dụng thuộc tính DateTime để không cho phép các cuộc gọi BringToFront tiếp theo nếu các cuộc gọi đã được thực hiện gần đây.

Giả sử một lớp lõi, 'Lõi', xử lý ví dụ ba biểu mẫu, 'Biểu mẫu 1, 2 và 3'. Mỗi biểu mẫu cần một thuộc tính DateTime và một sự kiện Kích hoạt gọi Core để đưa các cửa sổ lên phía trước:

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

Và sau đó tạo công việc trong Lớp lõi:

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

Mặt khác, nếu bạn muốn khôi phục cửa sổ thu nhỏ về trạng thái ban đầu (không được tối đa hóa), hãy sử dụng:

inForm.WindowState = FormWindowState.Normal;

Một lần nữa, tôi biết đây chỉ là một giải pháp vá lỗi trong việc thiếu một ComeToFrontWithoutF Focus. Nó có nghĩa là một gợi ý nếu bạn muốn tránh tập tin DLL.


1

Tôi không biết liệu đây có được coi là đăng bài không, nhưng đây là những gì tôi đã làm vì tôi không làm cho nó hoạt động với các phương thức "ShowWindow" và "SetWindowPos" của user32. Và không, ghi đè "ShowWithoutActivation" không hoạt động trong trường hợp này vì cửa sổ mới phải luôn ở trên đầu. Dù sao, tôi đã tạo ra một phương thức của trình trợ giúp có dạng là tham số; khi được gọi, nó hiển thị biểu mẫu, đưa nó ra phía trước và làm cho nó trở thành TopMost mà không lấy cắp trọng tâm của cửa sổ hiện tại (rõ ràng là như vậy, nhưng người dùng sẽ không nhận thấy).

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

0

Tôi biết nó nghe có vẻ ngu ngốc, nhưng điều này đã làm việc:

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

Nếu bạn gửi cửa sổ phía trước ra phía sau, nó có thể không còn hiển thị nếu các cửa sổ nền chồng lên nền trước mới.
TamusJRoyce

0

Tôi cần phải làm điều này với cửa sổ TopMost của tôi. Tôi đã triển khai phương thức PInvoke ở trên nhưng thấy rằng sự kiện Tải của tôi không được gọi như Talha ở trên. Cuối cùng tôi đã thành công. Có lẽ điều này sẽ giúp được ai đó. Đây là giải pháp của tôi:

        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

-4

Khi bạn tạo một biểu mẫu mới bằng cách sử dụng

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

nó đánh cắp trọng tâm vì mã của bạn không thể tiếp tục thực thi trên biểu mẫu chính cho đến khi biểu mẫu này được đóng lại.

Ngoại lệ là bằng cách sử dụng luồng để tạo một biểu mẫu mới sau đó Form.Show (). Hãy chắc chắn rằng luồng được hiển thị trên toàn cầu, bởi vì nếu bạn khai báo nó trong một hàm, ngay khi hàm của bạn thoát, luồng của bạn sẽ kết thúc và biểu mẫu sẽ biến mất.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.