2014-09-13 2 views
6

В настоящее время я моделирую окна с несколькими прямоугольниками выделения, когда пользователь перетаскивает мышь. Для того, чтобы синхронизировать наше понимание, эта картина показывает эффект, я хочу, чтобы имитировать:Имитировать эффект перетаскивания Windows в Winform FlowLayoutPanel

enter image description here

Теперь я хочу, чтобы имитировать этот эффект на FlowLayoutPanel с некоторыми элементами управления внутри.

До сих пор я сумел получить эффект почти сделано:

enter image description here

То, что я здесь был положить расфокусированное границы менее полупрозрачный (половина непрозрачности) формы на вершине главной форма. Чтобы получить имитацию границы, я обработал SizeChanged и Paint, чтобы нарисовать границу.

Однако это решение иногда мерцает, как и на границе владелец не может списываться по времени:

enter image description here

Я попытался с помощью двойной буферизации в форме крышки, установив DoubleBuffer в true, и переопределить CreateParam, чтобы установить WM_EX_COMPOSITED, но не работает.

Мой вопрос: Как уменьшить этот артефакт?

Большое спасибо!

Мой код:

Для формы крышки:

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

     BackColor = Color.CadetBlue; 
     FormBorderStyle = FormBorderStyle.None; 

     SizeChanged += (s, e) => Invalidate(); 
     Paint += (s, e) => 
        { 
         e.Graphics.Clear(BackColor); 

         using (var pen = new Pen(Color.DodgerBlue)) 
         { 
          e.Graphics.DrawRectangle(pen, 1, 1, Size.Width - 2, Size.Height - 2); 
         } 
        }; 
    } 

    protected override bool ShowWithoutActivation 
    { 
     get { return true; } 
    } 
} 

Для основной формы:

public Form1() 
{ 
    InitializeComponent(); 

    // mainPanel is the panel that simulates the dragging effect 
    mainPanel.MouseDown += (s, e) => 
           { 
            _isMouseDown = true; 
            _startPosition = e.Location; 
            coverForm.Location = mainPanel.PointToScreen(e.Location); 
            coverForm.Show(); 
           }; 
    mainPanel.MouseUp += (s, e) => 
           { 
            _isMouseDown = false; 
            coverForm.Hide(); 
           }; 
    mainPanel.MouseMove += CoverPanelMouseMoveHandler; 

    DoubleBuffered = true; 
} 

~Form1() 
{ 
    if (coverForm != null && !coverForm.IsDisposed) 
    { 
     coverForm.Dispose(); 
    } 
} 

# region Dragging Effect 
private void CoverPanelMouseMoveHandler(object sender, MouseEventArgs e) 
{ 
    if (_isMouseDown) 
    { 
     _curPosition = e.Location; 
     // find the dragging rectangle 
     var rect = CreateRect(_curPosition, _startPosition); 

     coverForm.Size = rect.Size; 
     coverForm.Location = mainPanel.PointToScreen(rect.Location); 

     foreach (Control control in mainPanel.Controls) 
     { 
      // logic to get button backcolor changed 
     } 

     mainPanel.Invalidate(true); 
    } 
} 

Update

Я попытался переопределить OnPaint и поставить мой рисунок там, но он дал ev ан хуже результат: старые краски не стираются:

enter image description here

Code I модифицирована для формы крышки:

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

     BackColor = Color.CadetBlue; 
     FormBorderStyle = FormBorderStyle.None; 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     base.OnPaint(e); 

     e.Graphics.Clear(BackColor); 

     using (var pen = new Pen(Color.FromArgb(255, 0, 0, 255))) 
     { 
      e.Graphics.DrawRectangle(pen, 0, 0, Size.Width - 1, Size.Height - 1); 
     } 
    } 

    protected override bool ShowWithoutActivation 
    { 
     get { return true; } 
    } 
} 

Update 2

На самом деле проблема я столкнулся составляет около рисунок выше FlowLayoutPanel, не является нормальным Панель. Причина, по которой я поставил Panel раньше, я искал ответ для моего мерцающего 2-слойного дизайна.Но так как кто-то подходит к проблеме с помощью , добавив контроль над панелью, чтобы получить ее над всеми элементами управления, я хотел бы указать на это: добавление элемента управления в панель было бы тривиально, но FlowLayoutPanel будет автоматически выравнивать добавленный элемент управления до следующее доступное положение, которое может испортить ожидаемый эффект.

+0

Вы можете попробовать использовать двойную буферизацию. www.google.ch/search?q=winforms+double+buffer –

+0

Я пробовал двойной буфер, но не работал, и, честно говоря, еще хуже. – nevets

+0

Возможно, вы можете поместить эти элементы управления внутри другого элемента управления (например, внутри настраиваемого элемента управления, полученного из элемента управления «Панель»), а не непосредственно внутри «Формы». Взгляните на эту тему для некоторых решений: http://stackoverflow.com/questions/8369163/c-sharp-paint-program-flickering/8369251#8369251. –

ответ

0

EDIT: с помощью дополнительного PictureBox и Bitmap делает все это работает

Следующая панель рисует прямоугольник без мерцания:

internal sealed class MyPanel : Panel 
{ 
    private readonly PictureBox _pictureBox; 
    private Bitmap _bitmapContent; 
    private Bitmap _bitmapForeground; 
    private Point? _point1; 
    private Point? _point2; 

    public MyPanel() 
    { 
     DoubleBuffered = true; 
     _pictureBox = new PictureBox(); 
    } 

    protected override void OnSizeChanged(EventArgs e) 
    { 
     if (_bitmapForeground != null) _bitmapForeground.Dispose(); 
     _bitmapForeground = new Bitmap(Size.Width, Size.Height); 
     if (_bitmapContent != null) _bitmapContent.Dispose(); 
     _bitmapContent = new Bitmap(Size.Width, Size.Height); 
     _pictureBox.Size = Size; 
     _pictureBox.Image = _bitmapForeground; 
     base.OnSizeChanged(e); 
    } 

    protected override void OnMouseDown(MouseEventArgs e) 
    { 
     _point1 = e.Location; 
     DrawToBitmap(_bitmapContent, new Rectangle(0, 0, Size.Width, Size.Height)); 
     SetControlsVisibility(false); 
     Controls.Add(_pictureBox); 
     base.OnMouseDown(e); 
    } 

    private void SetControlsVisibility(bool visible) 
    { 
     IEnumerable<Control> ofType = Controls.OfType<Control>(); 
     foreach (Control control in ofType) 
     { 
      control.Visible = visible; 
     } 
    } 

    protected override void OnMouseUp(MouseEventArgs e) 
    { 
     Controls.Remove(_pictureBox); 
     SetControlsVisibility(true); 
     _point1 = null; 
     _point2 = null; 
     Refresh(); 
     base.OnMouseUp(e); 
    } 

    protected override void OnMouseMove(MouseEventArgs e) 
    { 
     if (_point1 != null) 
     { 
      _point2 = e.Location; 
      if (_point1 != null && _point2 != null) 
      { 
       Point p1 = _point1.Value; 
       Point p2 = _point2.Value; 
       int x1 = p1.X; 
       int y1 = p1.Y; 
       int x2 = p2.X; 
       int y2 = p2.Y; 
       int xmin = Math.Min(x1, x2); 
       int ymin = Math.Min(y1, y2); 
       int xmax = Math.Max(x1, x2); 
       int ymax = Math.Max(y1, y2); 
       using (Graphics graphics = Graphics.FromImage(_bitmapForeground)) 
       { 
        graphics.DrawImageUnscaled(_bitmapContent, 0, 0, _bitmapContent.Width, _bitmapContent.Height); 
        graphics.DrawRectangle(Pens.Red, new Rectangle(xmin, ymin, xmax - xmin, ymax - ymin)); 
       } 
       _pictureBox.Refresh(); 
      } 
     } 
     base.OnMouseMove(e); 
    } 
} 

Однако прямоугольник будет ниже контрольной, не знаю, почему ...

+0

Я уже пробовал этот подход и получил прямоугольник под элементами управления, который не был тем, чего я хочу, поэтому я отказался от подхода. Это потому, что 'Panel' на 1 уровень ниже чем те элементы управления в панели.Таким образом, использование панели «Panel» не будет охватывать элементы управления на панели. – nevets

+0

См. Мое редактирование, теперь оно нарисовано и не мерцает: D – Aybe

+0

oh Я тоже рассматривал этот подход, но не очень соответствовал моему делу, так как он имеет недостаток: если панель является «FlowLayoutPanel», не помещаются в нужное положение. К сожалению, перед нами стоит «FlowLayoutPanel». Я не ставил это в свой вопрос, потому что я ожидаю двухслойный дизайн, например, то, что я сделал. Очень жаль, но все равно хороший ответ :) – nevets

1

Video Demo of the Solution: Remember to Switch to 1080p
записи в виртуальной машине на дерьмовый Мачин е. Так любопытно медленно.


Вы получаете эти артефакты, потому что вы делаете комбинацию из трех вещей одновременно.

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

Aybe, при условии довольно умного решения, но он не позволяет вам просматривать его или видеть, есть ли какие-либо обновления на панели .... поскольку он в основном просто копирует последний вывод в растровое изображение и использует это как back back (так же, как вы предполагаете, что кто-то может сделать, когда делает выделение в программе рисования).

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


Код требует довольно много знаний WIN32 .... повезло для вас Microsoft уже сделала тяжелую работу. Мы собираемся включить прозрачность пикселя в вашем обложке, используя PerPixelAlphaForm от Microsoft (вы можете его использовать). Я вставляю код здесь. Он просто создает окно со стилем WS_EX_LAYERED. Сохраняет буфер, который AlphaBlended с экраном (простой, да?).

/******************************** Module Header ********************************\ 
Module Name: PerPixelAlphaForm.cs 
Project:  CSWinFormLayeredWindow 
Copyright (c) Microsoft Corporation. 



This source is subject to the Microsoft Public License. 
See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL. 
All other rights reserved. 

THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES 
OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. 
\*******************************************************************************/ 

#region Using directives 
using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Text; 
using System.Windows.Forms; 
using System.Drawing.Imaging; 
using System.Runtime.InteropServices; 
#endregion 

namespace CSWinFormLayeredWindow 
{ 
    public partial class PerPixelAlphaForm : Form 
    { 
     public PerPixelAlphaForm() 
     { 
      InitializeComponent(); 
     } 


     protected override CreateParams CreateParams 
     { 
      get 
      { 
       // Add the layered extended style (WS_EX_LAYERED) to this window. 
       CreateParams createParams = base.CreateParams; 
       createParams.ExStyle |= WS_EX_LAYERED; 
       return createParams; 
      } 
     } 


     /// <summary> 
     /// Let Windows drag this window for us (thinks its hitting the title 
     /// bar of the window) 
     /// </summary> 
     /// <param name="message"></param> 
     protected override void WndProc(ref Message message) 
     { 
      if (message.Msg == WM_NCHITTEST) 
      { 
       // Tell Windows that the user is on the title bar (caption) 
       message.Result = (IntPtr)HTCAPTION; 
      } 
      else 
      { 
       base.WndProc(ref message); 
      } 
     } 


     /// <summary> 
     /// 
     /// </summary> 
     /// <param name="bitmap"></param> 
     public void SelectBitmap(Bitmap bitmap) 
     { 
      SelectBitmap(bitmap, 255); 
     } 


     /// <summary> 
     /// 
     /// </summary> 
     /// <param name="bitmap"> 
     /// 
     /// </param> 
     /// <param name="opacity"> 
     /// Specifies an alpha transparency value to be used on the entire source 
     /// bitmap. The SourceConstantAlpha value is combined with any per-pixel 
     /// alpha values in the source bitmap. The value ranges from 0 to 255. If 
     /// you set SourceConstantAlpha to 0, it is assumed that your image is 
     /// transparent. When you only want to use per-pixel alpha values, set 
     /// the SourceConstantAlpha value to 255 (opaque). 
     /// </param> 
     public void SelectBitmap(Bitmap bitmap, int opacity) 
     { 
      // Does this bitmap contain an alpha channel? 
      if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) 
      { 
       throw new ApplicationException("The bitmap must be 32bpp with alpha-channel."); 
      } 

      // Get device contexts 
      IntPtr screenDc = GetDC(IntPtr.Zero); 
      IntPtr memDc = CreateCompatibleDC(screenDc); 
      IntPtr hBitmap = IntPtr.Zero; 
      IntPtr hOldBitmap = IntPtr.Zero; 

      try 
      { 
       // Get handle to the new bitmap and select it into the current 
       // device context. 
       hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); 
       hOldBitmap = SelectObject(memDc, hBitmap); 

       // Set parameters for layered window update. 
       Size newSize = new Size(bitmap.Width, bitmap.Height); 
       Point sourceLocation = new Point(0, 0); 
       Point newLocation = new Point(this.Left, this.Top); 
       BLENDFUNCTION blend = new BLENDFUNCTION(); 
       blend.BlendOp = AC_SRC_OVER; 
       blend.BlendFlags = 0; 
       blend.SourceConstantAlpha = (byte)opacity; 
       blend.AlphaFormat = AC_SRC_ALPHA; 

       // Update the window. 
       UpdateLayeredWindow(
        this.Handle,  // Handle to the layered window 
        screenDc,  // Handle to the screen DC 
        ref newLocation, // New screen position of the layered window 
        ref newSize,  // New size of the layered window 
        memDc,   // Handle to the layered window surface DC 
        ref sourceLocation, // Location of the layer in the DC 
        0,    // Color key of the layered window 
        ref blend,  // Transparency of the layered window 
        ULW_ALPHA  // Use blend as the blend function 
        ); 
      } 
      finally 
      { 
       // Release device context. 
       ReleaseDC(IntPtr.Zero, screenDc); 
       if (hBitmap != IntPtr.Zero) 
       { 
        SelectObject(memDc, hOldBitmap); 
        DeleteObject(hBitmap); 
       } 
       DeleteDC(memDc); 
      } 
     } 


     #region Native Methods and Structures 

     const Int32 WS_EX_LAYERED = 0x80000; 
     const Int32 HTCAPTION = 0x02; 
     const Int32 WM_NCHITTEST = 0x84; 
     const Int32 ULW_ALPHA = 0x02; 
     const byte AC_SRC_OVER = 0x00; 
     const byte AC_SRC_ALPHA = 0x01; 

     [StructLayout(LayoutKind.Sequential)] 
     struct Point 
     { 
      public Int32 x; 
      public Int32 y; 

      public Point(Int32 x, Int32 y) 
      { this.x = x; this.y = y; } 
     } 

     [StructLayout(LayoutKind.Sequential)] 
     struct Size 
     { 
      public Int32 cx; 
      public Int32 cy; 

      public Size(Int32 cx, Int32 cy) 
      { this.cx = cx; this.cy = cy; } 
     } 

     [StructLayout(LayoutKind.Sequential, Pack = 1)] 
     struct ARGB 
     { 
      public byte Blue; 
      public byte Green; 
      public byte Red; 
      public byte Alpha; 
     } 

     [StructLayout(LayoutKind.Sequential, Pack = 1)] 
     struct BLENDFUNCTION 
     { 
      public byte BlendOp; 
      public byte BlendFlags; 
      public byte SourceConstantAlpha; 
      public byte AlphaFormat; 
     } 

     [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, 
      ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, 
      Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags); 

     [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     static extern IntPtr CreateCompatibleDC(IntPtr hDC); 

     [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     static extern IntPtr GetDC(IntPtr hWnd); 

     [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); 

     [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     static extern bool DeleteDC(IntPtr hdc); 

     [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); 

     [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     static extern bool DeleteObject(IntPtr hObject); 

     #endregion 
    } 
} 

OK, которые должны устранить ваши полупрозрачный проблему. Не забудьте избавиться от переопределения WndProc (вам это не понадобится). Установите двойной буфер на false и TopMost на true.

Теперь устранить две другие проблемы. Надеюсь, вы подумали о том, как это сделать ... но я дам вам свое решение. Всегда сохраняйте PerPixelAlphaForm размером с MainForm. То же место, тот же РАЗМЕР. :) И измените размер растрового изображения BackBuffer PerPixelAlphaForm на тот же размер, что и. Когда вы это делаете, все, что вам нужно сделать, это перерисовать прямоугольник выбора. Зачем? потому что он полностью перекрывает весь MainForm.

Так в основном

`OnMouseDown` = Save initial point of mouse, show the Cover layer 
`OnMouseMove` = clear the PerPixelAlphaForm bitmap, draw your rectangle 
       call SelectBitmap again update the form 
`OnMouseUp` = hide the Cover layer (or whatever you want to do) 

У меня лично все это крюк вплоть до Control-Key


Чтобы очистить PerPixelAlphaForm нам нужно делать определенным образом. Дайте все значения альфа от 0.

public void ClearBackbuffer() 
{ 
    Graphics g = Graphics.FromImage(_reference_to_your_backbuffer_); 
    g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; 
    SolidBrush sb = new SolidBrush(Color.FromArgb(0x00, 0x00, 0x00, 0x00)); 
    g.FillRectangle(sb, this.ClientRectangle); 
    sb.Dispose(); 
    g.Dispose(); 
} 

Video Demo of the Solution: Remember to Switch to 1080p


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

+0

Это выглядит хорошо :) Я попробую позже и вернусь к вам: D – nevets

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