C#中:如何使窗体记住它的界限和的WindowState(服用双显示器设置进去) [英] C#: How to make a form remember its Bounds and WindowState (Taking dual monitor setups into account)

查看:168
本文介绍了C#中:如何使窗体记住它的界限和的WindowState(服用双显示器设置进去)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经做了类形式可以继承,它处理的形式,位置,大小和状态。而且它很好地工作。除了一件事:

I have made a class which a form can inherit from and it handles form Location, Size and State. And it works nicely. Except for one thing:

当你最大化不同的屏幕比你的主要的一个,位置和大小(你以前最大化)被正确保存,但是当它的应用(根据以前的状态)最大化它最大化我的主监视器上。当我然后将其还原到正常状态,它关系到其他屏幕,它以前。当我还是那句话最大化,这当然最大化的正确屏幕上。

When you maximize the application on a different screen than your main one, the location and size (before you maximized) gets stored correctly, but when it is maximized (according to its previous state) it is maximized on my main monitor. When I then restore it to normal state, it goes to the other screen where it was before. When I then maximize it again, it of course maximized on the correct screen.

所以我的问题是...我怎么可以让一个形式,当它最大化,记住屏幕上它是最大化呢?而如何恢复,当窗体再次打开?

So my question is... how can I make a form, when it is maximized, remember what screen it was maximized on? And how do I restore that when the form opens again?


我接受了大约有一个很好的提示答案如何,如果在屏幕上。但是,这只是我的问题的一部分,所以这里是我的解决方案:

I accepted the answer which had a very good tip about how to if on screen. But that was just part of my problem, so here is my solution:

在负载


  1. 先得到存储边界的WindowState 无论从任何存储。

  2. 然后设置边界

  3. 确保边界可见无论是 Screen.AllScreens.Any(O =>ø.Bounds.IntersectsWith(边界)) MdiParent.Controls。 OfType< MDICLIENT方式>()(第)ClientRectangle.IntersectsWith(边界)

    • 如果不是这样,只是做位置=新的点();

  1. First get stored Bounds and WindowState from whatever storage.
  2. Then set the Bounds.
  3. Make sure Bounds are visible either by Screen.AllScreens.Any(ø => ø.Bounds.IntersectsWith(Bounds)) or MdiParent.Controls.OfType<MdiClient>().First().ClientRectangle.IntersectsWith(Bounds).
    • If it doesn't, just do Location = new Point();.

在结束


  1. 商店的WindowState

  2. 如果的WindowState FormWindowState.Normal ,然后存储边界,否则存储 RestoreBounds

  1. Store WindowState.
  2. If WindowState is FormWindowState.Normal, then store Bounds, otherwise store RestoreBounds.

和多数民众赞成它! =)

And thats it! =)

所以,通过的奥利弗,这里是一些代码。它需要排序被充实,但是这可以谁想被用作启动:

So, as suggested by Oliver, here is some code. It needs to be fleshed out sort of, but this can be used as a start for whoever wants to:

负责存储和某处获取数据的。

Takes care of storing and fetching the data somewhere.

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:在load方法,你必须寻找以前存储的的WindowState WindowBounds 和其他值。我们使用SQL Server,并与列的窗口编号名称计算机名(为 Environment.MachineName ),用户ID 的WindowState X 身高宽度。因此,对于每一个窗口,你都将有一行用的WindowState X 身高宽度为每个用户和机器。此外,我们有这只​​是有一个外键 WINDOWID ,一个 WindowValues​​ 类型的列字符串和类型的二进制。如果有东西,没有找到,我只是把事情的默认和返回false

Note 1: In the load method you have to look for previously stored WindowState, WindowBounds and other values. We use SQL Server, and have a Window table with columns for Id, Name, MachineName (for Environment.MachineName), UserId, WindowState, X, Y, Height, Width. So for every window, you would have one row with WindowState, X, Y, Height and Width for each user and machine. In addition we have a WindowValues table which just has a foreign key to WindowId, a Key column of type String and a Value column of type Binary. If there is stuff that is not found, I just leave things default and return false.

注意2:在保存方法你那么,当然,从做你Load方法做相反。为创建行窗口 WindowValues​​ ,如果他们不存在为当前用户和机器。

Note 2: In the save method you then, of course do the reverse from what you do in the Load method. Creating rows for Window and WindowValues if they don't exist already for the current user and machine.

本类使用上一类,并形成其他形式的一个方便的基类。

This class uses the previous class and forms a handy base class for other forms.

// 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继承。这将自动利用边界和国家的照顾。如果什么都应该保存,就像一个分路器的距离,你会听的 Values​​Loaded StoringValues​​ 事件,并在这些使用的GetValue StoreValue 方法。

And thats pretty much it. To use it, a form would just inherit from the PersistentFormBase. That would automatically take care of bounds and state. If anything else should be stored, like a splitter distance, you would listen for the ValuesLoaded and StoringValues events and in those use the GetValue and StoreValue methods.

希望这可以帮助别人!请让我知道如果这样做。而且,请提供一些反馈,如果有什么事情,你觉得可以做的更好或东西。我想学习=)

Hope this can help someone! Please let me know if it does. And also, please provide some feedback if there is anything you think could be done better or something. I would like to learn =)

推荐答案

我找到了一个解决问题的方法写一个小函数的,即测试,如果poitn是连接屏幕上。
的主要思想由
http://msdn.microsoft.com/en-us/library/system.windows.forms.screen(VS.80)的.aspx
,但一些修改。

I found a solution to your problem by writing a little functio, that tests, if a poitn is on a connected screen. The main idea came from http://msdn.microsoft.com/en-us/library/system.windows.forms.screen(VS.80).aspx but some modifications were needed.

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

这篇关于C#中:如何使窗体记住它的界限和的WindowState(服用双显示器设置进去)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆