web-dev-qa-db-ja.com

.NET / Windowsフォーム:ウィンドウのサイズと場所を記憶する

通常のウィンドウを持つWindowsフォームアプリケーションがあります。ここで、アプリケーションを閉じて再起動すると、メインウィンドウが閉じられた瞬間と同じサイズで画面上の同じ場所に表示されます。

Windows Formsで画面の場所とウィンドウサイズ(および可能であればウィンドウの状態)を覚える簡単な方法はありますか、それともすべてを手作業で行う必要がありますか?

39
clamp

アプリケーションの設定でウィンドウの場所とサイズを保存する必要があります。方法を示すための良いC# 記事 を次に示します。

[〜#〜] edit [〜#〜]

アプリケーションの設定には、必要なものをほとんど保存できます。設定グリッドの[タイプ]列で、任意の.NETタイプを参照できます。 WindowStateはSystem.Windows.Formsにあり、FormWindowStateとしてリストされています。 FormStartPositionのプロパティもあります。

33
Walter

このコードを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)
{
    WindowState = FormWindowState.Maximized;
    Location = Properties.Settings.Default.Location;
    Size = Properties.Settings.Default.Size;
}
else if (Properties.Settings.Default.Minimised)
{
    WindowState = FormWindowState.Minimized;
    Location = Properties.Settings.Default.Location;
    Size = Properties.Settings.Default.Size;
}
else
{
    Location = Properties.Settings.Default.Location;
    Size = Properties.Settings.Default.Size;
}

最後の状態を復元します。

さらに、アプリケーションが最大化されたマルチモニター設定のモニターを覚えています。

57
ChrisF

以前のソリューションではうまくいきませんでした。しばらくプレイした後、次のコードになりました:

  • 最大化された通常の状態を保持
  • 最小化された状態をデフォルトの位置に置き換えます
  • 画面サイズが変更された場合(モニターの切り離し、リモート接続など)、アプリケーションが画面外で開かれた状態でユーザーをイライラさせることはありません。

    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は意図的に間違っているため、最初の起動時にアプリケーションはデフォルトの場所を使用します。

3
jing

いくつかの異なる方法を試しました。これが最終的に私のために働いたものです。 (この場合-最初の起動時-デフォルトはまだ永続化されていないため、フォームはデザイナーで設定された値を使用します)

  1. プロジェクトに設定を追加します(手動-Visual Studioに依存しないでください): Properties.Settings

  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();
    }
    
3
darksider474

複数のフォームがある場合は、このようなものを使用できます...

この部分をすべてload load voidで追加します

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

マット-ユーザー設定としてWindowStateを保存するには、設定ダイアログの[タイプ]ドロップダウンで、一番下までスクロールして[参照]を選択します。

[タイプの選択]ダイアログでSystem.Windows.Formsを展開し、タイプとして[FormWindowState]を選択できます。

(申し訳ありませんが、コメントにコメントできるボタンは表示されません...)

2
JohnForDummies

情報を手動でどこかに保存する必要があります。アプリケーション設定として、ユーザー固有の分離ストレージに保存することをお勧めします。

ロードしたら、設定を読み、フォームのサイズを変更/移動します。

1
Ian

素晴らしいオープンソースライブラリ-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 license)の下)。

1
anakic

フォームを閉じるときに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>
0
Deniz

私はこれまでこの方法を使用してきましたが、うまく機能しています。アプリケーション設定をいじる必要はありません。代わりに、シリアル化を使用して設定ファイルを作業ディレクトリに書き込みます。私は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; }
}

そのコードを一度書くだけで、二度と混乱することはありません。ここからが楽しみです。フォームのLoadイベントとFormClosingイベントに次のコードを追加します。

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

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

あなたがする必要があるのはそれだけです。唯一のセットアップは、これらの拡張機能を共通のクラスに入れることです。その後、フォームのコードに2行のコードを追加するだけで完了です。

このコードは、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));
}

場所と状態のみを保存しますが、これを変更して、フォームの高さと幅などを記憶することができます。一度変更するだけで、それを呼び出すすべてのアプリケーションで機能します。

0

私の答えは ChrisF♦ 's answer から変更されていますが、私が気に入らないことを修正しました-閉じたときにウィンドウが最小化されている場合、次の起動時に最小化されて表示されます。

私のコードは、ウィンドウが最小化されたときにウィンドウが最大化されたか正常であったかを記憶し、それに応じて永続状態を設定することにより、そのケースを正しく処理します。

残念ながら、Winformsはその情報を直接公開しないため、WndProcをオーバーライドして自分で保存する必要がありました。 現在最小化されているウィンドウが最小化時に最大化または通常の状態になっているかどうかを確認する

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