图形窗体问题(异形窗体)

roger_xiong 2004-03-25 08:05:00
小弟欲设计一个类似下面这种UI的程序:

http://www.chinaui.com/bbs/UploadFile/2004-3/2004310191028913.jpg

这种窗体的方法,我目前采用的方法是:

用无边框的窗体 + picturebox

然后用循环逐个判断欲透明的色块,但是我发现这样做的话,窗体越大,消耗的时间就越多,有点吃不消,敢问诸位有没有更好的方法,领教~
...全文
119 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
roger_xiong 2004-04-03
  • 打赏
  • 举报
回复
'*************************************************************************
'**
'** 参数说明:
'**
'** hForm,目标窗体;transColor,透明色(默认为0,0位置的颜色)
'** AutoSizehForm,自动调整目标窗体大小
'**
'** 修改:熊俊 sand@yeu-cfans.org 大家多多交流!
'**
'*************************************************************************

Public Sub CreateGUI(hForm As Form, Optional transColor As Byte = vbNull, Optional AutoSizehForm As Boolean = True)
Dim hbm As Long
Dim bm As BITMAP
Dim Wid As Long
Dim Hgt As Long
Dim bmByte() As Byte

Dim X As Long
Dim Y As Long
Dim Rgn1 As Long
Dim Rgn2 As Long
Dim SPos As Long
Dim EPos As Long
Dim xoff As Long
Dim yoff As Long

If hForm.Picture = 0 Then Exit Sub

'获取窗体背景图片尺寸
hbm = hForm.Picture
GetObjectAPI hbm, Len(bm), bm
Wid = bm.bmWidth
Hgt = bm.bmHeight

'默认允许自动调整目标窗体大小
'15 Pixel = 1 Twip
If AutoSizehForm = True Then
hForm.Width = Wid * 15
hForm.Height = Hgt * 15
End If

ReDim bmByte(1 To Wid, 1 To Hgt)
GetBitmapBits hbm, Wid * Hgt, bmByte(1, 1) '获取图像数组


'如果没有传入transColor参数,则用第一个像素作为透明色

If transColor = vbNull Then transColor = bmByte(1, 1)

Rgn1 = CreateRectRgn(0, 0, 0, 0)

For Y = 1 To Hgt '逐行扫描
X = 0
Do
X = X + 1

While (bmByte(X, Y) = transColor) And (X < Wid)
X = X + 1 '跳过是透明色的点
Wend
SPos = X
While (bmByte(X, Y) <> transColor) And (X < Wid)
X = X + 1 '跳过不是透明色的点
Wend
EPos = X - 1

' 这一段是合并区域
If SPos <= EPos Then
Rgn2 = CreateRectRgn(SPos - 1, Y - 1, EPos, Y)
CombineRgn Rgn1, Rgn1, Rgn2, RGN_OR
DeleteObject Rgn2
End If
Loop Until X >= Wid
Next Y

SetWindowRgn hForm.hWnd, Rgn1, True '设定窗体形状区域
DeleteObject Rgn1

End Sub


roger_xiong 2004-04-03
  • 打赏
  • 举报
回复
Private Declare Function GetObjectAPI Lib "gdi32" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, lpObject As Any) As Long
'VB声明
'Declare Function GetObjectAPI& Lib "gdi32" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, lpObject As Any)
'说 明:
' 取得对指定对象进行说明的一个结构.windows手册建议用GetObject这个名字来引用该函数.GetObjectAPI在vb中用于避免与GetObject关键字混淆
'返回值:
' Long,如lpObject设为零(用ByVal As Long在这种情况下传递一个NULL参数),则必须设置缓冲区的长度。如执行成功,返回载入结构内部的实际字节数;如失败,返回零值
'参数表:
'参数 类型及说明
'hObject Long 画笔、刷子、字体、位图或调色板等对象的句柄
'nCount Long 欲取回的字节数。通常是由lpObject定义的那个结构的长度
'lpObject 任何类型,用于容纳对象数据的结构。
' 针对画笔,通常是一个LOGPEN结构;
' 针对扩展画笔,通常是EXTLOGPEN;
' 针对字体是LOGBRUSH;
' 针对位图是BITMAP;
' 针对DIBSection位图是DIBSECTION;
' 针对调色板,应指向一个整型变量,代表调色板中的条目数量

Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
'VB声明
'Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
'说 明:
' 用这个函数删除GDI对象,比如画笔、刷子、字体、位图、区域以及调色板等等。对象使用的所有系统资源都会被释放
'返回值:
' Long,非零表示成功,零表示失败
'参数表
'参数 类型及说明
'hObject Long 一个GDI对象的句柄
'注 解:
' 不要删除一个已选入设备场景的画笔、刷子或位图。如删除以位图为基础的阴影(图案)刷子,位图不会由这个函数删除——只有刷子被删掉

Private Declare Function SetWindowRgn Lib "user32" (ByVal hWnd As Long, ByVal hRgn As Long, ByVal bRedraw As Boolean) As Long
'VB声明
'Declare Function SetWindowRgn Lib "user32" (ByVal hWnd As Long, ByVal hRgn As Long, ByVal bRedraw As Boolean) As Long
'说 明:
' 这是那些很难有人注意到的对编程者来说是个巨大的宝藏的隐含的API函数中的一个?本函数允许您改变窗口的区域?
' 通常所有窗口都是矩形的——窗口一旦存在就含有一个矩形区域。本函数允许您放弃该区域。这意味着您可以创建圆的、星形的窗口,也可以将它分为两个或许多部分——实际上可以是任何形状
'返回值:
' Long,执行成功为非零值,失败为0
'参数表
'参数 类型及说明
'hWnd Long 将设置其区域的窗口
'hRgn Long 将设置的区域的句柄,一旦设置了该区域,就不能使用或修改该区域句柄,也不要删除它
'bRedraw Boolean 若为TRUE,则立即重画窗口
'注 解:
' 为区域指定的所有坐标都以窗口坐标(不是客户坐标)表示,它们以整个窗口(包括标题栏和边框)的左上角为起点

Private Declare Function CreateRectRgn Lib "gdi32" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
'VB声明
'Declare Function CreateRectRgn Lib "gdi32" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
'说 明:
' 创建一个由点X1,Y1和X2,Y2描述的矩形区域
'返回值:
' Long,执行成功为区域句柄,失败则为零
'参数表
'参数 类型及说明
'X1,Y1 Long 矩形左上角X,Y坐标
'X2,Y2 Long 矩形右下角X,Y坐标
'注 解:
' 不用时一定要用DeleteObject函数删除该区域
' 这个矩形的下边和右边不包含在区域之内

Private Declare Function CombineRgn Lib "gdi32" (ByVal hDestRgn As Long, ByVal hSrcRgn1 As Long, ByVal hSrcRgn2 As Long, ByVal nCombineMode As Long) As Long
'VB声明
'Declare Function CombineRgn Lib "gdi32" (ByVal hDestRgn As Long, ByVal hSrcRgn1 As Long, ByVal hSrcRgn2 As Long, ByVal nCombineMode As Long) As Long
'说 明:
' 将两个区域组合为一个新区域
'返回值:
' Long,下列常数之一:
' COMPLEXREGION: 区域有互相交叠的边界
' SIMPLEREGION: 区域边界没有互相交叠
' NULLREGION: 区域为空
' ERRORAPI: 不能创建组合区域
'参数表
'参数 类型及说明
'hDestRgn Long 包含组合结果的区域句柄
'hSrcRgn1 Long 源区域1
'hSrcRgn2 Long 源区域2
'nCombineMode Long,组合两区域的方法。可设为下述常数
'RGN_AND hDestRgn被设置为两个源区域的交集
'RGN_COPY hDestRgn被设置为hSrcRgn1的拷贝
'RGN_DIFF hDestRgn被设置为hSrcRgn1中与hSrcRgn2不相交的部分
'RGN_OR hDestRgn被设置为两个区域的并集
'RGN_XOR hDestRgn被设置为除两个源区域OR之外的部分

Private Declare Function GetBitmapBits Lib "gdi32" (ByVal hBitmap As Long, ByVal dwCount As Long, lpBits As Any) As Long
'VB声明
'Declare Function GetBitmapBits Lib "gdi32" (ByVal hBitmap As Long, ByVal dwCount As Long, lpBits As Any) As Long
'说 明:
' 将来自位图的二进制位复制到一个缓冲区
'返回值:
' Long,如执行成功,返回位图中的字节数量;零表示失败。会设置GetLastError
'参数表
'参数 类型及说明
'hBitmap Long 位图的句柄
'dwCount Long 欲复制的字节数。如设为零,表示取得位图中的字节数
'lpBits Any 指向容纳位图位的一个缓冲区的指针。注意事先将缓冲区至少初始化成dwCount个字节
'注 解:
' 虽然这个函数能正常工作,但强烈建议使用与设备无关的位图(GetDIBits)

Type BITMAP '14 bytes
bmType As Long
bmWidth As Long
bmHeight As Long
bmWidthBytes As Long
bmPlanes As Integer
bmBitsPixel As Integer
bmBits As Long
End Type
Const RGN_OR = 2

Public Declare Function ReleaseCapture Lib "user32" () As Long
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Const HTCAPTION = 2
Public Const WM_NCLBUTTONDOWN = &HA1

Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Public Const GWL_STYLE = (-16)
Public Const WS_SYSMENU = &H80000

roger_xiong 2004-03-26
  • 打赏
  • 举报
回复
非常谢谢 hisofty(瘦马) ,你给我的连接我试过了,确实比我的快了很多。

不过我试了试,程序中是用窗体的picture来存放图片的,应该可以支持更多的颜色啊。

另外谢谢 lijun84(李俊俊) ,不过vb.net的方法我们暂时还不讨论啊,呵呵,.net里面本身就有一个属性可以设置透明的,比较简单~ 不过还是谢谢大家。

我再测测程序,之后就结贴。
sakurako 2004-03-26
  • 打赏
  • 举报
回复
如果你的程序大小不会改变的话,可以用
CreatePolygonRgn,SetWindowRgn

另外,网上有很多不规则窗口生成器,可以创建基于图片的窗口,不妨一试
wolftop 2004-03-26
  • 打赏
  • 举报
回复
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace WindowsApplication1
{
/// <summary>
/// Form1 µÄժҪ˵Ã÷¡£
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private Point mouseOffset;
private bool isMouseDown = false;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.RichTextBox richTextBox1;

/// <summary>
/// ±ØÐèµÄÉè¼ÆÆ÷±äÁ¿¡£
/// </summary>
private System.ComponentModel.Container components = null;

public Form1()
{
//
// Windows ´°ÌåÉè¼ÆÆ÷Ö§³ÖËù±ØÐèµÄ
//
InitializeComponent();

//
// TODO: ÔÚ InitializeComponent µ÷ÓúóÌí¼ÓÈκι¹Ô캯Êý´úÂë
//
}

/// <summary>
/// ÇåÀíËùÓÐÕýÔÚʹÓõÄ×ÊÔ´¡£
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows ´°ÌåÉè¼ÆÆ÷Éú³ÉµÄ´úÂë
/// <summary>
/// Éè¼ÆÆ÷Ö§³ÖËùÐèµÄ·½·¨ - ²»ÒªÊ¹ÓôúÂë±à¼­Æ÷ÐÞ¸Ä
/// ´Ë·½·¨µÄÄÚÈÝ¡£
/// </summary>
private void InitializeComponent()
{
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Form1));
this.button1 = new System.Windows.Forms.Button();
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(360, 56);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(16, 16);
this.button1.TabIndex = 0;
this.button1.Text = "¡Á";
this.button1.Click += new System.EventHandler(this.button1_Click_1);
//
// richTextBox1
//
this.richTextBox1.BackColor = System.Drawing.Color.Yellow;
this.richTextBox1.Location = new System.Drawing.Point(80, 112);
this.richTextBox1.Name = "richTextBox1";
this.richTextBox1.Size = new System.Drawing.Size(176, 96);
this.richTextBox1.TabIndex = 2;
this.richTextBox1.Text = "ÇáËÉ´´½¨²»¹æÔò´°Ìå\n\n×÷Õߣºlzhm\nÓʼþ£ºlzhm@lzhm.net\nÖ÷Ò³£ºhttp://lzhm.net";
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
this.ClientSize = new System.Drawing.Size(400, 300);
this.Controls.Add(this.richTextBox1);
this.Controls.Add(this.button1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Name = "Form1";
this.Text = "Form1";
this.TransparencyKey = System.Drawing.Color.Yellow;
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown);
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseUp);
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseMove);
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// Ó¦ÓóÌÐòµÄÖ÷Èë¿Úµã¡£
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}

private void button1_Click(object sender, System.EventArgs e)
{
this.Close();
}

private void Form1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
int xOffset;
int yOffset;

if (e.Button == MouseButtons.Left)
{
xOffset = -e.X - SystemInformation.FrameBorderSize.Width;
yOffset = -e.Y - SystemInformation.CaptionHeight - SystemInformation.FrameBorderSize.Height;
mouseOffset = new Point(xOffset, yOffset);
isMouseDown = true;
}
}

private void Form1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (isMouseDown)
{
Point mousePos = Control.MousePosition;
mousePos.Offset(mouseOffset.X, mouseOffset.Y);
Location = mousePos;
}
}

private void Form1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
// Changes the isMouseDown field so that the form does
// not move unless the user is pressing the left mouse button.
if (e.Button == MouseButtons.Left)
{
isMouseDown = false;
}

}

private void button1_Click_1(object sender, System.EventArgs e)
{
this.Close();
}
}
}
hisofty 2004-03-26
  • 打赏
  • 举报
回复
用API制作图形窗体
http://www.csdn.net/Develop/article/22/22589.shtm

电脑爱好者上介绍的方法就是这种,我也看过
这比楼上提供的逐点扫描快多了
不过,这个只支持十六色,即gif图片
修改一下代码就可以支持jpg和bmp了
fuanwei 2004-03-26
  • 打赏
  • 举报
回复
用ACTIVESKIN控件做,简单点
roger_xiong 2004-03-26
  • 打赏
  • 举报
回复
Option Explicit '函数声明
Private Declare Function GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long) As Long
Private Declare Function SetWindowRgn Lib "user32" (ByVal hwnd As Long, ByVal hRgn As Long, ByVal bRedraw As Boolean) As Long
Private Declare Function CreateRectRgn Lib "gdi32" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function CombineRgn Lib "gdi32" (ByVal hDestRgn As Long, ByVal hSrcRgn1 As Long, ByVal hSrcRgn2 As Long, ByVal nCombineMode As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Const RGN_OR = 2

Private Sub CreateGUI(PictureBox As PictureBox, FormName As Form)
Dim Fullr, myColor, crn, hdc As Long
Dim mystart, mybool As Boolean
Dim i, j, linex As Integer

PictureBox.Left = 0
PictureBox.Top = 0

Me.Width = PictureBox.Width '设置窗体宽度等于图形宽度
Me.Height = PictureBox.Height '设置窗体宽度等于图形宽度

PictureBox.ScaleMode = vbPixels '设置PictureBox度量单位为像素
PictureBox.AutoRedraw = True '设置PictureBox自动重绘有效
PictureBox.AutoSize = True '设置PictureBox自动调整大小
PictureBox.BorderStyle = vbBSNone '设置PictureBox的边框样式

hdc = PictureBox.hdc
mystart = True
mybool = False

myColor = GetPixel(hdc, 0, 0) '获取PictureBox指定像素的rgb值

For j = 0 To PictureBox.ScaleHeight - 1
For i = 0 To PictureBox.ScaleWidth - 1
If GetPixel(hdc, i, j) = myColor Then '透明像素
If mybool Then
mybool = False
crn = CreateRectRgn(linex, j, i, j + 1) '创建矩形区域
If mystart Then
Fullr = crn
mystart = False
Else
CombineRgn Fullr, Fullr, crn, RGN_OR '合并区域
DeleteObject crn '删除透明区域
End If
End If
Else '非透明像素
If mybool = False Then
mybool = True
linex = i
End If
End If
Next
Next
SetWindowRgn FormName.hwnd, Fullr, True '设置窗体区域
End Sub

Private Sub Form_Load()
'生成不规则窗体
Call CreateGUI(Picture1, Me)
End Sub

------------------------------------
------------------------------------
这是我原来的方法,如果需要透明的区域越多,程序速度就越慢。

没有用到 createpolygonrgn ,好像比较土一点。我试试用 createpolygonrgn 的方法吧。

好了的话,我还会把代码贴上来的。请大家指教。

roger_xiong 2004-03-25
  • 打赏
  • 举报
回复
晕倒,找了很久只找到了电脑爱好者2001年合订本的下册,晕倒。
roger_xiong 2004-03-25
  • 打赏
  • 举报
回复
呵呵,大家讨论一下,看看能不能得出一个比较好的办法,大家都试一试吧。
kmzs 2004-03-25
  • 打赏
  • 举报
回复
谁做个给我。。。
online 2004-03-25
  • 打赏
  • 举报
回复
参考一下
http://www.ourfly.com/bbs/dispbbs.asp?boardID=5&ID=153
rainstormmaster 2004-03-25
  • 打赏
  • 举报
回复
//另外,如果不用activeskin之类的控件大家用什么办法做图形按钮呢,就像我上面的图片一样的椭圆形按钮?(不用image之类的控件,这样太消耗内存了,而且不容易实现快捷键。)难道只有自己写控件?

自绘

参考:
http://www.fantasiasoft.net/Zyl910/zSkinButton.zip
ColdMooon 2004-03-25
  • 打赏
  • 举报
回复
如果要用一个固定的图形做窗体的话,可以把边界点存到一个文件里,再用createpolygonrgn,setwindowrgn.
rainstormmaster 2004-03-25
  • 打赏
  • 举报
回复
逐点扫描肯定要慢上很多,你应该考虑用bitmap或dib(既先把图片的数据存储到一个数组里,再扫描数组)

或者你用win2000以上的话,用一个api就搞定了,不过为了保证你的程序在win98下也有用,还是建议用扫描数组的方法

电脑爱好者杂志在2001年第1期上介绍了这种方法,你可以找来看看
roger_xiong 2004-03-25
  • 打赏
  • 举报
回复
另外,如果不用activeskin之类的控件大家用什么办法做图形按钮呢,就像我上面的图片一样的椭圆形按钮?(不用image之类的控件,这样太消耗内存了,而且不容易实现快捷键。)难道只有自己写控件?

大家怎么做的呢?

1,486

社区成员

发帖
与我相关
我的任务
社区描述
VB API
社区管理员
  • API
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧