9

フォームが継承できるクラスを作成し、フォームの場所、サイズ、および状態を処理します。そして、それはうまく機能します。1つのことを除いて:

メイン画面とは異なる画面でアプリケーションを最大化すると、(最大化する前の) 場所とサイズは正しく保存されますが、(以前の状態に従って) 最大化すると、メイン モニターで最大化されます。その後、通常の状態に戻すと、以前の別の画面に移動します。もう一度最大化すると、もちろん正しい画面で最大化されました。

だから私の質問は...どうすればフォームを作成できますか?最大化されたときに、どの画面で最大化されたかを覚えていますか? また、フォームが再び開いたときにそれを復元するにはどうすればよいですか?


問題に対する完全な解決策の種類

画面に表示されている場合の方法について非常に良いヒントが含まれている回答を受け入れました。しかし、それは私の問題の一部に過ぎなかったので、ここに私の解決策があります:

ロード中

  1. 最初に保存されBoundsWindowState任意のストレージから取得します。
  2. を設定しBoundsます。
  3. またはBoundsのいずれかで表示されていることを確認してください。 Screen.AllScreens.Any(ø => ø.Bounds.IntersectsWith(Bounds))MdiParent.Controls.OfType<MdiClient>().First().ClientRectangle.IntersectsWith(Bounds)
    • そうでない場合は、実行してくださいLocation = new Point();
  4. 次に、ウィンドウの状態を設定します。

閉店時

  1. 保存しWindowStateます。
  2. である場合WindowStateFormWindowState.Normal保存Boundsし、そうでない場合は保存しRestoreBoundsます。

以上です!=)

いくつかのサンプルコード

したがって、 Oliverが示唆するように、ここにいくつかのコードがあります。肉付けする必要がありますが、これは、次のことを望む人のための出発点として使用できます。

PersistentFormHandler

データをどこかに保存して取得します。

public sealed class PersistentFormHandler
{
    /// <summary>The form identifier in storage.</summary>
    public string Name { get; private set; }


    /// <summary>Gets and sets the window state. (int instead of enum so that it can be in a BI layer, and not require a reference to WinForms)</summary>
    public int WindowState { get; set; }


    /// <summary>Gets and sets the window bounds. (X, Y, Width and Height)</summary>
    public Rectangle WindowBounds { get; set; }


    /// <summary>Dictionary for other values.</summary>
    private readonly Dictionary<string, Binary> otherValues;


    /// <summary>
    /// Instantiates new persistent form handler.
    /// </summary>
    /// <param name="windowType">The <see cref="Type.FullName"/> will be used as <see cref="Name"/>.</param>
    /// <param name="defaultWindowState">Default state of the window.</param>
    /// <param name="defaultWindowBounds">Default bounds of the window.</param>
    public PersistentFormHandler(Type windowType, int defaultWindowState, Rectangle defaultWindowBounds)
        : this(windowType, null, defaultWindowState, defaultWindowBounds) { }

    /// <summary>
    /// Instantiates new persistent form handler.
    /// </summary>
    /// <param name="windowType">The <see cref="Type.FullName"/> will be used as base <see cref="Name"/>.</param>
    /// <param name="id">Use this if you need to separate windows of same type. Will be appended to <see cref="Name"/>.</param>
    /// <param name="defaultWindowState">Default state of the window.</param>
    /// <param name="defaultWindowBounds">Default bounds of the window.</param>
    public PersistentFormHandler(Type windowType, string id, int defaultWindowState, Rectangle defaultWindowBounds)
    {
        Name = string.IsNullOrEmpty(id) 
            ? windowType.FullName 
            : windowType.FullName + ":" + id;

        WindowState = defaultWindowState;
        WindowBounds = defaultWindowBounds;

        otherValues = new Dictionary<string, Binary>();
    }


    /// <summary>
    /// Looks for previously stored values in database.
    /// </summary>
    /// <returns>False if no previously stored values were found.</returns>
    public bool Load()
    {
        // See Note 1
    }

    /// <summary>
    /// Stores all values in database
    /// </summary>
    public void Save()
    {
        // See Note 2
    }

    /// <summary>
    /// Adds the given <paramref key="value"/> to the collection of values that will be
    /// stored in database on <see cref="Save"/>.
    /// </summary>
    /// <typeparam key="T">Type of object.</typeparam>
    /// <param name="key">The key you want to use for this value.</param>
    /// <param name="value">The value to store.</param>
    public void Set<T>(string key, T value)
    {
        // Create memory stream
        using (var s = new MemoryStream())
        {
            // Serialize value into binary form
            var b = new BinaryFormatter();
            b.Serialize(s, value);

            // Store in dictionary
            otherValues[key] = new Binary(s.ToArray());
        }
    }

    /// <summary>
    /// Same as <see cref="Get{T}(string,T)"/>, but uses default(<typeparamref name="T"/>) as fallback value.
    /// </summary>
    /// <typeparam name="T">Type of object</typeparam>
    /// <param name="key">The key used on <see cref="Set{T}"/>.</param>
    /// <returns>The stored object, or the default(<typeparamref name="T"/>) object if something went wrong.</returns>
    public T Get<T>(string key)
    {
        return Get(key, default(T));
    }

    /// <summary>
    /// Gets the value identified by the given <paramref name="key"/>.
    /// </summary>
    /// <typeparam name="T">Type of object</typeparam>
    /// <param name="key">The key used on <see cref="Set{T}"/>.</param>
    /// <param name="fallback">Value to return if the given <paramref name="key"/> could not be found.
    /// In other words, if you haven't used <see cref="Set{T}"/> yet.</param>
    /// <returns>The stored object, or the <paramref name="fallback"/> object if something went wrong.</returns>
    public T Get<T>(string key, T fallback)
    {
        // If we have a value with this key
        if (otherValues.ContainsKey(key))
        {
            // Create memory stream and fill with binary version of value
            using (var s = new MemoryStream(otherValues[key].ToArray()))
            {
                try
                {
                    // Deserialize, cast and return.
                    var b = new BinaryFormatter();
                    return (T)b.Deserialize(s);
                }
                catch (InvalidCastException)
                {
                    // T is not what it should have been
                    // (Code changed perhaps?)
                }
                catch (SerializationException)
                {
                    // Something went wrong during Deserialization
                }
            }
        }

        // Else return fallback
        return fallback;
    }
}

注 1:WindowState load メソッドでは、以前に保存されたWindowBoundsおよびその他の値を探す必要があります。SQL Server を使用し、 、、( の場合)、、、、、のWindow列を持つテーブルがあります。したがって、ウィンドウごとに、ユーザーとマシンごとに 、、、を含む 1 つの行が作成されます。さらに、 への外部キー、タイプの列、およびタイプの列を持つテーブルがあります。見つからないものがある場合は、デフォルトのままにして false を返します。IdNameMachineNameEnvironment.MachineNameUserIdWindowStateXYHeightWidthWindowStateXYHeightWidthWindowValuesWindowIdKeyStringValueBinary

注 2: save メソッドでは、もちろん、Load メソッドで行うこととは逆のことを行います。の行を作成しWindowWindowValuesそれらが現在のユーザーとマシンにまだ存在しない場合。

PersistentFormBase

このクラスは前のクラスを使用し、他のフォームの便利な基本クラスを形成します。

// Should have been abstract, but that makes the the designer crash at the moment...
public class PersistentFormBase : Form
{
    private PersistentFormHandler PersistenceHandler { get; set; }

    private bool handlerReady;

    protected PersistentFormBase()
    {
        // Prevents designer from crashing
        if (LicenseManager.UsageMode != LicenseUsageMode.Designtime)
        {
            Load += persistentFormLoad;
            FormClosing += persistentFormFormClosing;
        }
    }

    protected event EventHandler<EventArgs> ValuesLoaded;
    protected event EventHandler<EventArgs> StoringValues;

    protected void StoreValue<T>(string key, T value)
    {
        if (!handlerReady)
            throw new InvalidOperationException();
        PersistenceHandler.Set(key, value);
    }

    protected T GetValue<T>(string key)
    {
        if (!handlerReady)
            throw new InvalidOperationException();
        return PersistenceHandler.Get<T>(key);
    }

    protected T GetValue<T>(string key, T fallback)
    {
        if (!handlerReady)
            throw new InvalidOperationException();
        return PersistenceHandler.Get(key, fallback);
    }

    private void persistentFormLoad(object sender, EventArgs e)
    {
        // Create PersistenceHandler and load values from it
        PersistenceHandler = new PersistentFormHandler(GetType(), (int) FormWindowState.Normal, Bounds);
        PersistenceHandler.Load();
        handlerReady = true;

        // Set size and location
        Bounds = PersistenceHandler.WindowBounds;

        // Check if we have an MdiParent
        if(MdiParent == null)
        {
            // If we don't, make sure we are on screen
            if (!Screen.AllScreens.Any(ø => ø.Bounds.IntersectsWith(Bounds)))
                Location = new Point();
        }
        else
        {
            // If we do, make sure we are visible within the MdiClient area
            var c = MdiParent.Controls.OfType<MdiClient>().FirstOrDefault();
            if(c != null && !c.ClientRectangle.IntersectsWith(Bounds))
                Location = new Point();
        }

        // Set state
        WindowState = Enum.IsDefined(typeof (FormWindowState), PersistenceHandler.WindowState) ? (FormWindowState) PersistenceHandler.WindowState : FormWindowState.Normal;

        // Notify that values are loaded and ready for getting.
        var handler = ValuesLoaded;
        if (handler != null)
            handler(this, EventArgs.Empty);
    }

    private void persistentFormFormClosing(object sender, FormClosingEventArgs e)
    {
        // Set common things
        PersistenceHandler.WindowState = (int) WindowState;
        PersistenceHandler.WindowBounds = WindowState == FormWindowState.Normal ? Bounds : RestoreBounds;

        // Notify that values will be stored now, so time to store values.
        var handler = StoringValues;
        if (handler != null)
            handler(this, EventArgs.Empty);

        // Save values
        PersistenceHandler.Save();
    }
}

そして、それはほとんどそれです。これを使用するには、フォームは PersistentFormBase から継承するだけです。これにより、境界と状態が自動的に処理されます。スプリッターの距離など、他のものを保存する必要がある場合は、ValuesLoadedおよびStoringValuesイベントをリッスンし、それらの中でGetValueおよびStoreValueメソッドを使用します。

これが誰かを助けることを願っています! その場合はお知らせください。また、改善すべき点や改善点があればフィードバックをお寄せください。学びたいです=)

4

4 に答える 4

4

これを行う組み込みの方法はありません。ロジックを自分で作成する必要があります。この理由の 1 つは、ウィンドウが最後に表示されたモニターが使用できなくなった場合の処理​​方法を決定する必要があるためです。これは、たとえばラップトップやプロジェクタではよくあることです。Screenクラスには、これを支援する便利な機能がいくつかありますが、ディスプレイを一意かつ一貫して識別することは困難な場合があります。

于 2009-01-30T12:41:37.783 に答える
3

ポイントが接続された画面上にあるかどうかをテストする小さな関数を作成することで、問題の解決策を見つけました。主なアイデアは http://msdn.microsoft.com/en-us/library/system.windows.forms.screen(VS.80).aspxから来まし たが、いくつかの変更が必要でした。

public static bool ThisPointIsOnOneOfTheConnectedScreens(Point thePoint)
    {
        bool FoundAScreenThatContainsThePoint = false;

        for(int i = 0; i < Screen.AllScreens.Length; i++)
        {
            if(Screen.AllScreens[i].Bounds.Contains(thePoint))
                FoundAScreenThatContainsThePoint = true;
        }
        return FoundAScreenThatContainsThePoint;
    }
于 2009-04-29T00:30:10.323 に答える
1

上記のソリューションにはいくつかの問題があります。

複数の画面、および復元画面が小さい場合。

フォームのコントロール部分が画面領域の外にある可能性があるため、IntersectsWithではなく、Contains (...)を使用する必要があります。

これらの線に沿って何かを提案します

bool TestBounds(Rectangle R) {
    if (MdiParent == null) return Screen.AllScreens.Any(ø => ø.Bounds.Contains(R)); // If we don't have an MdiParent, make sure we are entirely on a screen
    var c = MdiParent.Controls.OfType<MdiClient>().FirstOrDefault(); // If we do, make sure we are visible within the MdiClient area
    return (c != null && c.ClientRectangle.Contains(R));
}

と、このように使用します。(保存された値が機能しない場合は、Windowsに処理させることに注意してください)

bool BoundsOK=TestBounds(myBounds);
if (!BoundsOK) {
    myBounds.Location = new Point(8,8); // then try (8,8) , to at least keep the size, if other monitor not currently present, or current is lesser
    BoundsOK = TestBounds(myBounds);
}
if (BoundsOK) { //Only change if acceptable, otherwise let Windows handle it
    StartPosition = FormStartPosition.Manual;
    Bounds = myBounds;
    WindowState = Enum.IsDefined(typeof(FormWindowState), myWindowState) ? (FormWindowState)myWindowState : FormWindowState.Normal;
}
于 2015-06-13T13:18:54.150 に答える
0

復元された (最大化されていない) 状態で保存された場所にメイン フォームを生成し、最後の状態が最大化されている場合は最大化します。

Stu が言ったように、この場合、削除されたモニターには注意してください。保存された場所には画面外の座標 (負の座標も含む) が含まれている可能性があるため、実際にはウィンドウが非表示 (実際には画面外) になる可能性があります。以前の状態をロードする前にデスクトップの境界をチェックすると、これを防ぐことができると思います。

于 2009-01-30T13:07:17.590 に答える