.NET WPF 세션 간 창 크기 기억


94

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

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

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


2
크기와 위치를 모두 다시 정렬하는 경우 (아래의 대부분의 코드 샘플처럼) 다른 사람이 마지막으로 표시되었던 모니터의 플러그를 뽑은 가장자리 케이스를 처리하고 창 밖에서.
Omer Raviv

@OmerRaviv 엣지 케이스를 고려한 예를 찾았습니까?
Andrew Truckle 2016-06-19

댓글을 달기에는 평판이 너무 적어서이 새로운 awnser를 만들었습니다. RobJohnson 설정을 포함하여 Lance Cleveland 와 동일한 솔루션을 사용 하지만 하위 창에 사용하고 동시에 더 많은 창을 열려면 작동하지 않습니다.
AelanY

답변:


122

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

응용 프로그램이 닫혀있는 동안 사용자가 화면 연결을 끊거나 화면 해상도를 변경하여 디스플레이 영역을 작게 만들면 실패하므로 값을 적용하기 전에 원하는 위치와 크기가 여전히 유효한지 확인해야합니다.


5
실제로 "User"범위의 설정은 Program Files의 app.config 파일에 저장되지 않고 사용자의 응용 프로그램 데이터 디렉터리에있는 user.config 파일에 저장됩니다. 그래서 문제가되지 않습니다 ...
Thomas Levesque

7
실제로 설정에 "WindowState"를 추가 할 수 있습니다. 유형 선택 -> 검색 -> PresentationFramework -> System.Windows -> WINDOWSTATE :
마틴 Vseticka

2
FWIW, 응용 프로그램이 충돌하는 경우에도 크기가 변경된 처리기에서이 작업을 수행합니다. 처리되지 않은 예외 처리에서는 드물지만, 미스터리하게 발생하면 크기 / 위치 손실로 사용자를 처벌하는 이유는 무엇입니까?
Thomas

7
이 코드에는 사용자가 두 번째 화면에서 창을 연 다음 컴퓨터에서 해당 화면의 연결을 끊으면 다음에 창을 열 때 화면 밖으로 표시된다는 버그가 있습니다. 창이 모달이면 사용자는 앱과 전혀 상호 작용할 수 없으며 무슨 일이 벌어지고 있는지 이해할 수 없습니다. 화면 좌표를 DPI 종속 값으로 변환 한
Omer Raviv

2
@OmerRaviv-버그는 아니지만 제한 사항입니다. :) 심각하게-문제의 해당 측면을 다루지 않았습니다.
ChrisF

74

실제로 그렇게하기 위해 코드 숨김을 사용할 필요가 없습니다 (설정 저장 제외). 사용자 지정 태그 확장을 사용하여 다음과 같은 설정에 창 크기 및 위치를 바인딩 할 수 있습니다.

<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/ 에서 찾을 수 있습니다 .


4
나는 선택한 대답보다이 대답을 더 좋아합니다. 잘 했어.
moswald

6
+1-바인딩 및 확장 사용을 좋아합니다! 바인딩 된 설정에 WindowState를 추가하면 전체 기능을 제공합니다. 당신의 DataContext에서 사용할 수있는 사용자 설정이있는 경우 또는, 당신은 같은 것을 사용할 수있는 {Binding Settings.Height}등,
매트 DeKrey

이 접근 방식은 창이 최대화 될 때 사용자가 응용 프로그램을 닫을 때 문제가 있습니다.
Vinicius Rocha 2013

@Vinicius, 자세히 설명해 주시겠습니까? 정확히 무엇이 문제입니까?
Thomas Levesque 2013

4
사람들이 두 대의 모니터를 가지고있어서 음의 좌표를 갖고 모니터 구성을 변경하고 값이 더 이상 유효하지 않을 때 어떨까요?
Andrew Truckle

33

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

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


좋은 솔루션입니다. 그러나 난 그냥 GetWindowPlacement / SetWindowPlacement는 것을 발견 에어로 스냅을 인식하지
베르 마크

1
@RandomEngy는이를 기반으로 개선 된 답변을 게시했습니다.
Stéphane Gourichon

27

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

이 솔루션을 선택했지만 창 상태가 정상인 경우에만 설정을 저장했습니다. 그렇지 않으면 최대화 모드에서 벗어나게 될 수 있습니다
David Sykes

7
+1 나도 이것을 사용했습니다. @DavidSykes-창 상태에 대한 다른 설정을 추가하는 것이 충분히 잘 작동하는 것 같습니다. 예WindowState="{Binding Source={x:Static properties:Settings.Default}, Path=WindowState, Mode=TwoWay}"
RobJohnson

@RobJohnson 나는 당신의 제안을 시도했고 그것은 매우 잘 작동했습니다, 감사합니다.
데이비드 사익스

4

또는 다음 접근 방식을 좋아할 수도 있습니다 ( 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
}
}

3

이를 해결하는 기본 방법은 설정 파일을 사용하는 것입니다. 설정 파일의 문제는 모든 설정을 정의하고 데이터를 직접 복사하는 코드를 작성해야한다는 것입니다. 추적 할 속성이 많은 경우 매우 지루합니다.

이를 위해 매우 유연하고 사용하기 쉬운 라이브러리를 만들었습니다. 추적 할 개체의 속성을 지정하면 나머지 작업을 수행합니다. 원하는 경우 쓰레기도 구성 할 수 있습니다.

라이브러리는 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] 속성을 슬랩하기 만하면됩니다.

나는 도서관이 최고 수준이라고 생각하고 그것에 대해 입을 다물고 싶기 때문에이 모든 것을 쓰고 있습니다.


감사합니다. 새 클래스에서 코드 스 니펫을 사용하여 프로그램 이름을 기반으로 한 경로로 상태 추적기를 설정했습니다. 이제부터는 한 줄만 작성하면 모든 창 속성이 처리됩니다.
Awesomeness

1

나는 이것을하는 빠른 수업을 썼다. 이름은 다음과 같습니다.

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

window.Intitialized는 window.Loaded 볼 수 있어야 mostlytech.blogspot.com/2008/01/...을
에 Gleb Sevruk

@Gleb, 둘 다 내 생각에 작동합니다. Initialized에서 문제가 있습니까?
tster

예, 초기화 된 이벤트 만 사용하면 최대화 된 창이 잘못된 화면에 표시됩니다. 내가 한 일이 작동하는 것 같습니다. 이제 Loaded 이벤트도 구독하고 있습니다. _window.WindowState = s.Maximized 이동 했습니까? WindowState.Maximized : WindowState.Normal; "Loaded"이벤트 핸들러 내부의 줄. window.Initialized + = InitializedHandler; window.Loaded + = LoadedHandler; BTW :이 방법 같은 I
에 Gleb Sevruk

1

있다 NuGet 프로젝트 RestoreWindowPlace가 에서 볼 GitHub의 XML 파일의 정보를 저장, 당신을 위해 모든 작업을 수행합니다.

창에서 작동하게하려면 다음을 호출하는 것만 큼 간단합니다.

((App)Application.Current).WindowPlace.Register(this);

앱에서 창을 관리하는 클래스를 만듭니다. 자세한 내용은 위의 github 링크를 참조하십시오.


0

당신은 이것을 좋아할 것입니다 :

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

앱이 닫힐 때 :

        Properties.Settings.Default.Win1Placement = WindowStateHelper.ToXml(win1);
        Properties.Settings.Default.Win2Placement = WindowStateHelper.ToXml(win2);
        ...

앱이 시작되면 :

        WindowStateHelper.FromXml(Properties.Settings.Default.Win1Placement, win1);
        WindowStateHelper.FromXml(Properties.Settings.Default.Win2Placement, win2);
        ...

0

기본 설정에서 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);
        }
    }
}

0

나는 Lance Cleveland의 답변을 사용하고 설정을 바인딩합니다. 하지만 내 창이 화면 밖으로 나가는 것을 피하기 위해 더 많은 코드를 사용하고 있습니다.

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

0

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"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.