[c#] .NET WPF 세션 간 창 크기 기억

기본적으로 사용자가 내 응용 프로그램의 창 크기를 조정할 때 응용 프로그램을 다시 열 때 응용 프로그램 크기가 같기를 원합니다.

처음에는 SizeChanged 이벤트를 처리하고 Height와 Width를 저장했지만 더 쉬운 해결책이 있어야한다고 생각합니다.

아주 간단한 문제이지만 쉬운 해결책을 찾을 수 없습니다.



답변

user.config 파일에 값을 저장하십시오.

설정 파일에서 값을 만들어야합니다. 속성 폴더에 있어야합니다. 5 개의 값을 만듭니다.

  • Top 유형 double
  • Left 유형 double
  • Height 유형 double
  • Width 유형 double
  • Maximized 유형 bool -창이 최대화되었는지 여부를 유지합니다. 더 많은 정보를 저장하려면 다른 유형 또는 구조가 필요합니다.

처음 2 개를 0으로 초기화하고 두 번째 2 개를 애플리케이션의 기본 크기로 초기화하고 마지막 2 개를 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/ 에서 찾을 수 있습니다
.


답변

“직접 롤링”하여 수동으로 어딘가에 설정을 저장할 수 있으며 일반적으로 작동하지만 모든 경우를 올바르게 처리하지 못하는 것은 매우 쉽습니다. 종료시 GetWindowPlacement () 를 호출 하고 시작시 SetWindowPlacement () 를 호출하여 OS가 작업을 수행하도록하는 것이 훨씬 좋습니다 . 발생할 수있는 모든 미친 엣지 케이스 (다중 모니터, 최대화 된 상태에서 닫 혔을 경우 창을 보통 크기로 저장 등)를 처리하므로 필요하지 않습니다.

이 MSDN 샘플 은 WPF 앱에서이를 사용하는 방법을 보여줍니다. 샘플은 완벽하지 않지만 (창은 처음 실행할 때 가능한 한 작게 왼쪽 상단 모서리에서 시작되며 설정 디자이너가 type 값을 저장하는 이상한 동작이 있습니다 WINDOWPLACEMENT), 적어도 시작해야합니다.


답변

Thomas가 위에 게시 한 “긴 형식”바인딩에는 코딩이 거의 필요하지 않습니다. 네임 스페이스 바인딩이 있는지 확인하십시오.

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


답변

또는 다음 접근 방식을 좋아할 수도 있습니다 ( source 참조 ). 프로젝트에 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 번 언급해야합니다. 속성 을 명시 적으로 만들 때 한 번, 값을 앞뒤로 복사하는 코드에서 추가로 4 번 언급해야 합니다.

스토리지, 직렬화 등은 완전히 구성 할 수 있습니다. 또한 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();
        }
    }
}