В основном, когда пользователь изменяет размер окна моего приложения, я хочу, чтобы приложение было того же размера, когда приложение снова открывается.
Сначала я решил обработать событие SizeChanged и сохранить высоту и ширину, но я думаю, что должно быть более простое решение.
Довольно простая проблема, но я не могу ее решить.
Ответы:
Сохраните значения в файле user.config.
Вам нужно будет создать значение в файле настроек - оно должно находиться в папке «Свойства». Создайте пять значений:
Top
типаdouble
Left
типаdouble
Height
типаdouble
Width
типаdouble
Maximized
of typebool
- удерживать независимо от того, развернуто ли окно или нет. Если вы хотите хранить больше информации, вам потребуется другой тип или структура.Инициализируйте первые два значения 0, вторые два - размером по умолчанию вашего приложения, а последний - значением false.
Создайте обработчик событий Window_OnSourceInitialized и добавьте следующее:
this.Top = Properties.Settings.Default.Top; this.Left = Properties.Settings.Default.Left; this.Height = Properties.Settings.Default.Height; this.Width = Properties.Settings.Default.Width; // Very quick and dirty - but it does the job if (Properties.Settings.Default.Maximized) { WindowState = WindowState.Maximized; }
ПРИМЕЧАНИЕ. Установленное размещение окна должно происходить в исходном инициализированном событии окна, а не в конструкторе, иначе, если вы развернули окно на втором мониторе, оно всегда будет перезапускаться в максимальном размере на основном мониторе, и вы не сможете чтобы получить к нему доступ.
Создайте обработчик события Window_Closing и добавьте следующее:
if (WindowState == WindowState.Maximized) { // Use the RestoreBounds as the current values will be 0, 0 and the size of the screen Properties.Settings.Default.Top = RestoreBounds.Top; Properties.Settings.Default.Left = RestoreBounds.Left; Properties.Settings.Default.Height = RestoreBounds.Height; Properties.Settings.Default.Width = RestoreBounds.Width; Properties.Settings.Default.Maximized = true; } else { Properties.Settings.Default.Top = this.Top; Properties.Settings.Default.Left = this.Left; Properties.Settings.Default.Height = this.Height; Properties.Settings.Default.Width = this.Width; Properties.Settings.Default.Maximized = false; } Properties.Settings.Default.Save();
Это не удастся, если пользователь уменьшит область отображения - либо отключив экран, либо изменив разрешение экрана - в то время как приложение закрыто, поэтому вы должны добавить проверку, что желаемое местоположение и размер все еще действительны, прежде чем применять значения.
источник
На самом деле вам не нужно использовать программный код для этого (кроме сохранения настроек). Вы можете использовать собственное расширение разметки, чтобы привязать размер и положение окна к таким настройкам:
<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:my="clr-namespace:WpfApplication1" Title="Window1" Height="{my:SettingBinding Height}" Width="{my:SettingBinding Width}" Left="{my:SettingBinding Left}" Top="{my:SettingBinding Top}">
Вы можете найти код этого расширения разметки здесь: http://www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/
источник
{Binding Settings.Height}
и т. Д.Хотя вы можете «свернуть свой собственный» и вручную где-нибудь сохранить настройки, и в целом это сработает, очень легко не справиться со всеми случаями правильно. Гораздо лучше позволить ОС делать всю работу за вас, вызывая GetWindowPlacement () при выходе и SetWindowPlacement () при запуске. Он обрабатывает все безумные крайние случаи, которые могут возникнуть (несколько мониторов, сохранить нормальный размер окна, если оно закрыто при максимальном увеличении, и т. Д.), Так что вам не нужно.
В этом образце MSDN показано, как использовать их с приложением WPF. Образец не идеален (при первом запуске окно будет начинаться в верхнем левом углу настолько маленьким, насколько это возможно, и при сохранении значения типа конструктором настроек наблюдается странное поведение
WINDOWPLACEMENT
), но он должен, по крайней мере, помочь вам начать работу.источник
Привязка "длинной формы", которую Томас опубликовал выше, почти не требует кодирования, просто убедитесь, что у вас есть привязка пространства имен:
<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:p="clr-namespace:WpfApplication1.Properties" Title="Window1" Height="{Binding Source={x:Static p:Settings.Default}, Path=Height, Mode=TwoWay}" Width="{Binding Source={x:Static p:Settings.Default}, Path=Width, Mode=TwoWay}" Left="{Binding Source={x:Static p:Settings.Default}, Path=Left, Mode=TwoWay}" Top="{Binding Source={x:Static p:Settings.Default}, Path=Top, Mode=TwoWay}">
Затем, чтобы сэкономить на коде программной части:
private void frmMain_Closed(object sender, EventArgs e) { Properties.Settings.Default.Save(); }
источник
WindowState="{Binding Source={x:Static properties:Settings.Default}, Path=WindowState, Mode=TwoWay}"
В качестве альтернативы вам также может понравиться следующий подход ( см. Источник ). Добавьте класс WindowSettings в свой проект и вставьте
WindowSettings.Save="True"
в заголовок главного окна:<Window x:Class="YOURPROJECT.Views.ShellView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Services="clr-namespace:YOURNAMESPACE.Services" Services:WindowSettings.Save="True">
Где WindowSettings определяется следующим образом:
using System; using System.ComponentModel; using System.Configuration; using System.Windows; namespace YOURNAMESPACE.Services { /// <summary> /// Persists a Window's Size, Location and WindowState to UserScopeSettings /// </summary> public class WindowSettings { #region Fields /// <summary> /// Register the "Save" attached property and the "OnSaveInvalidated" callback /// </summary> public static readonly DependencyProperty SaveProperty = DependencyProperty.RegisterAttached("Save", typeof (bool), typeof (WindowSettings), new FrameworkPropertyMetadata(OnSaveInvalidated)); private readonly Window mWindow; private WindowApplicationSettings mWindowApplicationSettings; #endregion Fields #region Constructors public WindowSettings(Window pWindow) { mWindow = pWindow; } #endregion Constructors #region Properties [Browsable(false)] public WindowApplicationSettings Settings { get { if (mWindowApplicationSettings == null) mWindowApplicationSettings = CreateWindowApplicationSettingsInstance(); return mWindowApplicationSettings; } } #endregion Properties #region Methods public static void SetSave(DependencyObject pDependencyObject, bool pEnabled) { pDependencyObject.SetValue(SaveProperty, pEnabled); } protected virtual WindowApplicationSettings CreateWindowApplicationSettingsInstance() { return new WindowApplicationSettings(this); } /// <summary> /// Load the Window Size Location and State from the settings object /// </summary> protected virtual void LoadWindowState() { Settings.Reload(); if (Settings.Location != Rect.Empty) { mWindow.Left = Settings.Location.Left; mWindow.Top = Settings.Location.Top; mWindow.Width = Settings.Location.Width; mWindow.Height = Settings.Location.Height; } if (Settings.WindowState != WindowState.Maximized) mWindow.WindowState = Settings.WindowState; } /// <summary> /// Save the Window Size, Location and State to the settings object /// </summary> protected virtual void SaveWindowState() { Settings.WindowState = mWindow.WindowState; Settings.Location = mWindow.RestoreBounds; Settings.Save(); } /// <summary> /// Called when Save is changed on an object. /// </summary> private static void OnSaveInvalidated(DependencyObject pDependencyObject, DependencyPropertyChangedEventArgs pDependencyPropertyChangedEventArgs) { var window = pDependencyObject as Window; if (window != null) if ((bool) pDependencyPropertyChangedEventArgs.NewValue) { var settings = new WindowSettings(window); settings.Attach(); } } private void Attach() { if (mWindow != null) { mWindow.Closing += WindowClosing; mWindow.Initialized += WindowInitialized; mWindow.Loaded += WindowLoaded; } } private void WindowClosing(object pSender, CancelEventArgs pCancelEventArgs) { SaveWindowState(); } private void WindowInitialized(object pSender, EventArgs pEventArgs) { LoadWindowState(); } private void WindowLoaded(object pSender, RoutedEventArgs pRoutedEventArgs) { if (Settings.WindowState == WindowState.Maximized) mWindow.WindowState = Settings.WindowState; } #endregion Methods #region Nested Types public class WindowApplicationSettings : ApplicationSettingsBase { #region Constructors public WindowApplicationSettings(WindowSettings pWindowSettings) { } #endregion Constructors #region Properties [UserScopedSetting] public Rect Location { get { if (this["Location"] != null) return ((Rect) this["Location"]); return Rect.Empty; } set { this["Location"] = value; } } [UserScopedSetting] public WindowState WindowState { get { if (this["WindowState"] != null) return (WindowState) this["WindowState"]; return WindowState.Normal; } set { this["WindowState"] = value; } } #endregion Properties } #endregion Nested Types } }
источник
По умолчанию способ решения - использование файлов настроек. Проблема с файлами настроек заключается в том, что вам нужно определить все настройки и написать код, который сам копирует данные туда и обратно. Довольно утомительно, если вам нужно отслеживать много объектов недвижимости.
Я сделал для этого довольно гибкую и очень простую в использовании библиотеку, вы просто указываете ей, какие свойства какого объекта нужно отслеживать, а он делает все остальное. Вы тоже можете настроить из него хрень, если хотите.
Библиотека называется Jot (github) , вот старая статья CodeProject, которую я написал об этом.
Вот как вы могли бы использовать его для отслеживания размера и местоположения окна:
public MainWindow() { InitializeComponent(); _stateTracker.Configure(this) .IdentifyAs("MyMainWindow") .AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState)) .RegisterPersistTrigger(nameof(Closed)) .Apply(); }
Jot vs. файлы настроек: с Jot значительно меньше кода, и он намного меньше подвержен ошибкам, поскольку вам нужно упомянуть каждое свойство только один раз . В файлах настроек вам нужно упомянуть каждое свойство 5 раз : один раз, когда вы явно создаете свойство, и еще четыре раза в коде, который копирует значения туда и обратно.
Хранение, сериализация и т. Д. Полностью настраиваются. Кроме того, при использовании IOC вы даже можете подключить его, чтобы он автоматически применял отслеживание ко всем объектам, которые он разрешает, так что все, что вам нужно сделать, чтобы сделать свойство постоянным, - это добавить к нему атрибут [Trackable].
Я пишу все это, потому что считаю, что библиотека на высшем уровне, и хочу рассказать об этом.
источник
Я написал быстрый класс, который делает это. Вот как это называется:
public MainWindow() { FormSizeSaver.RegisterForm(this, () => Settings.Default.MainWindowSettings, s => { Settings.Default.MainWindowSettings = s; Settings.Default.Save(); }); InitializeComponent(); ...
А вот код:
public class FormSizeSaver { private readonly Window window; private readonly Func<FormSizeSaverSettings> getSetting; private readonly Action<FormSizeSaverSettings> saveSetting; private FormSizeSaver(Window window, Func<string> getSetting, Action<string> saveSetting) { this.window = window; this.getSetting = () => FormSizeSaverSettings.FromString(getSetting()); this.saveSetting = s => saveSetting(s.ToString()); window.Initialized += InitializedHandler; window.StateChanged += StateChangedHandler; window.SizeChanged += SizeChangedHandler; window.LocationChanged += LocationChangedHandler; } public static FormSizeSaver RegisterForm(Window window, Func<string> getSetting, Action<string> saveSetting) { return new FormSizeSaver(window, getSetting, saveSetting); } private void SizeChangedHandler(object sender, SizeChangedEventArgs e) { var s = getSetting(); s.Height = e.NewSize.Height; s.Width = e.NewSize.Width; saveSetting(s); } private void StateChangedHandler(object sender, EventArgs e) { var s = getSetting(); if (window.WindowState == WindowState.Maximized) { if (!s.Maximized) { s.Maximized = true; saveSetting(s); } } else if (window.WindowState == WindowState.Normal) { if (s.Maximized) { s.Maximized = false; saveSetting(s); } } } private void InitializedHandler(object sender, EventArgs e) { var s = getSetting(); window.WindowState = s.Maximized ? WindowState.Maximized : WindowState.Normal; if (s.Height != 0 && s.Width != 0) { window.Height = s.Height; window.Width = s.Width; window.WindowStartupLocation = WindowStartupLocation.Manual; window.Left = s.XLoc; window.Top = s.YLoc; } } private void LocationChangedHandler(object sender, EventArgs e) { var s = getSetting(); s.XLoc = window.Left; s.YLoc = window.Top; saveSetting(s); } } [Serializable] internal class FormSizeSaverSettings { public double Height, Width, YLoc, XLoc; public bool Maximized; public override string ToString() { using (var ms = new MemoryStream()) { var bf = new BinaryFormatter(); bf.Serialize(ms, this); ms.Position = 0; byte[] buffer = new byte[(int)ms.Length]; ms.Read(buffer, 0, buffer.Length); return Convert.ToBase64String(buffer); } } internal static FormSizeSaverSettings FromString(string value) { try { using (var ms = new MemoryStream(Convert.FromBase64String(value))) { var bf = new BinaryFormatter(); return (FormSizeSaverSettings) bf.Deserialize(ms); } } catch (Exception) { return new FormSizeSaverSettings(); } } }
источник
Там в NuGet проекта RestoreWindowPlace увидеть на GitHub , что делает все это для вас, сохраняя информацию в файле XML.
Чтобы заставить его работать с окном, достаточно просто позвонить:
((App)Application.Current).WindowPlace.Register(this);
В приложении вы создаете класс, который управляет вашими окнами. См. Ссылку на github выше для получения дополнительной информации.
источник
Вам может понравиться это:
public class WindowStateHelper { public static string ToXml(System.Windows.Window win) { XElement bounds = new XElement("Bounds"); if (win.WindowState == System.Windows.WindowState.Maximized) { bounds.Add(new XElement("Top", win.RestoreBounds.Top)); bounds.Add(new XElement("Left", win.RestoreBounds.Left)); bounds.Add(new XElement("Height", win.RestoreBounds.Height)); bounds.Add(new XElement("Width", win.RestoreBounds.Width)); } else { bounds.Add(new XElement("Top", win.Top)); bounds.Add(new XElement("Left", win.Left)); bounds.Add(new XElement("Height", win.Height)); bounds.Add(new XElement("Width", win.Width)); } XElement root = new XElement("WindowState", new XElement("State", win.WindowState.ToString()), new XElement("Visibility", win.Visibility.ToString()), bounds); return root.ToString(); } public static void FromXml(string xml, System.Windows.Window win) { try { XElement root = XElement.Parse(xml); string state = root.Descendants("State").FirstOrDefault().Value; win.WindowState = (System.Windows.WindowState)Enum.Parse(typeof(System.Windows.WindowState), state); state = root.Descendants("Visibility").FirstOrDefault().Value; win.Visibility = (System.Windows.Visibility)Enum.Parse(typeof(System.Windows.Visibility), state); XElement bounds = root.Descendants("Bounds").FirstOrDefault(); win.Top = Convert.ToDouble(bounds.Element("Top").Value); win.Left = Convert.ToDouble(bounds.Element("Left").Value); win.Height = Convert.ToDouble(bounds.Element("Height").Value); win.Width = Convert.ToDouble(bounds.Element("Width").Value); } catch (Exception x) { System.Console.WriteLine(x.ToString()); } } }
Когда приложение закрывается:
Когда приложение запускается:
источник
Создайте строку с именем WindowXml в настройках по умолчанию.
Используйте этот метод расширения для событий Window Loaded и Closing, чтобы восстановить и сохранить размер и местоположение окна.
using YourProject.Properties; using System; using System.Linq; using System.Windows; using System.Xml.Linq; namespace YourProject.Extensions { public static class WindowExtensions { public static void SaveSizeAndLocation(this Window w) { try { var s = "<W>"; s += GetNode("Top", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Top : w.Top); s += GetNode("Left", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Left : w.Left); s += GetNode("Height", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Height : w.Height); s += GetNode("Width", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Width : w.Width); s += GetNode("WindowState", w.WindowState); s += "</W>"; Settings.Default.WindowXml = s; Settings.Default.Save(); } catch (Exception) { } } public static void RestoreSizeAndLocation(this Window w) { try { var xd = XDocument.Parse(Settings.Default.WindowXml); w.WindowState = (WindowState)Enum.Parse(typeof(WindowState), xd.Descendants("WindowState").FirstOrDefault().Value); w.Top = Convert.ToDouble(xd.Descendants("Top").FirstOrDefault().Value); w.Left = Convert.ToDouble(xd.Descendants("Left").FirstOrDefault().Value); w.Height = Convert.ToDouble(xd.Descendants("Height").FirstOrDefault().Value); w.Width = Convert.ToDouble(xd.Descendants("Width").FirstOrDefault().Value); } catch (Exception) { } } private static string GetNode(string name, object value) { return string.Format("<{0}>{1}</{0}>", name, value); } } }
источник
Я использую ответ Ланса Кливленда и связываю Настройки. Но я использую еще код, чтобы мое окно не выходило за пределы экрана.
private void SetWindowSettingsIntoScreenArea() { // first detect Screen, where we will display the Window // second correct bottom and right position // then the top and left position. // If Size is bigger than current Screen, it's still possible to move and size the Window // get the screen to display the window var screen = System.Windows.Forms.Screen.FromPoint(new System.Drawing.Point((int)Default.Left, (int)Default.Top)); // is bottom position out of screen for more than 1/3 Height of Window? if (Default.Top + (Default.Height / 3) > screen.WorkingArea.Height) Default.Top = screen.WorkingArea.Height - Default.Height; // is right position out of screen for more than 1/2 Width of Window? if (Default.Left + (Default.Width / 2) > screen.WorkingArea.Width) Default.Left = screen.WorkingArea.Width - Default.Width; // is top position out of screen? if (Default.Top < screen.WorkingArea.Top) Default.Top = screen.WorkingArea.Top; // is left position out of screen? if (Default.Left < screen.WorkingArea.Left) Default.Left = screen.WorkingArea.Left; }
источник
Я сделал более общее решение, основанное на блестящем ответе RandomEngys. Он сохраняет позицию в файл в текущей папке, и вам не нужно создавать новые свойства для каждого нового окна, которое вы создаете. Это решение отлично работает для меня с минимальным кодом в коде.
using System; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Windows; using System.Windows.Interop; using System.Xml; using System.Xml.Serialization; namespace WindowPlacementNameSpace { // RECT structure required by WINDOWPLACEMENT structure [Serializable] [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; public RECT(int left, int top, int right, int bottom) { this.Left = left; this.Top = top; this.Right = right; this.Bottom = bottom; } } // POINT structure required by WINDOWPLACEMENT structure [Serializable] [StructLayout(LayoutKind.Sequential)] public struct POINT { public int X; public int Y; public POINT(int x, int y) { this.X = x; this.Y = y; } } // WINDOWPLACEMENT stores the position, size, and state of a window [Serializable] [StructLayout(LayoutKind.Sequential)] public struct WINDOWPLACEMENT { public int length; public int flags; public int showCmd; public POINT minPosition; public POINT maxPosition; public RECT normalPosition; } public static class WindowPlacement { private static readonly Encoding Encoding = new UTF8Encoding(); private static readonly XmlSerializer Serializer = new XmlSerializer(typeof(WINDOWPLACEMENT)); [DllImport("user32.dll")] private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl); [DllImport("user32.dll")] private static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl); private const int SW_SHOWNORMAL = 1; private const int SW_SHOWMINIMIZED = 2; private static void SetPlacement(IntPtr windowHandle, string placementXml) { if (string.IsNullOrEmpty(placementXml)) { return; } byte[] xmlBytes = Encoding.GetBytes(placementXml); try { WINDOWPLACEMENT placement; using (MemoryStream memoryStream = new MemoryStream(xmlBytes)) { placement = (WINDOWPLACEMENT)Serializer.Deserialize(memoryStream); } placement.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT)); placement.flags = 0; placement.showCmd = (placement.showCmd == SW_SHOWMINIMIZED ? SW_SHOWNORMAL : placement.showCmd); SetWindowPlacement(windowHandle, ref placement); } catch (InvalidOperationException) { // Parsing placement XML failed. Fail silently. } } private static string GetPlacement(IntPtr windowHandle) { WINDOWPLACEMENT placement; GetWindowPlacement(windowHandle, out placement); using (MemoryStream memoryStream = new MemoryStream()) { using (XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8)) { Serializer.Serialize(xmlTextWriter, placement); byte[] xmlBytes = memoryStream.ToArray(); return Encoding.GetString(xmlBytes); } } } public static void ApplyPlacement(this Window window) { var className = window.GetType().Name; try { var pos = File.ReadAllText(Directory + "\\" + className + ".pos"); SetPlacement(new WindowInteropHelper(window).Handle, pos); } catch (Exception exception) { Log.Error("Couldn't read position for " + className, exception); } } public static void SavePlacement(this Window window) { var className = window.GetType().Name; var pos = GetPlacement(new WindowInteropHelper(window).Handle); try { File.WriteAllText(Directory + "\\" + className + ".pos", pos); } catch (Exception exception) { Log.Error("Couldn't write position for " + className, exception); } } private static string Directory => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); } }
В вашем коде вы добавляете эти два метода
///This method is save the actual position of the window to file "WindowName.pos" private void ClosingTrigger(object sender, EventArgs e) { this.SavePlacement(); } ///This method is load the actual position of the window from the file protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); this.ApplyPlacement(); }
в окне xaml вы добавляете это
Closing="ClosingTrigger"
источник