Pada dasarnya ketika pengguna mengubah ukuran jendela aplikasi saya, saya ingin aplikasi memiliki ukuran yang sama ketika aplikasi dibuka kembali.
Awalnya saya berpikir untuk menangani event SizeChanged dan menyimpan Height dan Width, tapi saya rasa pasti ada solusi yang lebih mudah.
Masalah yang cukup sederhana, tetapi saya tidak dapat menemukan solusi yang mudah untuk itu.
Jawaban:
Simpan nilai di file user.config.
Anda harus membuat nilai di file pengaturan - itu harus ada di folder Properties. Buat lima nilai:
Top
tipedouble
Left
tipedouble
Height
tipedouble
Width
tipedouble
Maximized
jenisbool
- untuk menahan apakah jendela dimaksimalkan atau tidak. Jika Anda ingin menyimpan lebih banyak informasi, maka dibutuhkan tipe atau struktur yang berbeda.Inisialisasi dua yang pertama menjadi 0 dan dua yang kedua ke ukuran default aplikasi Anda, dan yang terakhir menjadi false.
Buat event handler Window_OnSourceInitialized dan tambahkan yang berikut ini:
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; }
CATATAN: Penempatan jendela yang disetel harus masuk ke acara yang diinisialisasi pada sumber dari jendela bukan konstruktor, jika tidak, jika jendela dimaksimalkan pada monitor kedua, itu akan selalu dimaksimalkan ulang pada monitor utama dan Anda tidak akan dapat untuk mengaksesnya.
Buat event handler Window_Closing dan tambahkan yang berikut ini:
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();
Ini akan gagal jika pengguna membuat area tampilan lebih kecil - baik dengan memutuskan sambungan layar atau mengubah resolusi layar - saat aplikasi ditutup, jadi Anda harus menambahkan tanda centang bahwa lokasi dan ukuran yang diinginkan masih valid sebelum menerapkan nilai.
sumber
Sebenarnya Anda tidak perlu menggunakan kode di belakang untuk melakukan itu (kecuali untuk menyimpan pengaturan). Anda dapat menggunakan ekstensi markup khusus untuk mengikat ukuran dan posisi jendela ke pengaturan seperti ini:
<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}">
Anda dapat menemukan kode untuk ekstensi markup ini di sini: http://www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/
sumber
{Binding Settings.Height}
, dll.Meskipun Anda dapat "menggulung sendiri" dan secara manual menyimpan pengaturan di suatu tempat, dan secara umum ini akan berhasil, sangat mudah untuk tidak menangani semua kasus dengan benar. Jauh lebih baik membiarkan OS yang bekerja untuk Anda, dengan memanggil GetWindowPlacement () saat keluar dan SetWindowPlacement () saat memulai. Ini menangani semua kasus tepi gila yang dapat terjadi (beberapa monitor, simpan ukuran normal jendela jika ditutup saat dimaksimalkan, dll.) Sehingga Anda tidak perlu melakukannya.
Contoh MSDN ini menunjukkan cara menggunakannya dengan aplikasi WPF. Sampel tidak sempurna (jendela akan dimulai di sudut kiri atas sekecil mungkin saat pertama kali dijalankan, dan ada beberapa perilaku aneh dengan desainer Pengaturan menyimpan nilai tipe
WINDOWPLACEMENT
), tetapi setidaknya itu harus membantu Anda memulai.sumber
Pengikatan "bentuk panjang" yang diposting Thomas di atas hampir tidak memerlukan pengkodean, cukup pastikan Anda memiliki pengikatan namespace:
<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}">
Kemudian untuk menghemat kode di belakang:
private void frmMain_Closed(object sender, EventArgs e) { Properties.Settings.Default.Save(); }
sumber
WindowState="{Binding Source={x:Static properties:Settings.Default}, Path=WindowState, Mode=TwoWay}"
Atau, Anda mungkin juga menyukai pendekatan berikut ( lihat sumber ). Tambahkan kelas WindowSettings ke proyek Anda dan masukkan
WindowSettings.Save="True"
di header jendela utama Anda:<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">
Di mana WindowSettings didefinisikan sebagai berikut:
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 } }
sumber
Cara default untuk mengatasinya adalah dengan menggunakan file pengaturan. Masalah dengan file pengaturan adalah Anda harus menentukan semua pengaturan dan menulis kode yang menyalin data sendiri. Cukup membosankan jika Anda memiliki banyak properti yang harus diperhatikan.
Saya membuat pustaka yang cukup fleksibel dan sangat mudah digunakan untuk ini, Anda cukup memberi tahu properti mana dari objek mana yang akan dilacak dan itu melakukan sisanya. Anda dapat mengkonfigurasi omong kosong itu juga jika Anda mau.
Library tersebut bernama Jot (github) , ini adalah artikel CodeProject lama yang saya tulis tentangnya.
Inilah cara Anda menggunakannya untuk melacak ukuran dan lokasi jendela:
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. file pengaturan: Dengan Jot kode jauh lebih sedikit, dan jauh lebih sedikit rawan kesalahan karena Anda hanya perlu menyebutkan setiap properti satu kali . Dengan file pengaturan, Anda perlu menyebutkan setiap properti 5 kali : sekali ketika Anda secara eksplisit membuat properti dan empat kali tambahan dalam kode yang menyalin nilai bolak-balik.
Penyimpanan, serialisasi, dll sepenuhnya dapat dikonfigurasi. Selain itu, saat menggunakan IOC, Anda bahkan dapat menghubungkannya sehingga menerapkan pelacakan secara otomatis ke semua objek yang diselesaikannya sehingga semua yang perlu Anda lakukan untuk membuat properti persisten adalah memasang atribut [Trackable] di atasnya.
Saya menulis semua ini karena saya pikir perpustakaan itu adalah yang terbaik dan saya ingin berbicara banyak tentangnya.
sumber
Saya menulis kelas singkat yang melakukan ini. Inilah namanya:
public MainWindow() { FormSizeSaver.RegisterForm(this, () => Settings.Default.MainWindowSettings, s => { Settings.Default.MainWindowSettings = s; Settings.Default.Save(); }); InitializeComponent(); ...
Dan inilah kodenya:
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(); } } }
sumber
Ada NuGet Project RestoreWindowPlace lihat di github yang melakukan semua ini untuk Anda, menyimpan informasi dalam file XML.
Untuk membuatnya berfungsi di jendela, itu semudah menelepon:
((App)Application.Current).WindowPlace.Register(this);
Di App Anda membuat kelas yang mengelola jendela Anda. Lihat tautan github di atas untuk informasi lebih lanjut.
sumber
Anda mungkin menyukai ini:
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()); } } }
Saat aplikasi ditutup:
Saat aplikasi dimulai:
sumber
Buat string bernama WindowXml di Pengaturan default Anda.
Gunakan metode ekstensi ini pada acara Window Loaded dan Closing Anda untuk memulihkan dan menyimpan ukuran dan lokasi Window.
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); } } }
sumber
Saya menggunakan jawaban dari Lance Cleveland dan mengikat Setting. Tetapi saya menggunakan beberapa Kode lagi untuk menghindari Jendela saya keluar dari Layar.
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; }
sumber
Saya membuat solusi yang lebih umum berdasarkan jawaban brilian RandomEngys. Ini menyimpan posisi ke file di folder yang sedang berjalan dan Anda tidak perlu membuat properti baru untuk setiap jendela baru yang Anda buat. Solusi ini bekerja sangat baik untuk saya dengan kode minimal di belakang kode.
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); } }
Dalam kode Anda di belakang Anda menambahkan dua metode ini
///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(); }
di jendela xaml Anda menambahkan ini
Closing="ClosingTrigger"
sumber