Por que você não pode vincular o tamanho de um formulário do Windows ao ApplicationSettings?

StackOverflow https://stackoverflow.com/questions/18585

Pergunta

Atualizar:Resolvido, com código

Eu consegui funcionar, veja minha resposta abaixo para o código ...

Postagem original

Como Tundey apontou em a resposta dele para o meu ultima questão, você pode vincular quase tudo sobre um controle de formulários do Windows a ApplicationSettings com bastante facilidade.Então não há realmente nenhuma maneira de fazer isso com o tamanho do formulário? Este tutorial diz que você precisa manipular Size explicitamente para poder salvar RestoreBounds em vez de size se a janela estiver maximizada ou minimizada.No entanto, eu esperava poder usar apenas uma propriedade como:

public Size RestoreSize
{
    get
    {
        if (this.WindowState == FormWindowState.Normal)
        {
            return this.Size;
        }
        else
        {
            return this.RestoreBounds.Size;
        }
    }
    set
    {
        ...
    }
}

Mas não consigo ver uma maneira de vincular isso no designer (o tamanho está faltando na lista PropertyBinding).

Foi útil?

Solução

Finalmente criei uma subclasse Form que resolve isso de uma vez por todas.Para usá-lo:

  1. Herdar de RestorableForm em vez de Form.
  2. Adicione uma ligação em (ApplicationSettings) -> (PropertyBinding) a WindowRestoreState.
  3. Chame Properties.Settings.Default.Save() quando a janela estiver prestes a fechar.

Agora a posição e o estado da janela serão lembrados entre as sessões.Seguindo as sugestões de outros postadores abaixo, incluí uma função ConstrainToScreen que garante que a janela se encaixe perfeitamente nas telas disponíveis ao se restaurar.

Código

// Consider this code public domain. If you want, you can even tell
// your boss, attractive women, or the other guy in your cube that
// you wrote it. Enjoy!

using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;

namespace Utilities
{
    public class RestorableForm : Form, INotifyPropertyChanged
    {
        // We invoke this event when the binding needs to be updated.
        public event PropertyChangedEventHandler PropertyChanged;

        // This stores the last window position and state
        private WindowRestoreStateInfo windowRestoreState;

        // Now we define the property that we will bind to our settings.
        [Browsable(false)]        // Don't show it in the Properties list
        [SettingsBindable(true)]  // But do enable binding to settings
        public WindowRestoreStateInfo WindowRestoreState
        {
            get { return windowRestoreState; }
            set
            {
                windowRestoreState = value;
                if (PropertyChanged != null)
                {
                    // If anybody's listening, let them know the
                    // binding needs to be updated:
                    PropertyChanged(this,
                        new PropertyChangedEventArgs("WindowRestoreState"));
                }
            }
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            WindowRestoreState = new WindowRestoreStateInfo();
            WindowRestoreState.Bounds
                = WindowState == FormWindowState.Normal ?
                  Bounds : RestoreBounds;
            WindowRestoreState.WindowState = WindowState;

            base.OnClosing(e);
        }

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

            if (WindowRestoreState != null)
            {
                Bounds = ConstrainToScreen(WindowRestoreState.Bounds);
                WindowState = WindowRestoreState.WindowState;
            }
        }

        // This helper class stores both position and state.
        // That way, we only have to set one binding.
        public class WindowRestoreStateInfo
        {
            Rectangle bounds;
            public Rectangle Bounds
            {
                get { return bounds; }
                set { bounds = value; }
            }

            FormWindowState windowState;
            public FormWindowState WindowState
            {
                get { return windowState; }
                set { windowState = value; }
            }
        }

        private Rectangle ConstrainToScreen(Rectangle bounds)
        {
            Screen screen = Screen.FromRectangle(WindowRestoreState.Bounds);
            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 Rectangle(left, top, width, height);
        }
    }
}

Referências de ligações de configurações

Outras dicas

A razão pela qual a propriedade Form.Size não está disponível na interface de ligação de configurações é porque esta propriedade está marcada DesignerSerializationVisibility.Hidden.Isso significa que o designer não sabe como serializá-lo, muito menos gerar uma ligação de dados para ele.Em vez disso, o Formulário.ClientSize propriedade é aquela que é serializada.

Se você tentar ser inteligente vinculando Localização e Tamanho do cliente, você verá outro problema.Ao tentar redimensionar seu formulário a partir da borda esquerda ou superior, você verá um comportamento estranho.Aparentemente, isso está relacionado à maneira como a ligação de dados bidirecional funciona no contexto de conjuntos de propriedades que se afetam mutuamente.Ambos Localização e Tamanho do cliente eventualmente chamar um método comum, SetBoundsCore().

Além disso, a vinculação de dados a propriedades como Localização e Tamanho simplesmente não é eficiente.Cada vez que o usuário move ou redimensiona o formulário, o Windows envia centenas de mensagens para o formulário, fazendo com que a lógica de vinculação de dados faça muito processamento, quando tudo o que você realmente deseja é armazenar a última posição e tamanho antes de o formulário ser fechado.

Esta é uma versão muito simplificada do que eu faço:

private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
    Properties.Settings.Default.MyState = this.WindowState;
    if (this.WindowState == FormWindowState.Normal)
    {
       Properties.Settings.Default.MySize = this.Size;
       Properties.Settings.Default.MyLoc = this.Location;
    }
    else
    {
       Properties.Settings.Default.MySize = this.RestoreBounds.Size;
       Properties.Settings.Default.MyLoc = this.RestoreBounds.Location;
    }
    Properties.Settings.Default.Save();
}

private void MyForm_Load(object sender, EventArgs e)
{
    this.Size = Properties.Settings.Default.MySize;
    this.Location = Properties.Settings.Default.MyLoc;
    this.WindowState = Properties.Settings.Default.MyState;
} 

Por que esta é uma versão muito simplificada?Porque fazer isso corretamente é muito mais complicado do que parece :-)

Bem, eu brinquei rapidamente com isso e você está correto, embora não haja como diretamente vincular o tamanho do formulário em AppSettings, você pode adicionar seus próprios valores e alterar o tamanho durante o carregamento.

Talvez eu recomende que, se esse for um recurso comum, você subclasse Form e faça com que ele teste automaticamente o App.Config para obter as configurações de tamanho dos formulários.

(Ou você pode criar seu próprio arquivo.Faça com que ele consulte um arquivo Xml "formname.settings.xml" ou algo assim?- Pensando alto!)..

Aqui está o que eu tinha (muito difícil, sem verificação de erros, etc.).

App.Config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
        <add key ="FormHeight" value="500" />
        <add key ="FormWidth" value="200"/>
    </appSettings>
</configuration>

Código do formulário

    private void Form1_Load(object sender, EventArgs e)
    {
        string height = ConfigurationManager.AppSettings["FormHeight"];
        int h = int.Parse(height);
        string width = ConfigurationManager.AppSettings["FormWidth"];
        int w = int.Parse(width);
        this.Size = new Size(h, w);
    }

Um dos motivos pelos quais imagino que a vinculação de tamanho não seja permitida é porque a tela pode mudar entre as sessões.

Carregar o tamanho novamente quando a resolução for reduzida pode fazer com que a barra de título ultrapasse os limites da tela.

Você também precisa ter cuidado com configurações de vários monitores, onde os monitores podem não estar mais disponíveis na próxima execução do aplicativo.

Concordo com a resposta de Rob Cooper.Mas acho que Martin tem um argumento muito bom.Nada como fazer com que os usuários abram seu aplicativo e ele fique fora da tela!

Então, na realidade, você desejará combinar as duas respostas e ter em mente as dimensões atuais da tela antes de definir o tamanho do seu formulário.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top