Svi*_*ish 9 c# persistence winforms
我创建了一个表可以继承的类,它处理表单位置,大小和状态.而且效果很好.除了一件事:
当您在与主要屏幕不同的屏幕上最大化应用程序时,位置和大小(在最大化之前)被正确存储,但是当它最大化时(根据其先前的状态),它在我的主监视器上最大化.当我将其恢复到正常状态时,它会转到之前的其他屏幕.当我再次最大化它时,它当然会在正确的屏幕上最大化.
所以我的问题是......我怎样才能制作一个表格,当它最大化时,记住它最大化的屏幕是什么?当表单再次打开时,如何恢复?
我接受了一个非常好的提示,如果在屏幕上如何.但这只是我问题的一部分,所以这是我的解决方案:
负载
Bounds和WindowState任何存储.Bounds.Bounds通过Screen.AllScreens.Any(ø => ø.Bounds.IntersectsWith(Bounds))或可见MdiParent.Controls.OfType<MdiClient>().First().ClientRectangle.IntersectsWith(Bounds).
Location = new Point();.关闭时
WindowState.WindowState是FormWindowState.Normal,则存储Bounds,否则存储RestoreBounds.就是这样!=)
因此,正如Oliver所建议的,这里有一些代码.它需要充实,但这可以作为任何想要的人的开始:
负责在某处存储和获取数据.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
注1:在加载方法,你必须寻找先前存储的WindowState,WindowBounds和其他值.我们使用SQL Server,并有一个Window表格,表格中的列Id,Name,MachineName(为Environment.MachineName), ,UserId,WindowState,X,Y,.Height Width因此,对于每一个窗口,你将有一个排WindowState,X,Y,Height并Width为每个用户和机器.另外,我们有一个WindowValues只有一个外键的表WindowId,一个Key类型String的Value列和一个类型的列Binary.如果有找不到的东西,我只是保留默认值并返回false.
注意2:在save方法中,您当然要执行与Load方法相反的操作.为当前用户和计算机创建行Window以及WindowValues它们是否已存在.
此类使用上一个类,并为其他表单形成一个方便的基类.
// 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();
}
}
Run Code Online (Sandbox Code Playgroud)
这就是它.要使用它,表单只会从PersistentFormBase继承.这将自动处理边界和状态.如果还应存储任何其他内容,例如分割器距离,您将监听ValuesLoaded和StoringValues事件,并使用GetValue和StoreValue方法.
希望这可以帮助别人!如果有,请告诉我.而且,如果您认为可以做得更好或其他什么,请提供一些反馈.我想学习=)
我通过编写一个小函数找到了解决您问题的方法,该函数可以测试某个点是否位于连接的屏幕上。主要思想来自 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;
}
Run Code Online (Sandbox Code Playgroud)