Сохранить позицию и размер формы

Здравствуйте
Мне надо сохранять позицию/размер формы при выходе и восстанавливать их при запуске. Использую JSON, так как он простой и удобный.
Проблема в том, что сохранять надо только в том случае, когда форма не свёрнута и не развёрнута. А это значит, что если закрыть форму когда она развёрнута, то предыдущие значения не сохранятся и будут утеряны. Следовательно, надо ловить события Maximized и Minimized, в которых запоминать позицию/размер формы и сохранять их если форма была закрыта когда она свёрнута или развёрнута… подумал я и обнаружил, что этих событий тупо нет :dizzy_face: Приплыли! Видимо, за 20 лет эти события никому, кроме меня одного, не понадобились А иначе, почему ещё их не подвезли? :thinking:
В интернете нашел примерно такой костыль:

        protected override void WndProc(ref Message message)
        {
            const int WM_SYSCOMMAND = 0x0112;
            IntPtr SC_MAXIMIZE = new IntPtr(0xF030);
            IntPtr SC_MINIMIZE = new IntPtr(0xF020);
            IntPtr SC_RESTORE = new IntPtr(0xF120);
            switch (message.Msg)
            {
                case WM_SYSCOMMAND:
                    if (message.WParam == SC_MAXIMIZE)
                    {
                        System.Diagnostics.Debug.WriteLine($"maximized: {Location} {Size}");
                    }
                    else if (message.WParam == SC_MINIMIZE)
                    {
                        System.Diagnostics.Debug.WriteLine($"minimized: {Location} {Size}");
                    }
                    else if (message.WParam == SC_RESTORE)
                    {
                        System.Diagnostics.Debug.WriteLine("restored");
                    }
                    break;
            }
            base.WndProc(ref message);
        }

Казалось бы, работает, но нет, нифига! :grin: Привет Бригману.
Сообщения SC_MAXIMIZE и SC_RESTORE приходят только если нажать мышкой на кнопку “развернуть”. Если делать даблклик по неклиентской области или нажимать win+вверх/win+вниз, то ничего не приходит. Возможно, приходит что-то другое, но я не проверял и не знаю, как это проверить.
Если правильно помню, на Delphi тоже такая фигня была, но там она как-то решалась (не помню как).
А тут что делать? :thinking:

Думаю лучше просто проверять WindowState формы.

А что сохранять при этом? :thinking: Там же будут размеры развёрнутой формы и позиция 0,0

Так при сохранении проверять WindowState, и не сохранять если свернуто.

А изменение размера можно например в Resize ловить если надо.

А почему в конструкторе нельзя поменять позицию окна? :dizzy_face: Размер меняется, а позиция нет.

Form.StartPosition Property (System.Windows.Forms) | Microsoft Docs не Manual?

Во, точно! Спасибо. Я думал, что эта фигня просто задаёт начальные координаты в дизайнере, но не запрещает их потом менять. Не логично.

Это получается, что надо сначала проверить, есть ли в конфиге значение x. Если его нет, то ставим WindowsDefaultLocation и пропускаем загрузку y. А если есть, то делаем то же самое для y. А если в конфиге есть x, но нет y, то варианта два. Либо делать Top = 0, либо ставить WindowsDefaultLocation и выбрасывать значение x. Чё-то слишком замудрёный алгоритм получается, чтобы всего-то координаты окна загрузить :thinking:
Как узнать, где должно было появиться окно, как если бы стояло StartPosition == WindowsDefaultLocation? Чтобы не ставить нули, если в конфиге отсутствуют нужные параметры (x или y, или оба). На WinAPI, например, была константа CW_USEDEFAULTS и не приходилось заниматься мазохизмом.
О, какой прикол нашёл

Если обратиться к хэндлу окна, то работает и всё замечательно.
Ещё одно подтверждение тому, что WinForms был написан по накурке, чтобы прям как у Borland, только своё.

А куда оно денется? Разве что пользователь зачем-то решить файл сломать ) Для таких редких случаев можно проверять, что оба есть.

Устанавливать только одно из них, а второе оставлять по умолчанию, вряд ли хорошая идея. Например, может быть два монитора разных размеров, и может получиться, что х был от первого монитора, а y от другого, и окно будет за их пределами.

Еще в идеале надо как минимум проверять, что они не выходят за пределы экрана. Мониторы и их настройки могут меняться.

Так это просто приводит к созданию окна. Можно вместо этого просто подождать какое-нибудь событие )

Вот видите. Мы опять напоролись на полное отсутствие логики. Конструктор создаёт экземпляр класса, но не создаёт окно формы, ради которого этот класс и был создан :man_shrugging: Событие Load приходит только при первом появлении окна на экране (которое совсем не обязательно должно быть показано сразу). Так а почему нет события в момент именно создания окна, когда всё нужное уже создано, но ещё не показано? В чём логика делать это (создавать окно) при обращении к хэндлу? :thinking:

Ну мало ли. Если будет какая-нибудь ошибка, то можно будет легко отредактировать конфиг, удалив параметр и он примет дефолтное значение.
Если бы я не хотел, чтобы юзер лез в конфиг, то взял бы какой-нибудь бинарный сериализатор.

Я бы так не сказал. И не важно, что за пределы экрана может выйти.

Ну так оно и при наличии обоих (x,y) может за пределы экрана выйти, если юзер поменяет конфигурацию мониторов и не сбросит конфиг. Я раньше гуглил, и похоже, что за этим очень трудно уследить (по-этому, я даже не пытаюсь это делать). А если один из параметров отсутствует, то окно, наоборот, почти всегда появится на экране, какой бы ни была их конфигурация. Вернее, не почти всегда, а шансов на это будет больше.

Каким образом выход за пределы экрана лучше стандартной позиции?)

Отсутствие одного из параметров это ж дико редкая ситуация, зачем что-то придумывать для такого случая, особенно если оно может вызвать новые проблемы.

Идеально может и сложно, но какие-то базовые проверки вполне можно добавить.
Ну и например это решение выглядит вполне норм: https://stackoverflow.com/a/987090/964478

Да, согласен. Такая проверка нужна. Не знал, что есть свойство AllScreens. Но если юзер додумается поставить мониторы диагонально друг другу, тогда не будет работать. Окно может остаться в невидимой область.
Кстати, раньше я просто делал иконку в трее и пункт меню, который возвращал окно в центр экрана.
Но VCL такой VCL и винда такая винда, что иконка в трее не всегда появляется.

А, хотя нет, будет. Я вчера в код не вник.