65.9K
CodeProject 正在变化。 阅读更多。
Home

背景图块创建器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (21投票s)

2011年7月25日

CPOL

7分钟阅读

viewsIcon

49339

downloadIcon

1530

一个用于创建有趣背景平铺图像的小型实用程序。包含“设为墙纸”功能

引言

首先:最近的一次更新中出现了一个相当讨厌的 bug,我没有发现。该问题现已解决。对于收到有 bug 版本的 BTC 的各位,我深表歉意。

背景平铺创建器(以下简称“BTC”)是一个简单的实用程序,可以根据现有图像创建背景平铺图像。根据您的源图像以及您在其上的选择,生成的结果可能从精美到怪异不等。只需打开一张图像,用鼠标进行选择,平铺就会自动创建。

BTC 如何创建平铺

当您在源图像上拖动选区时,BTC 会创建一个大小为选区两倍的新空白图像。原始选区放置在左上角。然后,BTC 会水平翻转选区,并将此图像放置在右上角。然后,此第二个图像会垂直翻转并放置在右下角,最后,第三个图像再次水平翻转并绘制到左下角。

您可以通过将鼠标指向适当的拖动手柄并朝一个或另一个方向拖动来调整选区的大小。您还可以通过按住鼠标左键在选区内部拖动来移动选区矩形。平铺和页面背景预览都可以随着您的绘制或移动选区而更新(这看起来很有趣)。要执行此操作,请在左窗格中选中“在选择过程中更新平铺”复选框。请注意,对于大于 100x100 的选区,这可能会对性能产生不利影响。如果您注意到程序在选择过程中有任何部分变慢,只需取消选中该复选框。然后,将在您释放鼠标时更新平铺,而不是在拖动过程中更新。

您可以使用左窗格中的滑块控件来调整完成的平铺的大小。选中“顺时针旋转 90 度”复选框会将平铺顺时针旋转 90 度。考虑到图像是一个平铺,进一步的翻转/旋转操作意义不大。

要保存您的平铺,请单击工具栏中的“保存”按钮。将打开一个标准的 Windows SaveFileDialog

已知问题:当调整完成的平铺大小以使其变大时,背景预览选项卡中各个平铺之间有时会出现细线。这仅发生在预览中。一旦图像被保存并在其他地方使用,就不会出现线条。我仍在试图弄清楚为什么会发生这种情况。

代码一瞥...

已添加一个引用:Shell32.dll

BTC 导入以下命名空间

Imports System.Windows.Forms
Imports System.IO
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Math
Imports System.Runtime.InteropServices
Imports Shell32

以下是整个项目中使用的变量和矩形

#Region "declarations"
  Friend title As String = "Background Tile Creator"
  Friend imgName As String 'the filename of the src image
  Dim msg As String 'for messages
  Dim x, y, l As Integer 'x&y coords, and width/height values
  Dim WithEvents pntPnl As New PaintPanel

  '-----------------------------------------------------
  'sr is the selection rectangle

  'r is used in drawing the source image

  'RectInfo is a rectangle that holds the value of the
  'last instance of the selection rectangle (sr). This
  'is used to properly draw a new instance of sr. You'll
  'see it used in the MouseMove sub.
  '-----------------------------------------------------

  Friend r, sr, RectInfo As Rectangle

  'grab-handles for resizing selection
  Dim grabHandles(8) As Rectangle
  Dim curs() As Cursor = {Cursors.SizeNWSE, Cursors.SizeNS, Cursors.SizeNESW, _
             Cursors.SizeWE, Cursors.SizeNWSE, Cursors.SizeNS, _
             Cursors.SizeNESW, Cursors.SizeWE, Cursors.Default}
  Friend grabSize As New Size(6, 6) 'size of grab-handle rects
  Friend grabPen As New Pen(Color.Black, 1) 'grab-handle outline
  Friend grabBrush As New SolidBrush(Color.White) 'grab-handle fill color

  Friend rectPoints As Point 'x-y location of sel rect
  Dim selSize As Size 'size of selection rect

  'these are for drawing the selection rectangle
  Dim myPen As New Pen(Color.White, 1)
  Dim innerBrush As New SolidBrush(Color.FromArgb(60, 0, 0, 255))

  Dim res As DialogResult
  Dim g As Graphics 'draw the original image
  '----------------------------------------------------------------
  'isDown is true anytime the left mouse button is pressed inside
  'the source image.

  'canResize is true when the mouse button is pressed over an edge 
  'of the selection rectangle. You'll get a double-arrow cursor.

  'canMove is true when the left mouse button is pressed more than
  'two pixels inside the selection rect. This is for moving the
  'selection rectangle around the image.
  '----------------------------------------------------------------
  Dim isDown, canResize, canMove As Boolean

  '----------------------------------------------------------------
  'original is a copy of the original image used as a source image.
  'bmp is the "working" image - a copy of the original
  'selBMP is the tile image created when you make a selection
  '----------------------------------------------------------------
  Friend bmp, original, selBMP As Bitmap

  'for determining resize and moving operations
  'of the selection rectangle (see mousemove event handler)
  Enum CursorPos
    TopLeft = 0
    TopSide = 1
    TopRight = 2
    RightSide = 3
    BottomRight = 4
    BottomSide = 5
    BottomLeft = 6
    LeftSide = 7
    Inside = 8
    NotOnRect = 9
  End Enum
  Dim curPos As CursorPos = CursorPos.NotOnRect

  'false after changes to tile - true when saved
  Dim isSaved As Boolean = False
  'full path of saved tile image
  Friend tilePath As String = String.Empty
  Dim openPath As String

#End Region

程序加载时要执行的一些操作...

Private Sub LoadApplication() Handles MyBase.Load
    Me.WindowState = FormWindowState.Maximized
    'if My.Settings.RecentFiles contains items, add them to the 
    'Open button's dropdown list
    Dim rf() As String = My.Settings.RecentFiles.Split("|")
    If rf.Length > 0 Then
      For Each s As String In rf
        If File.Exists(s) Then
          tb_Open.DropDown.Items.Add(New ToolStripMenuItem(s))
        End If
      Next
    End If
    '--------------End of dropdown list addition---------------
    AddHandler Me.Activated, AddressOf UpdateUI
    myPen.DashStyle = Drawing2D.DashStyle.Dash
    splt_Left.Panel2.Controls.Add(pntPnl)
    pntPnl.Dock = DockStyle.Fill
    UpdateUI()
End Sub

以及关闭时...

Private Sub main_FormClosing (ByVal sender As Object, _
        ByVal e As System.Windows.Forms.FormClosingEventArgs) _
        Handles Me.FormClosing
    'update My.Settings recent items list
    If tb_Open.DropDownItems.Count > 0 Then
      Dim itemStr As String = String.Empty
      For l = 0 To tb_Open.DropDownItems.Count - 1
        If File.Exists(tb_Open.DropDownItems(l).Text) Then
          itemStr &= tb_Open.DropDownItems(l).Text
          If l < tb_Open.DropDownItems.Count - 1 Then
            itemStr &= "|"
          End If
        End If
      Next
      My.Settings.RecentFiles = itemStr
      My.Settings.Save()
    End If
End Sub

要打开源图像,请单击工具栏中的“打开”按钮。这是一个 SplitButton。单击下拉菜单会显示您最近使用的五个源图像。单击按钮的左侧将打开一个 OpenFileDialog

Private Sub OpenFileFromDialog() Handles tb_Open.ButtonClick
    Try
      If Directory.Exists(Path.GetDirectoryName(openPath)) Then
        dialog_Open.InitialDirectory = openPath
      Else
        dialog_Open.InitialDirectory = _
        Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)
      End If
    Catch ex As Exception
      MsgBox(ex.ToString, MsgBoxStyle.Exclamation, title)
      Exit Sub
    End Try

    res = dialog_Open.ShowDialog
    If res = Windows.Forms.DialogResult.OK Then
      Try
        OpenNewSourceImage(dialog_Open.FileName)
      Catch ex As Exception
        MsgBox(ex.ToString, MsgBoxStyle.Exclamation, title)
      End Try
    End If
End Sub
 

'open from recent files list
Private Sub OpenFromList (ByVal sender As Object, _
        ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) _
        Handles tb_Open.DropDownItemClicked
    Try
      If File.Exists(e.ClickedItem.Text) Then
        OpenNewSourceImage(e.ClickedItem.Text)
      Else
        tb_Open.DropDownItems.Remove(e.ClickedItem)
        MsgBox("The selected file no longer exists." & Chr(10) & _
               "The name has been removed from the list.", _
               MsgBoxStyle.Information, title)
      End If

    Catch ex As Exception
      MsgBox(ex.ToString, MsgBoxStyle.Exclamation, title)
    End Try
End Sub

'this sub uses the value from the subs above to open a source image
Private Sub OpenNewSourceImage(ByVal imgPath As String)
    Try
      original = Bitmap.FromFile(imgPath)
      imgName = imgPath
      openPath = imgPath
      r = New Rectangle(0, 0, original.Width, original.Height)
      sr = Nothing
      statLabel_ImgName.Text = imgPath

      'set the picturebox backgroundimage to the source image
      'the selection rect is drawn over the background
      picbox_SrcImage.Size = original.Size
      picbox_SrcImage.BackgroundImage = original
      Me.Invalidate()
      'clear the tile preview picturebox
      picbox_TilePreview.Image = Nothing
      UpdateUI()
      UpdateRecentFiles()
    Catch ex As Exception
      MsgBox(ex.ToString, MsgBoxStyle.Exclamation, title)
    End Try
End Sub

创建选区矩形

与大多数图形应用程序一样,选区矩形是通过在图像上拖动鼠标来定义的。以下是 BTC 的实现方式

  • 当左鼠标按钮未按下
  • 移动鼠标定位以绘制选区,或将其移至现有选区的抓取柄上以调整大小。将鼠标放在选区内部以移动它(光标将变为 Cursors.SizeAll)。

    MouseMove 事件期间,canResizecanMove 布尔变量会设置为 TrueFalse。例如,如果您将鼠标指向抓取柄,canResize 变量将设置为 True,而 canMove 设置为 False

  • 当左鼠标按钮按下
  • 当在源图像上按下左鼠标按钮时,isDown 布尔变量将设置为 True。这会告诉 MouseMove 事件,正在绘制、移动或调整选区的大小。

    MouseDown 事件发生时,程序将根据在下一个 MouseMove 事件发生时,其中一个布尔值(canMovecanResize)是否为 true 来执行操作。

    如果 canMoveisDown 都为 True(鼠标按钮已按下且鼠标悬停在现有选区矩形内),则在移动鼠标时会拖动选区矩形。如果 isDowncanResize 都为 True(鼠标按钮已按下且鼠标悬停在抓取柄上),则在移动鼠标时会调整选区大小。

  • 当鼠标按钮释放时...
  • 当鼠标按钮释放并且 MouseUp 事件触发时,isDown 将设置为 False。在按钮释放期间,您可以将鼠标移动到另一个位置(例如,移到另一个抓取柄上),然后再次按下按钮以启动另一个移动/调整大小操作。

    每次 MouseUp 事件发生时,程序都会检查是否存在选区矩形。如果存在,则会创建并显示新的平铺。

请注意,您可以通过在源图像上单击鼠标来清除当前选区。

MouseDown 事件处理程序

Private Sub picbox_MouseDown(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs) _
        Handles picbox_SrcImage.MouseDown
    x = e.X : y = e.Y
    isDown = True
    If e.Button = Windows.Forms.MouseButtons.Left AndAlso _
    Not canResize AndAlso Not canMove Then
      sr.Width = 0
      sr.Height = 0
      Me.Invalidate()
    End If
End Sub

MouseUp 事件处理程序

Private Sub picbox_MouseUp (ByVal sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs) _
        Handles picbox_SrcImage.MouseUp
    RectInfo = New Rectangle(sr.Left, sr.Top, sr.Width, sr.Height)
    isDown = False
    canMove = False
    canResize = False
    picbox_SrcImage.Cursor = Cursors.Default
    createTile()
End Sub

MouseMove 事件处理程序...

Private Sub picbox_MouseMove(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs) _
        Handles picbox_SrcImage.MouseMove

    If Not original Is Nothing Then
      If e.X < 0 Or e.X > original.Width _
               Or e.Y < 0 Or e.Y > original.Height Then
        Exit Sub
      End If

      If isDown Then 'if left mouse button is down...
        sldr_SizeH.Value = 100
        sldr_SizeW.Value = 100

        'draw new selection rect...
        If Not canResize AndAlso Not canMove Then
          Dim iLeft As Integer = 0
          Dim iTop As Integer = 0
          Dim iRight As Integer = original.Width
          Dim iBtm As Integer = original.Height
          Try
            If e.X >= iLeft AndAlso e.X <= iRight _
                      AndAlso e.Y >= iTop AndAlso e.Y <= iBtm Then
              rectPoints = New Point(Min(x, e.X), Min(y, e.Y))
              selSize = New Size(Max(x - e.X, e.X - x), Max(y - e.Y, e.Y - y))

              BuildRects() 'build the selection rect and its resize handles
            End If

          Catch ex As Exception
            MsgBox("Error making selection..." & Chr(10) & ex.ToString)
          End Try

          'My.Application.DoEvents()
        End If
        '------------------------------End Draw Rect------------------------------

        'Here's where the CurPos enum is used. The math for resizing
        'the selection changes depending upon which side or corner
        'of the rectangle has been selected

        'resize sel rect...
        If canResize Then
          Select Case curPos
            Case CursorPos.BottomSide
              rectPoints = New Point(RectInfo.Left, Min(e.Y, RectInfo.Top))
              selSize = New Size(RectInfo.Width, Max(e.Y - RectInfo.Top, RectInfo.Top - e.Y))
              BuildRects()

            Case CursorPos.TopSide
              rectPoints = New Point(RectInfo.Left, Min(e.Y, RectInfo.Bottom))
              selSize = New Size(RectInfo.Width, Max(RectInfo.Bottom - e.Y, e.Y - RectInfo.Bottom))
              BuildRects()

            Case CursorPos.LeftSide
              rectPoints = New Point(Min(e.X, RectInfo.Right), RectInfo.Y)
              selSize = New Size(Max(RectInfo.Right - e.X, e.X - RectInfo.Right), RectInfo.Height)
              BuildRects()

            Case CursorPos.RightSide
              rectPoints = New Point(Min(e.X, RectInfo.Left), RectInfo.Top)
              selSize = New Size(Max(e.X - RectInfo.X, RectInfo.X - e.X), RectInfo.Height)
              BuildRects()

            Case CursorPos.BottomRight
              rectPoints = New Point(Min(e.X, RectInfo.Left), Min(e.Y, RectInfo.Top))
              selSize = New Size(Max(e.X - RectInfo.X, RectInfo.X - e.X), _
                        Max(e.Y - RectInfo.Top, RectInfo.Top - e.Y))
              BuildRects()

            Case CursorPos.BottomLeft
              rectPoints = New Point(Min(e.X, RectInfo.Right), Min(e.Y, RectInfo.Top))
              selSize = New Size(Max(RectInfo.Right - e.X, e.X - RectInfo.Right), _
                        Max(e.Y - RectInfo.Top, RectInfo.Top - e.Y))
              BuildRects()

            Case CursorPos.TopLeft
              rectPoints = New Point(Min(e.X, RectInfo.Right), Min(e.Y, RectInfo.Bottom))
              selSize = New Size(Max(RectInfo.Right - e.X, e.X - RectInfo.Right), _
                        Max(RectInfo.Bottom - e.Y, e.Y - RectInfo.Bottom))
              BuildRects()

            Case CursorPos.TopRight
              rectPoints = New Point(Min(e.X, RectInfo.Left), Min(e.Y, RectInfo.Bottom))
              selSize = New Size(Max(e.X - RectInfo.X, RectInfo.X - e.X), _
                        Max(RectInfo.Bottom - e.Y, e.Y - RectInfo.Bottom))
              BuildRects()

          End Select

        End If
        '------------------------end resize sel rect------------------

        'move sel rect...
        If canMove Then
          Dim offsetX As Integer = x - RectInfo.Left
          Dim offsetY As Integer = y - RectInfo.Top
          If (e.X - offsetX) >= 0 AndAlso ((e.X - offsetX) + _
                RectInfo.Width) <= original.Width AndAlso _
                (e.Y - offsetY) >= 0 AndAlso ((e.Y - offsetY) + _
                RectInfo.Height) <= original.Height Then
            rectPoints = New Point(e.X - offsetX, e.Y - offsetY)
            selSize = New Size(RectInfo.Width, RectInfo.Height)
            BuildRects()
          End If
        End If

        '------------------------end move sel rect----------------------

        'if left mouse button is not pressed...
      ElseIf Not isDown Then

        'check to see if mouse is within a grab handle
        For l = 0 To grabHandles.Length - 1
          If IsBetween(e.X, e.Y, grabHandles(l)) Then
            picbox_SrcImage.Cursor = curs(l)
            canResize = True
            canMove = False
            curPos = l
            Exit For
          Else
            picbox_SrcImage.Cursor = Cursors.Default
            canResize = False
            canMove = False
            curPos = CursorPos.NotOnRect
          End If
        Next l

        'if NOT inside a grab handle, check if mouse is inside sel rect
        If Not canResize AndAlso IsBetween(e.X, e.Y, sr) Then
          picbox_SrcImage.Cursor = Cursors.SizeAll
          canMove = True
          canResize = False
          curPos = CursorPos.Inside

        End If

      End If 'isdown 

    End If 'original is nothing

    My.Application.DoEvents()

End Sub

MouseMove 期间重新绘制选区矩形时,会调用 BuildRects() 子程序来用当前矩形更新屏幕。

Private Sub BuildRects()
    ' "sr" is the selection rectangle
    ' the "grabHandles" array as the name implies
    ' contains the resize handles for the selection

    sr = New Rectangle(rectPoints, selSize)

    grabHandles(0) = _
    New Rectangle(sr.Left - (grabSize.Width / 2), _
                  sr.Y - (grabSize.Height / 2), grabSize.Width, _
                  grabSize.Height) 'top left

    grabHandles(1) = _
    New Rectangle((sr.Left + (sr.Width / 2)) - grabSize.Width / 2, _
                  sr.Y - (grabSize.Height / 2), _
                  grabSize.Width, grabSize.Height) 'top

    grabHandles(2) = _
    New Rectangle(sr.Right - (grabSize.Width / 2), _
                  sr.Top - (grabSize.Height / 2), _
                  grabSize.Width, grabSize.Height) 'top right

    grabHandles(3) = _
    New Rectangle(sr.Right - (grabSize.Width / 2), _
                  ((sr.Bottom - sr.Height / 2)) _
                  - grabSize.Height / 2, grabSize.Width, _
                  grabSize.Height) 'right

    grabHandles(4) = _
    New Rectangle(sr.Right - (grabSize.Width / 2), _
                  sr.Bottom - (grabSize.Height / 2), _
                  grabSize.Width, grabSize.Height) 'bottom right

    grabHandles(5) = _
    New Rectangle((sr.Right - (sr.Width / 2)) - _
                  grabSize.Width / 2, sr.Bottom - _
                  (grabSize.Height / 2), grabSize.Width, _
                  grabSize.Height) 'bottom

    grabHandles(6) = _
    New Rectangle(sr.Left - (grabSize.Width / 2), _
                  sr.Bottom - (grabSize.Height / 2), _
                  grabSize.Width, grabSize.Height) 'bottom left

    grabHandles(7) = _
    New Rectangle(sr.Left - (grabSize.Width / 2), _
                  (sr.Bottom - (sr.Height / 2)) _
                  - grabSize.Height / 2, grabSize.Width, _
                  grabSize.Height) 'left

    'if "Update tile during selection" checkbox
    'is checked then create tile while dragging
    'mouse. Otherwise wait until mouse button
    'is released.
    If chk_AutoCreate.Checked Then
      createTile()
    Else
      Me.Invalidate()
    End If
    UpdateUI()
End Sub

当鼠标按钮释放时,会调用 createTile() 子程序来创建新的平铺图像。

Private Sub createTile()

    If sr.Width > 0 AndAlso sr.Height > 0 Then 'if a selection rect is drawn

      Try
        'create image from selection
        Dim flipImg As New Bitmap(sr.Width, sr.Height)
        Dim flipGrph As Graphics = Graphics.FromImage(flipImg)
        Dim destRect As New Rectangle(0, 0, sr.Width, sr.Height)
        Dim srcRect As New Rectangle(sr.Left, sr.Top, sr.Width, sr.Height)
        flipGrph.DrawImage(original, destRect, srcRect, GraphicsUnit.Pixel)

        'create the empty bitmap for drawing the mirrored inner tiles
        'tmp is a temporary bmp used to create the image
        Dim tmp As Bitmap = New Bitmap(flipImg.Width * 2, flipImg.Height * 2)
        'selBMP = New Bitmap(flipImg.Width * 2, flipImg.Height * 2)
        Dim tileG As Graphics = Graphics.FromImage(tmp)


        'draw inner tiles in selBMP...
        'top left...
        tileG.DrawImage(flipImg, 0, 0, flipImg.Width, flipImg.Height)

        'top right
        flipImg.RotateFlip(RotateFlipType.RotateNoneFlipX)
        tileG.DrawImage(flipImg, flipImg.Width, 0, flipImg.Width, flipImg.Height)

        'bottom right
        flipImg.RotateFlip(RotateFlipType.RotateNoneFlipY)
        tileG.DrawImage(flipImg, flipImg.Width, flipImg.Height, _
                        flipImg.Width, flipImg.Height)

        'bottom left
        flipImg.RotateFlip(RotateFlipType.RotateNoneFlipX)
        tileG.DrawImage(flipImg, 0, flipImg.Height, flipImg.Width, flipImg.Height)

        '-------------------------------------------------------
        'rotate the finished tile 90 deg clockwise
        'this is the only flip/rotate that has any meaningful
        'effect. Flipping the first inner tile affects only
        'what is shown at the left of the bkgnd and doesn't
        'affect the rest of the display.
        '--------------------------------------------------------
        If chk_RotateTile90.Checked Then
          tmp.RotateFlip(RotateFlipType.Rotate90FlipNone)
        End If

        'resize
        Dim newW, newH As Integer
        newW = (tmp.Width / 100) * sldr_SizeW.Value
        newH = (tmp.Height / 100) * sldr_SizeH.Value
        selBMP = New Bitmap(tmp, newW, newH)

        picbox_TilePreview.Image = selBMP
        pntPnl.BackgroundImage = selBMP

        Me.Invalidate()
        My.Application.DoEvents()
      Catch ex As Exception
        MsgBox("Error creating tile:" & Chr(10) & ex.ToString, _
               MsgBoxStyle.Exclamation, title)
      End Try

    End If
End Sub

一旦选区矩形和平铺完成,Paint 事件就会由 Me.Invalidate() 触发。请注意,只有矩形会被绘制。由于源图像已被设置为 PictureBox 的背景图像,因此无需重绘它。

Private Sub main_Paint(ByVal sender As Object, ByVal e As _
        System.Windows.Forms.PaintEventArgs) Handles Me.Paint
    Try
      If original IsNot Nothing Then 'if a source image is loaded

        'copy of original to display
        bmp = New Bitmap(original.Width, original.Height)
        g = Graphics.FromImage(bmp)
        'draw the selection rectangle and grab-handles
        If sr.Width > 0 AndAlso sr.Height > 0 Then
          g.FillRectangle(innerBrush, sr)
          g.DrawRectangle(myPen, sr)

          g.FillRectangles(grabBrush, grabHandles)
          g.DrawRectangles(grabPen, grabHandles)

        End If
        picbox_SrcImage.Image = bmp
        g.Dispose()
      End If
      UpdateUI()
    Catch ex As Exception
      MsgBox(ex.ToString)
    End Try
End Sub

调整平铺大小

左窗格中的滑块允许调整平铺的大小。更改会立即反映在滑块上方的平铺预览和(如果选中了“在选择过程中更新平铺”复选框)其下方的页面预览中。否则,图像将在释放鼠标时更新(对于较大的平铺更好)。涉及的数学是基于百分比而不是平铺的实际尺寸,范围从 10% 到 200%。您会在 CreateTile() 子程序的末尾附近找到代码。单击滑块下方的“1:1”按钮会将两者都重置为 100%。

从 BTC 设置墙纸

此版本包含从程序设置桌面墙纸的功能。以下是它的工作原理

在项目属性中,我添加了一个对 Shell32.dll 的引用。

WinAPI 类执行实际工作

Imports System.Runtime.InteropServices

Public Class WinAPI
  <DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Public Shared Function SystemParametersInfo(ByVal uAction As Integer, _
       ByVal uParam As Integer, ByVal lpvParam As String, _
       ByVal fuWinIni As Integer) As Integer
  End Function
  Public Const SPI_SETDESKWALLPAPER As Integer = 20
  Public Const SPIF_SENDCHANGE As Integer = &H2
  Public Const SPIF_UPDATEINIFILE As Integer = &H1&
  Public Const SPIF_SENDWININICHANGE As Integer = &H2&
End Class

在 BTC 窗口中,单击工具栏中的“墙纸”按钮,该按钮形似电脑显示器。首先,BTC 会检查以确保平铺已保存为 .bmp.jpg 格式。这是 Windows 可以接受的唯一格式(至少我的版本是这样)。接下来,它会检查平铺是否有未保存的更改。如果有,系统会提示您在继续之前保存。当您更改平铺时,Boolean 变量 isSaved 会设置为 False,当您保存它时设置为 True。一旦完成,就会创建一个 Shell 对象,用于最小化所有打开的窗口(包括 BTC),以便可以看到桌面。最后,BTC 会打开 dialog_Wallpaper 对话框。

这是 Click 事件处理程序

Private Sub tb_SetWP_Click() Handles tb_SetWP.Click

    If Not tilePath.EndsWith(".jpg") AndAlso _
           Not tilePath.EndsWith(".bmp") Then
      msg = "Image must be saved in either .jpg" & Chr(10)
      msg &= "or .bmp format. These are the only formats" & Chr(10)
      msg &= "Windows recognizes for wallpaper."
      MsgBox(msg, MsgBoxStyle.Information, title)
      Exit Sub
    End If

    If Not isSaved Then
      msg = "The image has unsaved changes. You must" & Chr(10)
      msg &= "first save the image. BE SURE to save it in either .jpg" & Chr(10)
      msg &= "or .bmp format. These are the only formats Windows" & Chr(10)
      msg &= "recognizes for wallpaper."
      MsgBox(msg, MsgBoxStyle.Information, title)
      Exit Sub
    End If

    'hide all open windows before opening wp dialog
    Try
      Dim sh As New Shell
      sh.MinimizeAll()
      dialog_WallPaper.ShowDialog()
      sh = Nothing
    Catch ex As Exception
      MsgBox(ex.ToString, MsgBoxStyle.Exclamation, title)
    End Try
End Sub

墙纸对话框

这是对话框的完整代码列表。其中包含注释以解释其作用。

Imports System.Windows.Forms
Imports System.Drawing
Imports System.IO
Imports Microsoft.Win32

Public Class dialog_WallPaper
  Dim imgPath As String = main.tilePath 'the path to your saved tile
  
  'in the event you change your mind these variables will contain your
  'previous wallpaper info
  Dim oldPath As String 
  Dim oldStyle As String
  Dim oldTile As String

   'the dialog's load event
  Private Sub dialog_WallPaper_Load(ByVal sender As Object, _
          ByVal e As System.EventArgs) Handles Me.Load
    'minimize the main window
    main.WindowState = FormWindowState.Minimized

    'get your current wallpaper settings from the registry
    oldPath = My.Computer.Registry.GetValue(_
       "HKEY_CURRENT_USER\Control Panel\Desktop", "WallPaper", Nothing)
    oldStyle = My.Computer.Registry.GetValue(_
       "HKEY_CURRENT_USER\Control Panel\Desktop", "WallpaperStyle", Nothing)
    oldTile = My.Computer.Registry.GetValue(_
       "HKEY_CURRENT_USER\Control Panel\Desktop", "TileWallpaper", Nothing)
    
    'set the wallpaper to the new tile
    SetWallpaper("1", "1")

  End Sub

  '"Accept" button - close the dialog and leave the new tile as your WP
  Private Sub OK_Button_Click(ByVal sender As System.Object, _
          ByVal e As System.EventArgs) Handles OK_Button.Click
    main.WindowState = FormWindowState.Maximized
    Me.DialogResult = System.Windows.Forms.DialogResult.OK
    Me.Close()
  End Sub

  '"Decline" button - close the dialog and revert to your original WP
  Private Sub Cancel_Button_Click(ByVal sender As System.Object, _
          ByVal e As System.EventArgs) Handles Cancel_Button.Click

    'set wp back to previous image and settings before closing
    My.Computer.Registry.SetValue("HKEY_CURRENT_USER\Control Panel\Desktop", _
                                  "WallPaper", oldPath)
    My.Computer.Registry.SetValue("HKEY_CURRENT_USER\Control Panel\Desktop", _
                                  "WallpaperStyle", oldStyle)
    My.Computer.Registry.SetValue("HKEY_CURRENT_USER\Control Panel\Desktop", _
                                  "TileWallpaper", oldTile)
    WinAPI.SystemParametersInfo(WinAPI.SPI_SETDESKWALLPAPER, 0, oldPath, _
           WinAPI.SPIF_UPDATEINIFILE Or WinAPI.SPIF_SENDWININICHANGE)
    main.WindowState = FormWindowState.Maximized
    Me.DialogResult = System.Windows.Forms.DialogResult.Cancel
    Me.Close()
  End Sub

   'sets the wallpaper to the new tile
   'writes the 3 necessary values to the registry
  Private Sub SetWallpaper(ByVal styleNum As String, ByVal tile As String)
    My.Computer.Registry.SetValue("HKEY_CURRENT_USER\Control Panel\Desktop", _
                                  "WallpaperStyle", styleNum)
    My.Computer.Registry.SetValue("HKEY_CURRENT_USER\Control Panel\Desktop", _
                                  "TileWallpaper", tile)
    My.Computer.Registry.SetValue("HKEY_CURRENT_USER\Control Panel\Desktop", _
                                  "WallPaper", main.tilePath)
    WinAPI.SystemParametersInfo(WinAPI.SPI_SETDESKWALLPAPER, 0, main.tilePath, _
           WinAPI.SPIF_UPDATEINIFILE Or WinAPI.SPIF_SENDWININICHANGE)
  End Sub
End Class

帮助文件

我放进去了一个纯粹为最终用户编写的帮助文件,可以从工具栏打开。HTML 包含在 My.Resources.help 中。打开的窗体包含一个 WebBrowser 控件,并且 Load 事件包含设置其 DocumentText 属性为资源的代码。

关注点

如果您想查看用 BTC 创建的平铺示例,请访问此页面。向下滚动到幻灯片放映并单击图像。页面背景将在选择每个平铺时显示。您需要在浏览器中启用 JavaScript。

在编写 BTC 的过程中,我学到的一件事是如何实现选区矩形。用鼠标绘制矩形很容易,但一个功能正常的选区矩形则更复杂一些。BTC 的选区矩形不包括在将选区拖动到显示区域之外时滚动源图像的功能。考虑到平铺通常都很小,我认为这不是必需的,尽管我以后可能会添加它。

在最近的这个版本中,我还学到了一些关于处理注册表和 Shell 的知识。我过去一直避免这两者,所以对我来说这是新的。如果您看到我在这方面应该做得不同的地方,请随时告诉我。

历史

  • 首次发布:2011 年 7 月。
  • 第二次发布:2011 年 8 月上传。
  • 第三次发布:2011 年 8 月 29 日上传。
  • 第四次发布(bug 修复):2011 年 9 月 2 日上传。
© . All rights reserved.