Восстановление Размера окна / Положение С Несколькими Мониторами

Использование директив и включение директив препроцессора - это две разные вещи. include примерно соответствует переменной среды CLASSPATH Java или опции -cp виртуальной машины Java.

То, что он делает, делает типы известными компилятору. Например, включение позволит вам ссылаться на std::string:

#include 
#include 

int main() {
    std::cout << std::string("hello, i'm a string");
}

. Теперь использование директив похоже на import в Java. Они делают имена видимыми в той области, в которой они появляются, поэтому вам больше не нужно полностью их квалифицировать. Как и в Java, используемые имена должны быть известны прежде, чем их можно будет сделать видимыми:

#include  // CLASSPATH, or -cp
#include 

// without import in java you would have to type java.lang.String . 
// note it happens that java has a special rule to import java.lang.* 
// automatically. but that doesn't happen for other packages 
// (java.net for example). But for simplicity, i'm just using java.lang here.
using std::string; // import java.lang.String; 
using namespace std; // import java.lang.*;

int main() {
    cout << string("hello, i'm a string");
}

Это плохая практика - использовать директиву using в заголовочных файлах, потому что это означает, что любой другой исходный файл, который включает его, будет увидеть эти имена, используя поиск без определения имени. В отличие от Java, где вы делаете имена видимыми только для пакета, в котором появляется строка импорта, в C ++ это может повлиять на всю программу, если они включают этот файл прямо или косвенно.

Будьте осторожны, когда делаете это в глобальном масштабе, даже в файлах реализации. Лучше использовать их как можно более локально. Для пространства имен std я никогда не использую это. Я и многие другие люди просто пишем std:: перед именами. Но если вам случится это сделать, сделайте это следующим образом:

#include 
#include 

int main() {
    using namespace std;
    cout << string("hello, i'm a string");
}

Что такое пространства имен и зачем они вам нужны, пожалуйста, прочитайте предложение, предложенное Бьярном Страуструпом в 1993 году для добавления их в предстоящий C ++. Стандарт. Хорошо написано:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1993/N0262.pdf

33
задан VVS 2 June 2009 в 14:15
поделиться

3 ответа

Ответ, предоставленный VVS, очень помог! Тем не менее, я обнаружил две незначительные проблемы с ним, поэтому я повторно публикую большую часть его кода с этими изменениями:

(1) В самый первый раз, когда приложение запускается, форма открывается в нормальном состоянии, но имеет такой размер, что отображается как строка заголовка. Я добавил в конструктор условие, чтобы исправить это.

(2) Если приложение закрывается в свернутом или развернутом виде, код в OnClosing не может запомнить размеры окна в его нормальном состоянии. (Три строки кода, которые я сейчас закомментировал, кажутся разумными, но по какой-то причине просто не работают.) К счастью, я ранее решил эту проблему и включил этот код в новую область в конце кода чтобы отслеживать состояние окна по мере его возникновения, а не ждать закрытия.


С этими двумя исправлениями, Я проверил:

A. закрытие в нормальном состоянии - восстанавливается до того же размера / положения и состояния

B. закрытие в свернутом состоянии - восстанавливает нормальное состояние с последним нормальным размером / положением

C. закрытие в развернутом состоянии - восстанавливается до максимального состояния и запоминает свой последний размер / положение, когда позже приспосабливается к нормальному состоянию.

D. закрытие на мониторе 2 - восстановление на мониторе 2.

E. закрытие монитора 2, затем отключение монитора 2 - восстановление в том же положении на мониторе 1

Дэвид: ваш код позволил мне почти без усилий достичь точек D и E - вы не только предоставили решение для моего вопроса, но и предоставили его в законченной программе, поэтому я запустил ее почти в течение нескольких секунд после вставки в Visual Studio. Так что большое вам спасибо за это!

public partial class MainForm : Form
{
    bool windowInitialized;

    public MainForm()
    {
        InitializeComponent();

        // this is the default
        this.WindowState = FormWindowState.Normal;
        this.StartPosition = FormStartPosition.WindowsDefaultBounds;

        // check if the saved bounds are nonzero and visible on any screen
        if (Settings.Default.WindowPosition != Rectangle.Empty &&
            IsVisibleOnAnyScreen(Settings.Default.WindowPosition))
        {
            // first set the bounds
            this.StartPosition = FormStartPosition.Manual;
            this.DesktopBounds = Settings.Default.WindowPosition;

            // afterwards set the window state to the saved value (which could be Maximized)
            this.WindowState = Settings.Default.WindowState;
        }
        else
        {
            // this resets the upper left corner of the window to windows standards
            this.StartPosition = FormStartPosition.WindowsDefaultLocation;

            // we can still apply the saved size
            // msorens: added gatekeeper, otherwise first time appears as just a title bar!
            if (Settings.Default.WindowPosition != Rectangle.Empty)
            {
                this.Size = Settings.Default.WindowPosition.Size;
            }
        }
        windowInitialized = true;
    }

    private bool IsVisibleOnAnyScreen(Rectangle rect)
    {
        foreach (Screen screen in Screen.AllScreens)
        {
            if (screen.WorkingArea.IntersectsWith(rect))
            {
                return true;
            }
        }

        return false;
    }

    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);

        // only save the WindowState if Normal or Maximized
        switch (this.WindowState)
        {
            case FormWindowState.Normal:
            case FormWindowState.Maximized:
                Settings.Default.WindowState = this.WindowState;
                break;

            default:
                Settings.Default.WindowState = FormWindowState.Normal;
                break;
        }

        # region msorens: this code does *not* handle minimized/maximized window.

        // reset window state to normal to get the correct bounds
        // also make the form invisible to prevent distracting the user
        //this.Visible = false;
        //this.WindowState = FormWindowState.Normal;
        //Settings.Default.WindowPosition = this.DesktopBounds;

        # endregion

        Settings.Default.Save();
    }

    # region window size/position
    // msorens: Added region to handle closing when window is minimized or maximized.

    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);
        TrackWindowState();
    }

    protected override void OnMove(EventArgs e)
    {
        base.OnMove(e);
        TrackWindowState();
    }

    // On a move or resize in Normal state, record the new values as they occur.
    // This solves the problem of closing the app when minimized or maximized.
    private void TrackWindowState()
    {
        // Don't record the window setup, otherwise we lose the persistent values!
        if (!windowInitialized) { return; }

        if (WindowState == FormWindowState.Normal)
        {
            Settings.Default.WindowPosition = this.DesktopBounds;
        }
    }

    # endregion window size/position
}
27
ответ дан 27 November 2019 в 17:54
поделиться

Если у вас несколько мониторов, я считаю, что размеры пользовательского интерфейса экрана просто больше. Так что обычный подход «1 монитор» для сохранения и восстановления местоположения будет работать. Я не пробовал это, потому что я далеко от своего второго монитора, но это не должно быть сложно проверить. То, как вы задали вопрос, похоже, вы его не тестировали.

Второе требование будет означать, что вам нужно будет проверить максимальные размеры сцены при восстановлении приложения, а затем при необходимости изменить положение. Для этого я использую следующий код:

    private System.Drawing.Rectangle ConstrainToScreen(System.Drawing.Rectangle bounds)
    {
        Screen screen = Screen.FromRectangle(bounds);
        System.Drawing.Rectangle workingArea = screen.WorkingArea;
        int width = Math.Min(bounds.Width, workingArea.Width);
        int height = Math.Min(bounds.Height, workingArea.Height);
        // mmm....minimax            
        int left = Math.Min(workingArea.Right - width, Math.Max(bounds.Left, workingArea.Left));
        int top = Math.Min(workingArea.Bottom - height, Math.Max(bounds.Top, workingArea.Top));
        return new System.Drawing.Rectangle(left, top, width, height);
    }

Я вызываю этот метод при восстановлении формы. Я сохраняю геометрию экрана в реестре при закрытии формы, а затем читаю геометрию в открытой форме. Я получаю границы, но затем ограничиваю восстановленные границы фактическим текущим экраном, используя метод выше.

Экономия при закрытии:

      // store the size of the form
      int w = 0, h = 0, left = 0, top = 0;
      if (this.Bounds.Width < this.MinimumSize.Width || this.Bounds.Height < this.MinimumSize.Height)
      {
          // The form is currently minimized.  
          // RestoreBounds is the size of the window prior to last minimize action.
          w = this.RestoreBounds.Width;
          h = this.RestoreBounds.Height;
          left = this.RestoreBounds.Location.X;
          top = this.RestoreBounds.Location.Y;
      }
      else
      {
          w = this.Bounds.Width;
          h = this.Bounds.Height;
          left = this.Location.X;
          top = this.Location.Y;
      }
      AppCuKey.SetValue(_rvn_Geometry,
        String.Format("{0},{1},{2},{3},{4}",
              left, top, w, h, (int)this.WindowState));

Восстановление при открытой форме:

    // restore the geometry of the form
    string s = (string)AppCuKey.GetValue(_rvn_Geometry);
    if (!String.IsNullOrEmpty(s))
    {
        int[] p = Array.ConvertAll<string, int>(s.Split(','),
                         new Converter<string, int>((t) => { return Int32.Parse(t); }));
        if (p != null && p.Length == 5)
            this.Bounds = ConstrainToScreen(new System.Drawing.Rectangle(p[0], p[1], p[2], p[3]));
    }
1
ответ дан 27 November 2019 в 17:54
поделиться

Попробуйте этот код. Интересно:

  • Проверяет, отображается ли окно (частично) в любой рабочей области экрана. Например, перетаскивание его за панель задач или полное перемещение за пределы экрана сбрасывает положение окна по умолчанию.
  • Сохраняет правильные границы, даже если форма свернута или развернута (обычная ошибка)
  • Сохраняет WindowState правильно. Сохранение FormWindowState.Minimized отключено по дизайну.

Границы и состояние сохраняются в настройках приложения с их соответствующим типом, поэтому нет необходимости выполнять синтаксический анализ строк. Позвольте фреймворку творить чудеса сериализации.

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();

        // this is the default
        this.WindowState = FormWindowState.Normal;
        this.StartPosition = FormStartPosition.WindowsDefaultBounds;

        // check if the saved bounds are nonzero and visible on any screen
        if (Settings.Default.WindowPosition != Rectangle.Empty &&
            IsVisibleOnAnyScreen(Settings.Default.WindowPosition))
        {
            // first set the bounds
            this.StartPosition = FormStartPosition.Manual;
            this.DesktopBounds = Settings.Default.WindowPosition;

            // afterwards set the window state to the saved value (which could be Maximized)
            this.WindowState = Settings.Default.WindowState;
        }
        else
        {
            // this resets the upper left corner of the window to windows standards
            this.StartPosition = FormStartPosition.WindowsDefaultLocation;

            // we can still apply the saved size
            this.Size = Settings.Default.WindowPosition.Size;
        }
    }

    private bool IsVisibleOnAnyScreen(Rectangle rect)
    {
        foreach (Screen screen in Screen.AllScreens)
        {
            if (screen.WorkingArea.IntersectsWith(rect))
            {
                return true;
            }
        }

        return false;
    }

    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);

        // only save the WindowState if Normal or Maximized
        switch (this.WindowState)
        {
            case FormWindowState.Normal:
            case FormWindowState.Maximized:
                Settings.Default.WindowState = this.WindowState;
                break;

            default:
                Settings.Default.WindowState = FormWindowState.Normal;
                break;
        }

        // reset window state to normal to get the correct bounds
        // also make the form invisible to prevent distracting the user
        this.Visible = false;
        this.WindowState = FormWindowState.Normal;

        Settings.Default.WindowPosition = this.DesktopBounds;
        Settings.Default.Save();
    }
}

Файл настроек для справки:

<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="ScreenTest" GeneratedClassName="Settings">
    <Profiles />
    <Settings>
        <Setting Name="WindowPosition" Type="System.Drawing.Rectangle" Scope="User">
            <Value Profile="(Default)">0, 0, 0, 0</Value>
        </Setting>
        <Setting Name="WindowState" Type="System.Windows.Forms.FormWindowState" Scope="User">
            <Value Profile="(Default)">Normal</Value>
        </Setting>
    </Settings>
</SettingsFile>
37
ответ дан 27 November 2019 в 17:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: