1

正如标题所述,是否有可能/如何创建一个自定义窗口来绘制?通常,您只会使用表单和表单控件,但我想要我自己的带有句柄的窗口,我将在该窗口上附加挂钩并处理绘制事件等。这可能吗?本质上,我只需要一个容器来存放我的程序图像,而不是表单。如果不在 VB.Net 中,是否可以在 C# 中使用?

编辑:我只是不太喜欢窗口的绘制方式(即使控制绘画事件)。我删除了表单边框和控制栏,并用我自己的功能替换了它们(放置最大/最小/退出按钮、标题、表单边框 + 大小等),所以我使用的表单本质上只是一个浮动面板 -虽然带有内置的钩子,当然很好。但是表格仍然闪烁太多,所以我想自己处理一切。我在我使用的所有控件上使用双缓冲,并且我使用 setbounds 来移动/调整控件的大小,而不是单独设置宽度/高度(减少了一些闪烁)。我在窗体的绘制事件中绘制窗体边框,其余部分作为控件绘制(包括窗体的顶部栏)。

我最讨厌展开表单时看到的黑框(通常在减小窗口大小时看不到,但仍有少量闪烁)。我猜另一种方法,也许是不同的绘制风格(在 VB 2010 中)或其他方法也可以。

编辑(再次):无论表单上有多少控件,都会出现黑盒问题。如果我尝试手动调整它的大小(下面发布的自定义空表单控件继承自 Form),则在单击和拖动事件期间在每个 mousemove 上使用 setbounds(在不希望的情况下不会发生,所以我知道它没有运行子超过它必须)。

编辑(代码): http: //img211.imageshack.us/img211/900/j9c.png 所以即使在没有控件的空白“SimpleForm”(如第一个答案中发布的那样)上,当调整为更大时(在图片,调整了东北方向的大小),黑框绘制在将绘制表单的位置下方。第二个答案中发布的 Controlstyles / backbuffering 以及 Hans 发布的 createparams。这是我用来设置表单边界的内容:

Protected Overrides ReadOnly Property CreateParams() As CreateParams
    Get
        Dim cp As CreateParams = MyBase.CreateParams
        cp.ExStyle = cp.ExStyle Or &H2000000
        cp.Style = cp.Style Or &H2000000
        Return cp
    End Get
End Property 'CreateParams

Public Sub New(ByRef ContentFolder As String, ByRef x As Integer, ByRef y As Integer, ByRef w As Integer, ByRef h As Integer)
    FormBorderStyle = FormBorderStyle.None
'Note, I have tried the original suggested control styles in many combinations 
    Me.SetStyle(ControlStyles.OptimizedDoubleBuffer Or ControlStyles.ResizeRedraw Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint
    UpdateStyles()

    OL = x 'Used for resizing, to know what the original bounds were - especially in maximizing, didn't like the standards maximize call
    OT = y
    OW = w
    OH = h
    BackColor = Color.White
    BorderColor = New Pen(BarColor.Color)
    MinimumSize = New Size(200, 200)
    TransparencyKey = Color.FromArgb(255, 255, 0, 128)
    CF = ContentFolder
    ControlBar = New FormBar(Me, "Explorer woo", CF)

    AddHandler Me.Load, AddressOf EF_Load
    AddHandler Me.MouseUp, AddressOf EF_MouseUp
    AddHandler Me.MouseDown, AddressOf EF_MouseDown
    AddHandler Me.MouseMove, AddressOf EF_MouseMove
    AddHandler Me.LostFocus, AddressOf EF_LostFocus
End Sub

Public Sub EF_Load(ByVal sender As Object, ByVal e As EventArgs)
    SetFormBounds(OL, OT, OW, OH)
End Sub

Protected Overrides Sub OnSizeChanged(ByVal e As EventArgs)
    ControlBar.SetBar(Width) 'Sets the width of controlbar to new width, and updates position of the 3 top-right form buttons
    If Not (_backBuffer Is Nothing) Then
        _backBuffer.Dispose()
        _backBuffer = Nothing
    End If
    RaiseEvent Resized(Me, e) 'Resizes controls in custom handler, in this example, it is unused - with controls, they don't flicker when resized though
    MyBase.OnSizeChanged(e)
End Sub

Private Sub SetFormBounds(ByRef l As Integer, ByRef t As Integer, ByRef w As Integer, ByRef h As Integer)
    If w < Me.MinimumSize.Width Then
        w = Me.MinimumSize.Width
        l = Left
    End If
    If h < Me.MinimumSize.Height Then
        h = Me.MinimumSize.Height
        t = Top
    End If
    If l = Left AndAlso t = Top AndAlso w = Width AndAlso h = Height Then Exit Sub
    ControlBar.SetBar(w)
    SetBounds(l, t, w, h)
    'Used for detecting if user coords are on the form borders with L-shaped areas so as to not include too much of the interior of the bar, Borderthickness = pixel width of border
    CornerRects = New List(Of Rectangle) From {{New Rectangle(0, 0, BorderThickness, 15)}, {New Rectangle(0, 0, 15, BorderThickness)}, {New Rectangle(Width - 15, 0, 15, BorderThickness)}, {New Rectangle(Width - BorderThickness, 0, BorderThickness, 15)}, {New Rectangle(0, Height - 15, BorderThickness, 15)}, {New Rectangle(BorderThickness, Height - BorderThickness, 10, BorderThickness)}, {New Rectangle(Width - BorderThickness, Height - 15, BorderThickness, 15)}, {New Rectangle(Width - 15, Height - BorderThickness, 10, BorderThickness)}}
End Sub

Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
    If _backBuffer Is Nothing Then
        _backBuffer = New Bitmap(Me.ClientSize.Width, Me.ClientSize.Height)
    End If

    Dim g As Graphics = Graphics.FromImage(_backBuffer)
    g.Clear(SystemColors.Control)
    'Draw Control Box
    g.TextRenderingHint = Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit
    g.FillRectangle(BarColor, 0, 0, Width, ControlBar.Height)
    If ControlBar.Title <> "" Then g.DrawString(ControlBar.Title, ControlBar.Font, ControlBar.FontBrush, ControlBar.TextLeft, ControlBar.TextTop)
    g.DrawImage(FormBar.bmpCorners(0), 0, 0) 'Makes transparent corner, very small bitmap created at run-time
    g.DrawImage(FormBar.bmpCorners(1), Width - FormBar.bmpCorners(0).Width, 0)
    'Draw Control Box buttons top right
    If ControlBar.ExitButton.Enabled = True Then g.DrawImage(ControlBar.ExitButton.Img, ControlBar.ExitButton.Rect.X, ControlBar.ExitButton.Rect.Y)
    If ControlBar.MaximizeButton.Enabled = True Then g.DrawImage(ControlBar.MaximizeButton.Img, ControlBar.MaximizeButton.Rect.X, ControlBar.MaximizeButton.Rect.Y)
    If ControlBar.MinimizeButton.Enabled = True Then g.DrawImage(ControlBar.MinimizeButton.Img, ControlBar.MinimizeButton.Rect.X, ControlBar.MinimizeButton.Rect.Y)
    If Not ControlBar.Ico Is Nothing Then g.DrawImage(ControlBar.Ico, 5, 5) 'Draw Control Box icon (program icon) if it is set

    'Draw the form border
    For i = 0 To BorderThickness - 1
        g.DrawLine(BorderColor, i, ControlBar.Height, i, Height - 1)
        g.DrawLine(BorderColor, Width - 1 - i, ControlBar.Height, Width - 1 - i, Height - 1)
        g.DrawLine(BorderColor, BorderThickness, Height - 1 - i, Width - BorderThickness, Height - 1 - i)
    Next
    g.Dispose()
    e.Graphics.DrawImageUnscaled(_backBuffer, 0, 0)
End Sub

Protected Overrides Sub OnPaintBackground(ByVal pevent As PaintEventArgs)
End Sub
4

2 回答 2

1

这在任何一种语言中都根本不可能。这与语言无关,甚至与框架(即 WinForms)无关。相反,这更多是因为 Windows 本身的设计。本质上, Windows 中的一切都是一个窗口,Form该类代表一个基本的顶层窗口,可以直接显示在桌面上。如果要在桌面上显示一个窗口,则需要使用Form该类。此外,如果您想要一个可以附加挂钩的窗口句柄,则需要使用此类;它拥有所有必要的管道来实现这一目标。

但这并不意味着它必须看起来像一个默认Form对象。外观可无限定制。首先设置FormBorderStyle表单的属性以删除默认的窗口框架/镶边。这会给你一个完全空白的石板。然后,像你说的那样处理它的Paint事件。除了当您想要处理派生类的事件时,您应该OnXxx直接覆盖该方法,而不是订阅事件。所以你会有这个代码:

Public Class SimpleForm : Inherits Form

   Public Sub New()
      ' Alter the form's basic appearance by removing the window frame,
      ' which gives you a blank slate to draw onto.
      FormBorderStyle = FormBorderStyle.None

      ' Indicate that we're painting our own background.
      SetStyle(ControlStyles.Opaque, True)
   End Sub

   Protected Overrides Sub OnPaint(e As System.Windows.Forms.PaintEventArgs)
      ' Call the base class.
      MyBase.OnPaint(e)

      ' Paint the background...
      e.Graphics.FillRectangle(Brushes.MediumAquamarine, Me.ClientRectangle)

      ' ...and then the foreground.
      ' For example, drawing an 'X' to mark the spot!
      Using p As New Pen(Color.Navy, 4.0)
         e.Graphics.DrawLine(p, 0, 0, Me.Width, Me.Height)
         e.Graphics.DrawLine(p, Me.Width, 0, 0, Me.Height)
      End Using
   End Sub

End Class

当然,这样的窗口存在严重的可用性问题。对于初学者,用户无法在屏幕上移动或关闭它。如果您要消除默认边框,则需要自己处理这些事情。

于 2013-07-28T14:33:58.073 回答
0

你能展示你用来启用双缓冲的方法吗?这是一篇解决这个问题的文章。也许它会有所帮助。

https://web.archive.org/web/20140811193726/http://bobpowell.net/doublebuffer.aspx

基本上,代码是这样的(来自文章):

Private _backBuffer As Bitmap

Public Sub New
    InitializeComponents()

    Me.SetStyle(ControlStyles.AllPaintingInWmPaint OR _
                ControlStyles.UserPaint OR _
                ControlStyles.DoubleBuffer, True)
End Sub

Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
    If _backBuffer Is Nothing Then
        _backBuffer = New Bitmap(Me.ClientSize.Width, Me.ClientSize.Height)
    End If

    Dim g As Graphics = Graphics.FromImage(_backBuffer)

    'Paint on the Graphics object here

    g.Dispose()

   'Copy the back buffer to the screen
   e.Graphics.DrawImageUnscaled(_backBuffer, 0, 0)

End Sub 'OnPaint


'Don't allow the background to paint
Protected Overrides Sub OnPaintBackground(ByVal pevent As PaintEventArgs)
End Sub 'OnPaintBackground


Protected Overrides Sub OnSizeChanged(ByVal e As EventArgs)
    If Not (_backBuffer Is Nothing) Then
      _backBuffer.Dispose()
      _backBuffer = Nothing
    End If
    MyBase.OnSizeChanged(e)
End Sub 'OnSizeChanged
于 2013-07-29T14:13:40.203 回答