【问题标题】:Bring a window to the front in WPF在 WPF 中将窗口置于最前面
【发布时间】:2010-09-20 10:21:42
【问题描述】:

如何将我的 WPF 应用程序带到桌面的前面?到目前为止,我已经尝试过:

SwitchToThisWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle, true);

SetWindowPos(new WindowInteropHelper(Application.Current.MainWindow).Handle, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

SetForegroundWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle);

没有一个在做这项工作(Marshal.GetLastWin32Error() 表示这些操作已成功完成,每个定义的 P/Invoke 属性确实有 SetLastError=true)。

如果我创建一个新的空白 WPF 应用程序,并使用计时器调用 SwitchToThisWindow,它会完全按预期工作,所以我不确定为什么它在我原来的情况下不起作用。

编辑:我正在结合全局热键来执行此操作。

【问题讨论】:

  • 您是否确认 MainWindow 是您想​​要的窗口?来自 MSDN:MainWindow 自动设置为引用要在 AppDomain 中实例化的第一个 Window 对象。
  • 好主意,但它是应用程序中唯一的窗口。
  • 你能提供更多的上下文代码吗?

标签: c# .net wpf winapi pinvoke


【解决方案1】:
myWindow.Activate();

尝试将窗口置于前台并激活它。

这应该可以解决问题,除非我误解了并且您想要始终处于最佳状态。在这种情况下,你想要:

myWindow.TopMost = true;

【讨论】:

  • 我只是在使用 myWindow.Show() ,有时它不在顶部。之后我立即拨打了 myWindow.Activate() 电话,它起作用了。
  • 激活有时在 Windows XP 上不起作用。我推荐@Matthew Xavier 的回答。
  • 有点奇怪,因为默认情况下 ShowActivated 是打开的。
  • 第一个答案很好,谢谢!但是第二行代码,使用Topmost 属性是一种不好的做法,因为它会掩盖其他弹出对话框并产生意外行为。
  • 实际上可以这样做:if (myWindow.WindowState == WindowState.Minimized) myWindow.WindowState = WindowState.Normal; 奇怪的是,它还会保留所有最大化的窗口,并且不会将它们恢复为正常状态。
【解决方案2】:

我找到了一种将窗口置于顶部的解决方案,但它的行为与普通窗口一样:

if (!Window.IsVisible)
{
    Window.Show();
}

if (Window.WindowState == WindowState.Minimized)
{
    Window.WindowState = WindowState.Normal;
}

Window.Activate();
Window.Topmost = true;  // important
Window.Topmost = false; // important
Window.Focus();         // important

【讨论】:

  • 很好的提示!如果窗口已经打开,但在其他窗口下方,TopMost 会在 Windows 7 上实现魔法。
  • 这对我也有用。感谢 gsb 对 TopMost 的奇怪用法的补充评论!
  • 谢谢 - 修复很简短。
  • 在我的情况下 Window.Activate() 和 Window.Focus() 就足够了。不需要设置 Window.TopMost。
  • 不要使用Window.Focus()。这会将焦点从用户当前在文本框中输入的内容转移,这对最终用户来说非常令人沮丧。上面的代码没有它就可以正常工作。
【解决方案3】:

如果您需要窗口在第一次加载时位于前面,那么您应该使用以下内容:

private void Window_ContentRendered(object sender, EventArgs e)
{
    this.Topmost = false;
}

private void Window_Initialized(object sender, EventArgs e)
{
    this.Topmost = true;
}

或者通过重写方法:

protected override void OnContentRendered(EventArgs e)
{
    base.OnContentRendered(e);
    Topmost = false;
}

protected override void OnInitialized(EventArgs e)
{
    base.OnInitialized(e);
    Topmost = true;
}

【讨论】:

  • 如果你在 C# 中开发类似于 Launchy (launchy.net) 的东西,你应该注意到这个答案几乎没用。
【解决方案4】:

我知道这个问题相当老了,但我刚刚遇到了这个精确的场景,并想分享我实施的解决方案。

正如本页 cmets 中所述,提出的一些解决方案不适用于 XP,我需要在我的场景中支持这些解决方案。虽然我同意@Matthew Xavier 的观点,即这通常是一种糟糕的用户体验实践,但有时它完全是一种合理的用户体验。

将 WPF 窗口置于顶部的解决方案实际上是由我用来提供全局热键的相同代码提供给我的。 A blog article by Joseph Cooney 包含一个 link to his code samples,其中包含原始代码。

我已经对代码进行了一些清理和修改,并将其实现为 System.Windows.Window 的扩展方法。我已经在 XP 32 位和 Win7 64 位上测试过,两者都可以正常工作。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Interop;
using System.Runtime.InteropServices;

namespace System.Windows
{
    public static class SystemWindows
    {
        #region Constants

        const UInt32 SWP_NOSIZE = 0x0001;
        const UInt32 SWP_NOMOVE = 0x0002;
        const UInt32 SWP_SHOWWINDOW = 0x0040;

        #endregion

        /// <summary>
        /// Activate a window from anywhere by attaching to the foreground window
        /// </summary>
        public static void GlobalActivate(this Window w)
        {
            //Get the process ID for this window's thread
            var interopHelper = new WindowInteropHelper(w);
            var thisWindowThreadId = GetWindowThreadProcessId(interopHelper.Handle, IntPtr.Zero);

            //Get the process ID for the foreground window's thread
            var currentForegroundWindow = GetForegroundWindow();
            var currentForegroundWindowThreadId = GetWindowThreadProcessId(currentForegroundWindow, IntPtr.Zero);

            //Attach this window's thread to the current window's thread
            AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, true);

            //Set the window position
            SetWindowPos(interopHelper.Handle, new IntPtr(0), 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);

            //Detach this window's thread from the current window's thread
            AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, false);

            //Show and activate the window
            if (w.WindowState == WindowState.Minimized) w.WindowState = WindowState.Normal;
            w.Show();
            w.Activate();
        }

        #region Imports

        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

        [DllImport("user32.dll")]
        private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

        [DllImport("user32.dll")]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        #endregion
    }
}

我希望这段代码可以帮助遇到这个问题的其他人。

【讨论】:

  • 你好!几个月来我一直在为此苦苦挣扎!这适用于我的两种情况。惊人的! (Windows 7 x64)
  • 实际上,它似乎只有在我这样做时才有效:App.mainWindow.Show(); SystemWindows.GlobalActivate(App.mainwindow); // 当我删除第一个 .show() 时,它不会出现在前面
  • +1 for SetWindowPos(),我一直在寻找一种方法,只将我的 Window 放在前面,而不会中断其他应用程序或窃取焦点。 this.Activate() 窃取焦点。
  • 这对我来说是这样做的,在我的情况下,重点是窃取焦点,因为它发生在用户与某个元素交互时。所以非常感谢这似乎工作一致!只是调用this.Activate() 似乎只在某些时候起作用。
  • 几年前,我发现评价最高的答案 (Activate/TopMost) 有效并对其进行了投票。但现在在 Windows 10 中,我有可重复的示例,但它不起作用。幸运的是,这个答案确实适用于那些边缘情况。
【解决方案5】:

为了使它成为一个快速的复制粘贴 -
使用此类'DoOnProcess 方法将进程'主窗口移动到前台(但不能从其他窗口窃取焦点)

public class MoveToForeground
{
    [DllImportAttribute("User32.dll")]
    private static extern int FindWindow(String ClassName, String WindowName);

    const int SWP_NOMOVE        = 0x0002;
    const int SWP_NOSIZE        = 0x0001;            
    const int SWP_SHOWWINDOW    = 0x0040;
    const int SWP_NOACTIVATE    = 0x0010;
    [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
    public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);

    public static void DoOnProcess(string processName)
    {
        var allProcs = Process.GetProcessesByName(processName);
        if (allProcs.Length > 0)
        {
            Process proc = allProcs[0];
            int hWnd = FindWindow(null, proc.MainWindowTitle.ToString());
            // Change behavior by settings the wFlags params. See http://msdn.microsoft.com/en-us/library/ms633545(VS.85).aspx
            SetWindowPos(new IntPtr(hWnd), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);
        }
    }
}

HTH

【讨论】:

  • +1 这是唯一对我有用的答案。我有一个带有一个主窗口和几个浮动从属窗口的应用程序。激活其中任何一个后,所有其他窗口也应置于前面。但没有像大多数答案所暗示的那样激活/获得焦点:这是一场灾难,因为它使当前点击的窗口无法点击,因为突然另一个窗口获得焦点。
  • 有什么理由不使用process.MainWindowHandle
  • 在我的情况下,我不想要主窗口,但同意,还有其他方法可以获得hWnd。 FWIW HwndSource 对象运行良好。
【解决方案6】:

如果用户正在与另一个应用程序交互,则可能无法将您的应用程序置于最前面。作为一般规则,如果该进程已经是前台进程,则该进程只能期望设置前台窗口。 (微软在SetForegroundWindow() MSDN 条目中记录了这些限制。)这是因为:

  1. 用户“拥有”前景。例如,如果另一个程序在用户打字时偷走了前台,至少会打断她的工作流程,并且可能导致意想不到的后果,因为攻击者会误解她对一个应用程序的击键,直到她注意到更改为止.
  2. 假设两个程序中的每一个都检查其窗口是否为前景,如果不是则尝试将其设置为前景。一旦第二个程序运行,计算机就会变得毫无用处,因为每次任务切换时,前景都会在两者之间反弹。

【讨论】:

  • 好点。不过,代码的目的是与全局热键结合使用,其他应用程序会以某种方式做到这一点。
  • 必须使用 C# 中的 PInvoke 来模拟本文中描述的内容,codeproject.com/Tips/76427/…
  • 那么当我有时切换到 Visual Studio 时,为什么表达式混合错误弹出对话框仍然可见? :-/
  • Simon,我怀疑您看到的错误弹出窗口是“最顶层”窗口(我不赞成这种设计决定)。前台窗口(接收用户输入)和 Z 顺序中的“最顶层”窗口之间存在差异。任何窗口都可以使自己成为“最顶层”,这会将其置于所有非最顶层窗口的顶部,但不会像成为前景窗口那样赋予窗口键盘焦点等。
  • 这个技巧在一些特殊的窗口中失败了。 Visual Studio 和命令提示符窗口必须具有防止其他窗口成为前台窗口的功能。
【解决方案7】:

为什么这个页面上的一些答案是错误的!

  • 任何使用window.Focus() 的答案都是错误的。

    • 为什么?如果弹出通知消息,window.Focus() 将把焦点从用户当时正在输入的任何内容上移开。这对最终用户来说非常令人沮丧,尤其是在弹出窗口频繁出现的情况下。
  • 任何使用window.Activate() 的答案都是错误的。

    • 为什么?它也会使所有父窗口可见。
  • 任何省略window.ShowActivated = false 的答案都是错误的。
    • 为什么?当消息弹出时,它会将焦点从另一个窗口中移开,非常烦人!
  • 任何不使用Visibility.Visible 隐藏/显示窗口的答案都是错误的。
    • 为什么?如果我们使用Citrix,如果窗口关闭时没有折叠,它会在屏幕上留下一个奇怪的黑色矩形。因此,我们不能使用window.Show()window.Hide()

基本上:

  • 该窗口在激活时不应将焦点从任何其他窗口中抢走;
  • 窗口在显示时不应激活其父窗口;
  • 该窗口应与 Citrix 兼容。

MVVM 解决方案

此代码与 Citrix 100% 兼容(屏幕没有空白区域)。使用普通 WPF 和 DevExpress 测试。

此答案适用于我们想要一个始终位于其他窗口前面的小通知窗口(如果用户在首选项中选择此窗口)的任何用例。

如果这个答案看起来比其他答案更复杂,那是因为它是健壮的企业级代码。此页面上的其他一些答案很简单,但实际上并不奏效。

XAML - 附加属性

将此附加属性添加到窗口内的任何UserControl。附加属性将:

  • 等到Loaded 事件被触发(否则它无法查找可视化树来查找父窗口)。
  • 添加一个事件处理程序,确保窗口可见与否。

在任何时候,您都可以通过翻转附加属性的值来设置窗口是否在前面。

<UserControl x:Class="..."
         ...
         attachedProperties:EnsureWindowInForeground.EnsureWindowInForeground=
             "{Binding EnsureWindowInForeground, Mode=OneWay}">

C# - 辅助方法

public static class HideAndShowWindowHelper
{
    /// <summary>
    ///     Intent: Ensure that small notification window is on top of other windows.
    /// </summary>
    /// <param name="window"></param>
    public static void ShiftWindowIntoForeground(Window window)
    {
        try
        {
            // Prevent the window from grabbing focus away from other windows the first time is created.
            window.ShowActivated = false;

            // Do not use .Show() and .Hide() - not compatible with Citrix!
            if (window.Visibility != Visibility.Visible)
            {
                window.Visibility = Visibility.Visible;
            }

            // We can't allow the window to be maximized, as there is no de-maximize button!
            if (window.WindowState == WindowState.Maximized)
            {
                window.WindowState = WindowState.Normal;
            }

            window.Topmost = true;
        }
        catch (Exception)
        {
            // Gulp. Avoids "Cannot set visibility while window is closing".
        }
    }

    /// <summary>
    ///     Intent: Ensure that small notification window can be hidden by other windows.
    /// </summary>
    /// <param name="window"></param>
    public static void ShiftWindowIntoBackground(Window window)
    {
        try
        {
            // Prevent the window from grabbing focus away from other windows the first time is created.
            window.ShowActivated = false;

            // Do not use .Show() and .Hide() - not compatible with Citrix!
            if (window.Visibility != Visibility.Collapsed)
            {
                window.Visibility = Visibility.Collapsed;
            }

            // We can't allow the window to be maximized, as there is no de-maximize button!
            if (window.WindowState == WindowState.Maximized)
            {
                window.WindowState = WindowState.Normal;
            }

            window.Topmost = false;
        }
        catch (Exception)
        {
            // Gulp. Avoids "Cannot set visibility while window is closing".
        }
    }
}

用法

为了使用它,您需要在 ViewModel 中创建窗口:

private ToastView _toastViewWindow;
private void ShowWindow()
{
    if (_toastViewWindow == null)
    {
        _toastViewWindow = new ToastView();
        _dialogService.Show<ToastView>(this, this, _toastViewWindow, true);
    }
    ShiftWindowOntoScreenHelper.ShiftWindowOntoScreen(_toastViewWindow);
    HideAndShowWindowHelper.ShiftWindowIntoForeground(_toastViewWindow);
}

private void HideWindow()
{
    if (_toastViewWindow != null)
    {
        HideAndShowWindowHelper.ShiftWindowIntoBackground(_toastViewWindow);
    }
}

其他链接

有关如何确保通知窗口始终切换回可见屏幕的提示,请参阅我的回答:In WPF, how to shift a window onto the screen if it is off the screen?

【讨论】:

  • “企业级代码”和几行之后的catch (Exception) { }。是的,对......它使用的代码甚至没有在答案中显示,如_dialogServiceShiftWindowOntoScreenHelper。加上要求在视图模型端创建窗口(这基本上打破了整个 MVVM 模式)......
  • @Kryptos 这是企业级代码。我是从记忆中打出来的,这种精确的技术被用于一家大型 FTSE100 公司。与我们都追求的完美设计模式相比,现实生活有些不那么原始。
  • 我不喜欢我们自己在视图模型中保留一个窗口实例的事实,正如 Kryptos 提到的那样,这破坏了 mvvm 的全部意义,也许它可以在代码隐藏中完成?
  • @Igor Meszaros 同意。现在我有了更多的经验,如果我必须再做一次,我会添加一个 Behavior,并使用绑定到 ViewModel 的Func&lt;&gt; 来控制它。
【解决方案8】:

我知道这是迟到的答案,可能对研究人员有帮助

 if (!WindowName.IsVisible)
 {
     WindowName.Show();
     WindowName.Activate();
 }

【讨论】:

    【解决方案9】:

    我在一个 WPF 应用程序中遇到了类似的问题,该应用程序通过 Shell 对象从 Access 应用程序调用。

    我的解决方案如下 - 适用于 XP 和 Win7 x64,应用程序编译为 x86 目标。

    我宁愿这样做也不愿模拟一个 alt-tab。

    void Window_Loaded(object sender, RoutedEventArgs e)
    {
        // make sure the window is normal or maximised
        // this was the core of the problem for me;
        // even though the default was "Normal", starting it via shell minimised it
        this.WindowState = WindowState.Normal;
    
        // only required for some scenarios
        this.Activate();
    }
    

    【讨论】:

      【解决方案10】:

      好吧,既然这是一个如此热门的话题……这对我有用。如果我不这样做会出错,因为如果您看不到窗口,Activate() 会在您身上出错。

      Xaml:

      <Window .... 
              Topmost="True" 
              .... 
              ContentRendered="mainWindow_ContentRendered"> .... </Window>
      

      代码隐藏:

      private void mainWindow_ContentRendered(object sender, EventArgs e)
      {
          this.Topmost = false;
          this.Activate();
          _UsernameTextBox.Focus();
      }
      

      这是我让窗口显示在顶部的唯一方法。然后激活它,这样您就可以在框中键入,而无需用鼠标设置焦点。除非窗口是 Active(),否则 control.Focus() 将不起作用;

      【讨论】:

      • 适用于我之前显示的初始屏幕。谢谢。
      【解决方案11】:

      好吧,我想出了一个解决办法。我正在从用于实现热键的键盘挂钩进行调用。如果我暂停将其放入 BackgroundWorker,则该调用将按预期工作。这是一个杂牌,但我不知道为什么它最初不起作用。

      void hotkey_execute()
      {
          IntPtr handle = new WindowInteropHelper(Application.Current.MainWindow).Handle;
          BackgroundWorker bg = new BackgroundWorker();
          bg.DoWork += new DoWorkEventHandler(delegate
              {
                  Thread.Sleep(10);
                  SwitchToThisWindow(handle, true);
              });
          bg.RunWorkerAsync();
      }
      

      【讨论】:

      • 只是感兴趣:您是否尝试过 Window.Activate(按照 Morten 的建议)和其他建议?他们似乎没有这个公认的杂牌那么老套。
      • 这是很久以前的事了,但是是的,当时我确实尝试过
      • 这不适用于我的 Windows XP。我推荐@Matthew Xavier 的回答。
      【解决方案12】:

      要显示任何当前打开的窗口,请导入这些 DLL:

      public partial class Form1 : Form
      {
          [DllImportAttribute("User32.dll")]
          private static extern int FindWindow(String ClassName, String WindowName);
          [DllImportAttribute("User32.dll")]
          private static extern int SetForegroundWindow(int hWnd);
      

      并且在程序中我们搜索具有指定标题的应用(写标题不带首字母(索引> 0))

        foreach (Process proc in Process.GetProcesses())
                      {
                          tx = proc.MainWindowTitle.ToString();
                          if (tx.IndexOf("Title of Your app WITHOUT FIRST LETTER") > 0)
                          {
                              tx = proc.MainWindowTitle;
                              hWnd = proc.Handle.ToInt32(); break;
                          }
                      }
                      hWnd = FindWindow(null, tx);
                      if (hWnd > 0)
                      {
                          SetForegroundWindow(hWnd);
                      }
      

      【讨论】:

      • “你的应用的标题没有第一个字母” 哎呀,hacky hacky hacky。为什么不正确使用IndexOf
      【解决方案13】:

      只是想为这个问题添加另一个解决方案。此实现适用于我的场景,其中 CaliBurn 负责显示主窗口。

      protected override void OnStartup(object sender, StartupEventArgs e)
      {
          DisplayRootViewFor<IMainWindowViewModel>();
      
          Application.MainWindow.Topmost = true;
          Application.MainWindow.Activate();
          Application.MainWindow.Activated += OnMainWindowActivated;
      }
      
      private static void OnMainWindowActivated(object sender, EventArgs e)
      {
          var window = sender as Window;
          if (window != null)
          {
              window.Activated -= OnMainWindowActivated;
              window.Topmost = false;
              window.Focus();
          }
      }
      

      【讨论】:

        【解决方案14】:

        问题可能是从钩子调用您的代码的线程尚未被运行时初始化,因此调用运行时方法不起作用。

        也许您可以尝试执行 Invoke 以将您的代码编组到 UI 线程,以调用将窗口置于前台的代码。

        【讨论】:

          【解决方案15】:

          这些代码在任何时候都可以正常工作。

          首先在 XAML 中设置激活的事件处理程序:

          Activated="Window_Activated"
          

          将下面的行添加到您的主窗口构造函数块中:

          public MainWindow()
          {
              InitializeComponent();
              this.LocationChanged += (sender, e) => this.Window_Activated(sender, e);
          }
          

          并在激活的事件处理程序中复制以下代码:

          private void Window_Activated(object sender, EventArgs e)
          {
              if (Application.Current.Windows.Count > 1)
              {
                  foreach (Window win in Application.Current.Windows)
                      try
                      {
                          if (!win.Equals(this))
                          {
                              if (!win.IsVisible)
                              {
                                  win.ShowDialog();
                              }
          
                              if (win.WindowState == WindowState.Minimized)
                              {
                                  win.WindowState = WindowState.Normal;
                              }
          
                              win.Activate();
                              win.Topmost = true;
                              win.Topmost = false;
                              win.Focus();
                          }
                      }
                      catch { }
              }
              else
                  this.Focus();
          }
          

          这些步骤可以正常工作,并将所有其他窗口置于其父窗口的前面。

          【讨论】:

            【解决方案16】:

            如果您试图隐藏窗口,例如最小化窗口,我发现使用

                this.Hide();
            

            将正确隐藏它,然后只需使用

                this.Show();
            

            然后将再次将窗口显示为最顶部的项目。

            【讨论】:

              【解决方案17】:

              记住不要将显示该窗口的代码放在 PreviewMouseDoubleClick 处理程序中,因为活动窗口将切换回处理事件的窗口。 只需将它放在 MouseDoubleClick 事件处理程序中或通过将 e.Handled 设置为 True 来停止冒泡。

              在我的情况下,我正在处理 Listview 上的 PreviewMouseDoubleClick 并且没有设置 e.Handled = true 然后它引发了 MouseDoubleClick 事件女巫坐在焦点回到原来的窗口。

              【讨论】:

                【解决方案18】:

                这是上面几个建议的组合,效果很好而且很简单。只有当这些事件触发时它才会出现在最前面,因此事件之后弹出的任何窗口当然都会保持在最前面。

                public partial class MainWindow : Window
                {
                    protected override void OnContentRendered(EventArgs e)
                    {
                        base.OnContentRendered(e);
                
                        Topmost = true;
                        Topmost = false;
                    }
                    protected override void OnInitialized(EventArgs e)
                    {
                        base.OnInitialized(e);
                
                        Topmost = true;
                        Topmost = false;
                    }
                
                    ....
                }
                

                【讨论】:

                  【解决方案19】:

                  我构建了一个扩展方法以便于重用。

                  using System.Windows.Forms;
                      namespace YourNamespace{
                          public static class WindowsFormExtensions {
                              public static void PutOnTop(this Form form) {
                                  form.Show();
                                  form.Activate();
                              }// END PutOnTop()       
                          }// END class
                      }// END namespace
                  

                  在表单构造函数中调用

                  namespace YourNamespace{
                         public partial class FormName : Form {
                         public FormName(){
                              this.PutOnTop();
                              InitalizeComponents();
                          }// END Constructor
                      } // END Form            
                  }// END namespace
                  

                  【讨论】:

                  • 嗨,迈克。你回答这个问题很晚。您能否在回答中解释为什么这种方法与已经针对该问题发布的非常好的答案不同(也许更好)?
                  • 迟到了,因为我只是需要这样做,我遇到了这个并想分享我是如何解决这个问题的,以防其他人想使用它。
                  • 当然,我被选中审查您的帖子并想让您知道。如果您认为这是对社区的良好贡献,提供新答案总是好的。
                  • 此问题专门针对 WPF,但您的解决方案适用于 WinForms。
                  猜你喜欢
                  • 1970-01-01
                  • 2011-11-04
                  • 2012-10-08
                  • 2017-12-02
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多