• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

Nazgulled

Como mover um Form apenas na vertical?

9 mensagens neste tópico

Uma das sugestões que me fizeram no tópico do FireNotes foi permitir mover o Form apenas na vertical quando este se encontra "snapped" a um dos lados do ecrã. Ora, eu não faço ideia de como fazer isto... Até faço, mas queria uma solução diferente. A solução que me vem à cabeça seria algo dentro destes moldes:

public partial class Form1 : Form {
  private int _left = 0;
  private int _top = 0;

  public Form1() {
    InitializeComponent();
  }

  private void Form1_LocationChanged(object sender, EventArgs e) {
    if (_left != this.Left) {
      this.Left = _left;
    }
  }

  private void Form1_Load(object sender, EventArgs e) {
    _left = this.Left;
    _top = this.Top;
  }
}

Mas isto é muito mau...

No fundo, o utilizador iria "conseguir" mover o form horizontalmente, mas o código iria repor o valor original. O mal disto? Provoca demasiado "flicker" no movimento. Eu procuro uma solução que permita ao utilizador mover o Form na vertical de forma suave.

O ideal seria ter alguma forma de ignorar o movimento horizontal. Por exemplo, se existir alguma forma de ter acesso a um evento tipo "BeforeLocationChanged", poderíamos verificar a nova posição antes de mover o Form e aqui sim, reponhamos o valor original antes da posição ser alterada e o "flicker" desaparecia. Mas um evento assim, não existe por omissão no Form e não sei se será possível, nem como, criar um.

Ideias?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Depois de muito googlar lá consegui arranjar maneira de o fazer...

Em resumo o que fiz foi brincar com as mensagens do Windows. O grande problema nisto é que a documentação existente para este tipo de coisas não é lá grande espingarda e o que deu trabalho foi encontrar exemplos das mensagens que eu tinha de processar. E para quem quiser saber, tive de processar a mensagem WM_WINDOWPOSCHANGING.

Mostrava um exemplo de código mas ainda não tenho o código limpo para mostrar isto, apenas uma salgalhada de código para ver se conseguia por a funcionar. Assim que limpar o código, posso colocar aqui se estiverem interessados....

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Olá,

Exacto... A mensagem que você precisa tratar é a WM_WINDOWPOSCHANGING. Essa mensagem é recebida ANTES do formulário ser movido, por isso basta você sobrescrever o método WndProc do seu formulário, capturar essa mensagem, e alterar a coordenada X para o valor original do form (Location.X), preservando a posição horizontal do formulário, sempre...

Como você disse que seu código estava uma "salgalhada" hehehe. Fiz um pequeno exemplo, bem simples, do que imagino que seja o seu objectivo:

As informações sobre a movimentação da janela (coordenadas, handle, etc...) estão representadas na seguinte estrutura:

// Essa e a estrutura que vem no LParam
// da mensagem WM_WINDOWPOSCHANGING
[structLayout(LayoutKind.Sequential)]
public struct WindowPos
{
    public IntPtr Hwnd;
    public IntPtr HwndInsertAfter;
    public int X;
    public int Y;
    public int CX;
    public int CY;
    public int Flags;
}

No seu formulário, como disse acima, basta fazer um override no método WndProc, e modificar a mensagem para que o form permaneça sempre na mesma coordenada X:

// Sobrescreve o metodo WndProc, para capturar (e modificar)
// as mensagens do Windows recebidas pelo formulario
protected override void WndProc(ref Message mensagem)
{
    // Identificador da mensagem WM_WINDOWPOSCHANGING
    const int WM_WINDOWPOSCHANGING = 0x46;

    switch (mensagem.Msg)
    {
        case WM_WINDOWPOSCHANGING:
            {
                // Estao a tentar movimentar o formulario...

                // Obtem os dados da estrutura que estao no LParam
                WindowPos newPosition = new WindowPos();
                newPosition = (WindowPos)Marshal.PtrToStructure(mensagem.LParam, typeof(WindowPos));

                // Altera o valor da nova posicao horizontal
                // para o valor original de onde o form foi iniciado
                newPosition.X = this.Location.X;

                // Converte a estrutura modificada de volta para o LParam
                Marshal.StructureToPtr(newPosition, mensagem.LParam, true);
                break;
            }
    }

    // Deixa a classe base tratar a mensagem
    base.WndProc(ref mensagem);
}

Abraços,

Caio Proiete

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

O meu código está uma salgalhada porque está inserido no código principal da minha aplicação sem qualquer preocupação de sintaxe/indentação, foi só para testar. Porque no fundo, é praticamente igual a esse exemplo.

Só uma nota, esta linha:

WindowPos newPosition = new WindowPos();

é desnecessária. Claro que se for removida, a linha seguinte tem de ser modificada para:

WindowPos newPosition = (WindowPos)Marshal.PtrToStructure(mensagem.LParam, typeof(WindowPos));

E não sei se será boa ideia declarar o WM_WINDOWPOSCHANGING dentro do WndProc. Tanto quanto sei, ele não será declarado de cada vez que o método WndProc for chamado? (o qual é chamado constantemente) De qualquer forma, eu tenho sempre uma classe, NativeMethods para este tipo de coisas. Que segundo o standard do FxCop é o que se deve fazer, claro que isto é subjectivo, mas eu gosto de seguir estes standards.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Só uma nota, esta linha:

WindowPos newPosition = new WindowPos();

é desnecessária.

Tens toda a razão... É mania de chamar o construtor explicitamente. Venho do C++, e quando sinto falta memset, tento compensar :biggrin:

E não sei se será boa ideia declarar o WM_WINDOWPOSCHANGING dentro do WndProc.

É uma boa, desde que esteja declarada como const... Ela só será declarada uma vez, para todas as chamadas.

É equivalente a declará-la como const dentro da classe, excepto que fica no âmbito do método (que é o objectivo, já que não é utilizada em nenhum outro lado).

Abraços,

Caio Proiete

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Mas se fica no âmbito do método, quando o método terminar, a variável não será "destruída"? Mesmo sendo uma constante pensei que fosse destruída na mesma e que o const serviria apenas para impedir a alteração do valor... Correcto ou errado?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Mas se fica no âmbito do método, quando o método terminar, a variável não será "destruída"? Mesmo sendo uma constante pensei que fosse destruída na mesma e que o const serviria apenas para impedir a alteração do valor... Correcto ou errado?

Não sei como funciona em .NET, mas se a variável for constante (não pode ser modificada) então o compilador está livre de meter a variável noutra secção de memória (read-only data por exemplo) e assim escusa de criar sempre uma variável em cada chamada da função. Tal como disse isto é apenas um bitaite, não sei como funciona. Se alguém souber, também estou interessado. :)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Boas,

Creio que o Triton disse tudo. O conceito de variáveis const é o mesmo para todas as linguagens... Variáveis do tipo const, são variáveis que não podem ter o seu valor alterado, e ficam em um lugar "especial" da memória. No entanto, o mais comum que se vê por aí, é a declaração de variáveis const no âmbito da classe (normalmente acompanhada de algum modificador como public, protected, private, etc...), e foi por isso que o Ricardo achou estranho.

O .NET oferece a possibilidade de declarar variáveis const dentro de um método, e se encarrega de tratá-la como const na mesma, excepto que apenas o método pode utilizar a variável. Se você fizer dez ou vinte chamadas ao método, apenas uma variávei será criada, na primeira vez que você efectuar a chamada. Se você nunca chegar a executar o método, então nenhuma variável será criada.

Se quiserem ler um pouco mais sobre isso, dêem uma vista de olhos no MSDN, que tem exemplos:

http://msdn.microsoft.com/en-us/library/e6w8fe1b.aspx

Abraços,

Caio Proiete

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora