【问题标题】:.NET / Windows Forms: remember windows size and location.NET / Windows Forms:记住窗口大小和位置
【发布时间】:2010-12-24 19:47:44
【问题描述】:

我有一个带有普通窗口的 Windows 窗体应用程序。现在,当我关闭应用程序并重新启动它时,我希望主窗口以与关闭时相同的大小出现在屏幕上的相同位置。

在 Windows 窗体中是否有一种简单的方法可以记住屏幕位置和窗口大小(如果可能,还可以记住窗口状态)还是必须手动完成所有操作?

【问题讨论】:

  • 这个问题之前已经被问过并在这里得到了回答:(这是一个很好的答案。如果你使用它,一定要给@Joe 一个赞成票。)这不是一个完全重复的 - 那个人没有问关于窗口大小,但你应该能够从那里推断出来。 stackoverflow.com/questions/105932/…
  • 删除了我的评论以及您的帖子... 呵呵。我不同意,因为该解决方案使用了 UserPreferencesManager,据我所知,这是一个正在使用的自定义类。相信问题是问你将如何实现这样的 UserPreferencesManager 类。
  • 请注意,如果用户的屏幕尺寸可变(例如,他有时将显示器连接到 nb 或远程连接不同的屏幕尺寸),当您的应用程序出现在屏幕边界之外时,这种尝试可能会导致令人沮丧的状态。查看stackoverflow.com/questions/105932/… 了解更复杂的解决方案。

标签: c# .net winforms window


【解决方案1】:

如果您将此代码添加到您的 FormClosing 事件处理程序:

if (WindowState == FormWindowState.Maximized)
{
    Properties.Settings.Default.Location = RestoreBounds.Location;
    Properties.Settings.Default.Size = RestoreBounds.Size;
    Properties.Settings.Default.Maximised = true;
    Properties.Settings.Default.Minimised = false;
}
else if (WindowState == FormWindowState.Normal)
{
    Properties.Settings.Default.Location = Location;
    Properties.Settings.Default.Size = Size;
    Properties.Settings.Default.Maximised = false;
    Properties.Settings.Default.Minimised = false;
}
else
{
    Properties.Settings.Default.Location = RestoreBounds.Location;
    Properties.Settings.Default.Size = RestoreBounds.Size;
    Properties.Settings.Default.Maximised = false;
    Properties.Settings.Default.Minimised = true;
}
Properties.Settings.Default.Save();

它将保存当前状态。

然后将此代码添加到表单的 OnLoad 处理程序中:

if (Properties.Settings.Default.Maximised)
{
    Location = Properties.Settings.Default.Location;
    WindowState = FormWindowState.Maximized;
    Size = Properties.Settings.Default.Size;
}
else if (Properties.Settings.Default.Minimised)
{
    Location = Properties.Settings.Default.Location;
    WindowState = FormWindowState.Minimized;
    Size = Properties.Settings.Default.Size;
}
else
{
    Location = Properties.Settings.Default.Location;
    Size = Properties.Settings.Default.Size;
}

它将恢复上一个状态。

它甚至会记住应用程序被最大化到多显示器设置中的哪个显示器。

【讨论】:

  • 对于那些尚未使用 Properties.Settings 的人,您需要进入项目属性并转到设置选项卡。创建一个默认设置文件并添加一个名为 Location 的属性和一个名为 Size 的属性。
  • 如果表单被最小化,您的位置存储代码将失败。您还需要检查 FormWindowState.Minimized 。 “最大化”是“最大化”的错字吗?
  • @EricLaw - “最大化”是属性的名称。我想它应该是“最大化”以保持一致性。重新检查“最小化” - 这是我从中获取的应用程序中没有的东西。
  • 你需要设置WindowState 之后设置Location。否则,窗口将始终在默认监视器上最大化,因为最大化位置基于您设置 WindowState 时的位置。这意味着当用户在他的辅助显示器上最大化它时,它会在主显示器上恢复。
  • 很好的解决方案。如果您在设置中将 WindowState 保存为 System.Windows.Forms.FormWindowState 类型,它将保存具有最大和最小标志。 X
【解决方案2】:

您需要在应用程序设置中保存窗口位置和大小。这是一个很好的 C# article 向您展示如何操作。

编辑

您可以在应用程序设置中保存几乎所有您想要的内容。在设置网格的类型列中,您可以浏览到任何 .NET 类型。 WindowState 位于 System.Windows.Forms 中,并列为 FormWindowState。 FormStartPosition 还有一个属性。

【讨论】:

  • 谢谢,这是一篇好文章。剩下的唯一一件事就是记住窗口状态(最大化,最小化,...)我怎样才能把它放到设置中?
  • 好的,谢谢。一开始我没有意识到您可以在这些设置中浏览到所有类型的 .NET。
  • 谢谢!还有一件事:如果窗口最大化,我怎么能记住在多显示器设置中它是在哪个显示器上最大化的?
  • 将监视器 ID 也添加到应用程序设置中。
  • 谢谢,但我找不到获取监视器 ID 的方法。我只找到了 Screen 对象,但它似乎没有与之相关的独特东西。
【解决方案3】:

我尝试了几种不同的方法;这就是最终为我工作的原因。 (在这种情况下 - 在首次启动时 - 尚未保留默认值,因此表单将使用设计器中设置的值)

  1. 将设置添加到项目中(手动 - 不要依赖 Visual Studio):

  2. 将以下代码添加到您的表单中:

    private void Form1_Load(object sender, EventArgs e)
    {
        this.RestoreWindowPosition();
    }
    
    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        this.SaveWindowPosition();
    }
    
    private void RestoreWindowPosition()
    {
        if (Settings.Default.HasSetDefaults)
        {
            this.WindowState = Settings.Default.WindowState;
            this.Location = Settings.Default.Location;
            this.Size = Settings.Default.Size;
        }
    }
    
    private void SaveWindowPosition()
    {
        Settings.Default.WindowState = this.WindowState;
    
        if (this.WindowState == FormWindowState.Normal)
        {
            Settings.Default.Location = this.Location;
            Settings.Default.Size = this.Size;
        }
        else
        {
            Settings.Default.Location = this.RestoreBounds.Location;
            Settings.Default.Size = this.RestoreBounds.Size;
        }
    
        Settings.Default.HasSetDefaults = true;
    
        Settings.Default.Save();
    }
    

【讨论】:

    【解决方案4】:

    以前的解决方案对我不起作用。玩了一段时间后,我得到了以下代码:

    • 保持最大化和正常状态
    • 用默认位置替换最小化状态
    • 如果屏幕尺寸发生变化(分离的显示器、远程连接等),它不会让用户在屏幕外打开应用程序时陷入沮丧状态。

      private void MyForm_Load(object sender, EventArgs e)
      {
          if (Properties.Settings.Default.IsMaximized)
              WindowState = FormWindowState.Maximized;
          else if (Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(Properties.Settings.Default.WindowPosition)))
          {
              StartPosition = FormStartPosition.Manual;
              DesktopBounds = Properties.Settings.Default.WindowPosition;
              WindowState = FormWindowState.Normal;
          }
      }
      
      private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
      {
          Properties.Settings.Default.IsMaximized = WindowState == FormWindowState.Maximized;
          Properties.Settings.Default.WindowPosition = DesktopBounds;
          Properties.Settings.Default.Save();
      }
      

    用户设置:

        <userSettings>
        <WindowsFormsApplication2.Properties.Settings>
            <setting name="WindowPosition" serializeAs="String">
                <value>0, 0, -1, -1</value>
            </setting>
            <setting name="IsMaximized" serializeAs="String">
                <value>False</value>
            </setting>
        </WindowsFormsApplication2.Properties.Settings>
    </userSettings>
    

    注意:WindowsPosition 是故意错误的,所以在首次启动应用程序时将使用默认位置。

    请注意,IntersectsWith 需要一个矩形,而不是一个点。因此,与其他答案不同,此答案是将 DesktopBounds 而不是 Location 保存到 Properties.Settings.Default.WindowPosition

    【讨论】:

    • 这个对我有用,但如果窗口在第二个屏幕上最大化,它会在主屏幕上恢复为最大化。桌面边界应该在设置状态之前恢复(如果它相交)。
    【解决方案5】:

    Matt - 将 WindowState 保存为用户设置,在设置对话框的“类型”下拉菜单中,滚动到底部并选择“浏览”。

    在“选择类型”对话框中,展开 System.Windows.Forms,您可以选择“FormWindowState”作为类型。

    (抱歉,我没有看到允许我对评论发表评论的按钮...)

    【讨论】:

    • 再次,对不起,我无法在其他线程中添加评论...但是马特,通过设置表单的位置,它会将其移动到正确的屏幕,并且然后您可以将 WindowState 设置为 Maximized 等,它将显示在正确的屏幕上。
    【解决方案6】:

    如果您有超过 1 个表单,您可以使用类似这样的内容...

    添加这部分所有表单加载无效

    var AbbA = Program.LoadFormLocationAndSize(this);
                this.Location = new Point(AbbA[0], AbbA[1]);
                this.Size = new Size(AbbA[2], AbbA[3]);
                this.FormClosing += new FormClosingEventHandler(Program.SaveFormLocationAndSize);
    

    将表单位置和大小保存到 app.config xml

    public static void SaveFormLocationAndSize(object sender, FormClosingEventArgs e)
    {
        Form xForm = sender as Form;
        Configuration config = ConfigurationManager.OpenExeConfiguration(Application.ExecutablePath);
        if (ConfigurationManager.AppSettings.AllKeys.Contains(xForm.Name))
            config.AppSettings.Settings[xForm.Name].Value = String.Format("{0};{1};{2};{3}", xForm.Location.X, xForm.Location.Y, xForm.Size.Width, xForm.Size.Height);
        else
            config.AppSettings.Settings.Add(xForm.Name, String.Format("{0};{1};{2};{3}", xForm.Location.X, xForm.Location.Y, xForm.Size.Width, xForm.Size.Height));
        config.Save(ConfigurationSaveMode.Full);
    }
    

    从 app.config xml 加载表单位置和大小

    public static int[] LoadFormLocationAndSize(Form xForm)
    {
        int[] LocationAndSize = new int[] { xForm.Location.X, xForm.Location.Y, xForm.Size.Width, xForm.Size.Height };
        //---//
        try
        {
            Configuration config = ConfigurationManager.OpenExeConfiguration(Application.ExecutablePath);
            var AbbA = config.AppSettings.Settings[xForm.Name].Value.Split(';');
            //---//
            LocationAndSize[0] = Convert.ToInt32(AbbA.GetValue(0));
            LocationAndSize[1] = Convert.ToInt32(AbbA.GetValue(1));
            LocationAndSize[2] = Convert.ToInt32(AbbA.GetValue(2));
            LocationAndSize[3] = Convert.ToInt32(AbbA.GetValue(3));
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
        //---//
        return LocationAndSize;
    }
    

    【讨论】:

      【解决方案7】:

      如果您使用出色的开源库 - Jot,您可以忘记繁琐的 .settings 文件,只需执行以下操作:

      public MainWindow()
      {
          InitializeComponent();
      
          _stateTracker.Configure(this)
              .IdentifyAs("MyMainWindow")
              .AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState))
              .RegisterPersistTrigger(nameof(Closed))
              .Apply();
      }
      

      还有一个 Nuget 包,您可以配置关于数据存储方式/时间/位置的几乎所有内容。

      免责声明:我是作者,但该库是完全开源的(在 MIT 许可下)。

      【讨论】:

      • Jot 是业余解决方案。让您的表单记住第二个监视器上的位置,禁用监视器,您将看不到您的表单。在某些情况下,您永远不会看到它,因为 Windows 窗口定位按钮可能会被忽略。现在想象一下快乐的客户和支持团队。我还没有研究过如何改变表单的布局。怎么回事?如果 StartPosition 属性未设置为 Manual,则 Location 属性上的 Jot 活动将被忽略。等等等等。但这是一个不错的尝试。顺其自然吧。
      • 嘿。多个显示器(拔出的场景)在“真实世界窗体/窗口跟踪”部分的 github 自述文件中得到解决,并使用回调处理特殊情况,如最小化窗口。出于说明目的,上面的代码很简单。它也有点过时了。如果您准备针对任何特定场景提交问题(或拉取请求),那就太好了。
      • 我应该将它视为一个库,而不是一个框架。我会阅读自述文件。非常感谢。
      • 是的,大约 500 行没有空格和 cmets 的代码,所以它相当小。我肯定会称它为库,即使它可以与 ioc 容器集成并作为基础架构工作。
      【解决方案8】:

      您必须手动将信息保存在某处。我建议这样做作为应用程序设置,将它们存储在用户特定的隔离存储中。

      加载后,阅读设置然后调整/移动表单。

      【讨论】:

        【解决方案9】:

        我的回答改编自 ChrisF♦'s answer,但我已经修复了一件我不喜欢的事情 - 如果窗口在关闭时被最小化,它会在下次启动时显示为最小化。

        我的代码通过记住窗口在最小化时是最大化还是正常,并相应地设置持久状态来正确处理这种情况。

        不幸的是,Winforms 不直接公开该信息,因此我需要重写 WndProc 并自己存储它。见Check if currently minimized window was in maximized or normal state at the time of minimization

        partial class Form1 : Form
        {
            protected override void WndProc(ref Message m)
            {
                if (m.Msg == WM_SYSCOMMAND)
                {
                    int wparam = m.WParam.ToInt32() & 0xfff0;
        
                    if (wparam == SC_MAXIMIZE)
                        LastWindowState = FormWindowState.Maximized;
                    else if (wparam == SC_RESTORE)
                        LastWindowState = FormWindowState.Normal;
                }
        
                base.WndProc(ref m);
            }
        
            private const int WM_SYSCOMMAND = 0x0112;
            private const int SC_MAXIMIZE = 0xf030;
            private const int SC_RESTORE = 0xf120;
            private FormWindowState LastWindowState;
        
            private void Form1_FormClosing(object sender, FormClosingEventArgs e)
            {
                if (WindowState == FormWindowState.Normal)
                {
                    Properties.Settings.Default.WindowLocation = Location;
                    Properties.Settings.Default.WindowSize = Size;
                }
                else
                {
                    Properties.Settings.Default.WindowLocation = RestoreBounds.Location;
                    Properties.Settings.Default.WindowSize = RestoreBounds.Size;
                }
        
                if (WindowState == FormWindowState.Minimized)
                {
                    Properties.Settings.Default.WindowState = LastWindowState;
                }
                else
                {
                    Properties.Settings.Default.WindowState = WindowState;
                }
        
                Properties.Settings.Default.Save();
            }
        
            private void Form1_Load(object sender, EventArgs e)
            {
                if (Properties.Settings.Default.WindowSize != new Size(0, 0))
                {
                    Location = Properties.Settings.Default.WindowLocation;
                    Size = Properties.Settings.Default.WindowSize;
                    WindowState = Properties.Settings.Default.WindowState;
                }
            }
        

        【讨论】:

          【解决方案10】:

          您也可以在关闭表单时将其保存在(比如说)config.xml 中:

          private void Form1_FormClosed(object sender, FormClosedEventArgs e)
          {
              XmlDocument docConfigPath = new XmlDocument();
              docConfigPath.Load(XML_Config_Path);
          
              WriteNode(new string[] { "config", "Size", "Top", Top.ToString() }, docConfigPath);
              WriteNode(new string[] { "config", "Size", "Left", Left.ToString() }, docConfigPath);
              WriteNode(new string[] { "config", "Size", "Height", Height.ToString() }, docConfigPath);
              WriteNode(new string[] { "config", "Size", "Width", Width.ToString() }, docConfigPath);
          
              docConfigPath.Save(XML_Config_Path);
          }
          
          public static XmlNode WriteNode(string[] sNode, XmlDocument docConfigPath)
          {
              int cnt = sNode.Length;
              int iNode = 0;
              string sNodeNameLast = "/" + sNode[0];
              string sNodeName = "";
              XmlNode[] xN = new XmlNode[cnt];
          
              for (iNode = 1; iNode < cnt - 1; iNode++)
              {
                  sNodeName = "/" + sNode[iNode];
                  xN[iNode] = docConfigPath.SelectSingleNode(sNodeNameLast + sNodeName);
                  if (xN[iNode] == null)
                  {
                      xN[iNode] = docConfigPath.CreateNode("element", sNode[iNode], "");
                      xN[iNode].InnerText = "";
                      docConfigPath.SelectSingleNode(sNodeNameLast).AppendChild(xN[iNode]);
                  }
                  sNodeNameLast += sNodeName;
              }
          
              if (sNode[cnt - 1] != "")
                  xN[iNode - 1].InnerText = sNode[cnt - 1];
          
              return xN[cnt - 2];
          }
          

          加载在你的:

          private void Form1_Load(object sender, EventArgs e)
          {
              XmlDocument docConfigPath = new XmlDocument();
              docConfigPath.Load(XML_Config_Path);
              XmlNodeList nodeList = docConfigPath.SelectNodes("config/Size");
          
              Height = ReadNodeInnerTextAsNumber("config/Size/Height", docConfigPath);
              Width = ReadNodeInnerTextAsNumber("config/Size/Width", docConfigPath);
              Top = ReadNodeInnerTextAsNumber("config/Size/Top", docConfigPath);
              Left = ReadNodeInnerTextAsNumber("config/Size/Left", docConfigPath);
          }
          

          config.xml 应包含以下内容:

          <?xml version="1.0" encoding="utf-8"?>
          <config>
            <Size>
              <Height>800</Height>
              <Width>1400</Width>
              <Top>100</Top>
              <Left>280</Left>
            </Size>
          </config>
          

          【讨论】:

            【解决方案11】:

            到目前为止,我一直在使用这种方法,并且效果很好。您不必摆弄应用程序设置。相反,它使用序列化将设置文件写入您的工作目录。我使用 JSON,但您可以使用 .NET 的原生 XML 序列化或任何序列化。

            将这些静态方法放在一个通用的扩展类中。如果您有一个被多个项目引用的通用扩展项目,则可以加分:

            const string WINDOW_STATE_FILE = "windowstate.json";
            
            public static void SaveWindowState(Form form)
            {
                var state = new WindowStateInfo
                {
                    WindowLocation = form.Location,
                    WindowState = form.WindowState
                };
            
                File.WriteAllText(WINDOW_STATE_FILE, JsonConvert.SerializeObject(state));
            }
            
            public static void LoadWindowState(Form form)
            {
                if (!File.Exists(WINDOW_STATE_FILE)) return;
            
                var state = JsonConvert.DeserializeObject<WindowStateInfo>(File.ReadAllText(WINDOW_STATE_FILE));
            
                if (state.WindowState.HasValue) form.WindowState = state.WindowState.Value;
                if (state.WindowLocation.HasValue) form.Location = state.WindowLocation.Value;
            }
            
            public class WindowStateInfo
            {
                public FormWindowState? WindowState { get; set; }
            
                public Point? WindowLocation { get; set; }
            }
            

            您只需要编写一次该代码,再也不会乱来。现在是有趣的部分:将以下代码放入表单的 LoadFormClosing 事件中,如下所示:

            private void Form1_Load(object sender, EventArgs e)
            {
                WinFormsGeneralExtensions.LoadWindowState(this);
            }
            
            private void Form1_FormClosing(object sender, FormClosingEventArgs e)
            {
                WinFormsGeneralExtensions.SaveWindowState(this);
            }
            

            这就是你需要做的。唯一的设置是将这些扩展放入一个通用类中。之后,只需在表单代码中添加两行代码即可。

            只有当您的 WinForm 应用程序只有一个表单时,此代码才会真正起作用。如果您想记住多个表格的位置,则需要发挥创意并执行以下操作:

            public static void SaveWindowState(Form form)
            {
                var state = new WindowStateInfo
                {
                    WindowLocation = form.Location,
                    WindowState = form.WindowState
                };
            
                File.WriteAllText($"{form.Name} {WINDOW_STATE_FILE}", JsonConvert.SerializeObject(state));
            }
            

            我只保存位置和状态,但您可以修改它以记住表单的高度和宽度或其他任何内容。只需进行一次更改,它将适用于任何调用它的应用程序。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-05-23
              • 1970-01-01
              • 2011-02-19
              • 2010-10-25
              • 1970-01-01
              • 2022-07-05
              • 2010-10-10
              • 2015-03-06
              相关资源
              最近更新 更多