2013-10-06 4 views
1

У меня есть окно winform. Когда я изменяю размер экрана, экран сразу увеличивается или уменьшается.Переопределить поведение изменения окна winform

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

Я видел несколько примеров, которые показывают, что скрывая рамку окна, а затем, щелкнув по самому краю рамки окна.

Я хочу, чтобы при нажатии на кадре окна (я не хочу, чтобы скрыть рамку) и не на окне.

Есть ли способ сделать это? (Может переопределить поведение Resize любым способом).

ответ

5

Я уверен, что вы не можете найти какое-либо решение в Интернете. Однако я пробовал демо для этого, и он работает довольно хорошо.

В winforms и многих других технологиях пользовательского интерфейса вы не можете сделать что-то вне самого окна. Чтобы получить эффект, который мы хотим, мы должны отобразить примерно ориентировочную границу снаружи или внутри окна в зависимости от того, как пользователь изменяет размер. Похоже, мы застряли?

НО есть своего рода техника для этого (я называю это методом слоя). Нам нужен прозрачный, не сфокусированный слой, чтобы отобразить ориентировочную границу. Этот слой будет иметь свои Size и Location, синхронизированные с Size и Location (с небольшим смещением) главного окна. По умолчанию слой также будет invisible и будет отображаться только при изменении размера и скрытия пользователя при окончательном изменении размера.

Это очень хорошо с техникой, о которой я упомянул. Однако как предотвратить/отказаться от размера по умолчанию, когда пользователь изменяет размер окна? Это, к счастью, что Win32 поддерживает 2 сообщения для этого нужно сделать легко:

  • WM_RESIZING: посылается в окно при запуске пользователем и сохраняет изменения размера. LParam сохраняет структуру RECT текущего окна при изменении размера. Мы читаем эту информацию для правильной визуализации границы. Затем нам нужно изменить этот RECT на текущий Bounds окна, чтобы отменить эффект изменения размера по умолчанию (размер и местоположение меняются немедленно).
  • WM_EXITSIZEMOVE: отправляется в окно при изменении размера или перемещении. Нам нужно поймать это сообщение, чтобы назначить Size и Location окна на основе Size и Location прозрачного слоя и, конечно же, скрыть слой.

Теперь проблема полностью разрешимая. Вот демо-код, который я сделал. Обратите внимание, что здесь есть очень неприятная неразрешимая и непонятная ошибка, это происходит, когда вы изменяете размер угла Top-Left, Size обновляется правильно после отпускания мыши, но Location устанавливается со смещением. Я отлаживаю, но не повезло.В какой-то момент Top и Left перескакивают на неожиданные значения для без ясной причины. Однако, изменение размера по всем сторонам (слева, сверху, справа, внизу) и другими углами в порядке. Фактически, изменение размера на Top-Left corner вряд ли выполняется пользователем, поэтому, я думаю, это решение приемлемо.

//Must add using System.Runtime.InteropServices; 
public partial class Form1 : Form 
{   
    public Form1() 
    { 
     InitializeComponent(); 
     //Sizing border initialization 
     SizingBorderWidth = 3; 
     SizingBorderStyle = DashStyle.Custom; 
     SizingBorderColor = Color.Orange; 
     //layer initialization 
     layer.Owner = this;//especially this one. 
     layer.Width = Width + SizingBorderWidth * 2; 
     layer.Height = Height + SizingBorderWidth * 2;       
     //Paint the border when sizing 
     layer.Paint += (s, e) => { 
      using (Pen p = new Pen(SizingBorderColor) { Width = SizingBorderWidth }) { 
       if (Use3DSizingBorder) { 
        ControlPaint.DrawBorder3D(e.Graphics, sizingRect.Left, sizingRect.Top, sizingRect.Width, sizingRect.Height, Border3DStyle.Bump, Border3DSide.All); 
       } 
       else { 
        p.DashStyle = SizingBorderStyle; 
        p.LineJoin = LineJoin.Round; 
        if(p.DashStyle == DashStyle.Custom) 
         p.DashPattern = new float[] { 8f, 1f, 1f, 1f };//length of each dash from right to left 
        e.Graphics.DrawRectangle(p, sizingRect); 
       } 
      } 
     }; 
     //Bind the Location of the main form and the layer form together 
     LocationChanged += (s, e) => { 
      Point p = Location; 
      p.Offset(-SizingBorderWidth, -SizingBorderWidth); 
      layer.Location = p; 
     }; 
     //Set the intial Location of layer 
     Load += (s, e) =>{     
      Point p = Location; 
      p.Offset(-SizingBorderWidth, -SizingBorderWidth); 
      layer.Location = p; 
     };    
    } 
    //Set this to true to use 3D indicative/preview border 
    public bool Use3DSizingBorder { get; set; } 
    //Change the indicative/preview border thickness 
    public int SizingBorderWidth { get; set; } 
    //Change the indicative/preview border style 
    public DashStyle SizingBorderStyle { get; set; } 
    //Change the indicative/preview border color 
    public Color SizingBorderColor { get; set; } 
    //hold the current sizing Rectangle 
    Rectangle sizingRect; 
    bool startSizing; 
    bool suppressSizing; 
    //This is a Win32 RECT struct (don't use Rectangle) 
    public struct RECT 
    { 
     public int left, top, right, bottom; 
    } 
    protected override void WndProc(ref Message m) 
    { 
     if (m.Msg == 0x214&&!suppressSizing)//WM_SIZING = 0x214 
     {     
      RECT rect = (RECT) m.GetLParam(typeof(RECT)); 
      int w = rect.right - rect.left; 
      int h = rect.bottom - rect.top; 
      sizingRect = new Rectangle() {X = SizingBorderWidth/2, Y = SizingBorderWidth/2, 
              Width = w, Height = h}; 
      layer.Left = rect.left-SizingBorderWidth; 
      layer.Top = rect.top-SizingBorderWidth; 
      layer.Width = w+2*SizingBorderWidth; 
      layer.Height = h+2*SizingBorderWidth; 
      if (!startSizing) 
      { 
       layer.Show(); 
       startSizing = true; 
      } 
      layer.Invalidate(); 
      //Keep the current position and size fixed 
      rect.right = Right; 
      rect.bottom = Bottom; 
      rect.top = Top; 
      rect.left = Left; 
      //--------------------------- 
      Marshal.StructureToPtr(rect, m.LParam, true); 
     } 
     if (m.Msg == 0x232)//WM_EXITSIZEMOVE = 0x232 
     { 
      layer.Visible = false; 
      BeginInvoke((Action)(() => { 
       suppressSizing = true; 
       Left = layer.Left + SizingBorderWidth; 
       Top = layer.Top + SizingBorderWidth; 
       Width = layer.Width - 2 * SizingBorderWidth; 
       Height = layer.Height - SizingBorderWidth * 2; 
       suppressSizing = false; 
      })); 
      startSizing = false; 
     } 
     base.WndProc(ref m);    
    } 
    //Here is the layer I mentioned before. 
    NoActivationForm layer = new NoActivationForm(); 
}  
public class NoActivationForm : Form { 
    public NoActivationForm() { 
     //The following initialization is very important 
     TransparencyKey = BackColor; 
     FormBorderStyle = FormBorderStyle.None; 
     ShowInTaskbar = false; 
     StartPosition = FormStartPosition.Manual;    
     //----------------------------------------------       
    } 
    protected override bool ShowWithoutActivation { 
     get { return true; } 
    } 
} 

Некоторые снимки экрана:

enter image description here enter image description here enter image description here enter image description here

EDIT: (Это изменение было предложено Hodaya Shalom, ОП (странно :)

Я нашел решение левого угла м:

перед BeginInvoke я сохранить переменные и в Invoke я поставил локальную переменную:

int _top = layer.Top + SizingBorderWidth; 
int _left = layer.Left + SizingBorderWidth; 
int _width = layer.Width - 2 * SizingBorderWidth; 
int _height = layer.Height - SizingBorderWidth * 2; 
BeginInvoke((Action)(() => { 
    suppressSizing = true; 
    Left = _left; 
    Top = _top; 
    Width =_width; 
    Height =_height; 
    suppressSizing = false; 
})); 
+1

OMG. Этот ответ так хорош. Спасибо –

+1

@HodayaShalom благодарит за размещение предложения. Это действительно работает. Ну, однако я не думаю, что это из-за использования «BeginInvoke», я использовал «BeginInvoke», потому что я думаю, что нам нужно как можно скорее выполнить обновление окна. Нам не нужно использовать 'BeginInvoke', если это из-за' BeginInvoke', удаление 'BeginInvoke' решило бы проблему, но это не могло. Я думаю, что назначение 'Left' и' Top' ** напрямую ** может вызвать проблему, поэтому сохранение значений в некоторые локальные переменные решает проблему. По крайней мере, благодаря вам, я знаю об этом странном поведении и применяю его в тех же ситуациях. –

Смежные вопросы