关于C#绘制qq好友列表控件Word下载.docx
《关于C#绘制qq好友列表控件Word下载.docx》由会员分享,可在线阅读,更多相关《关于C#绘制qq好友列表控件Word下载.docx(6页珍藏版)》请在冰点文库上搜索。
![关于C#绘制qq好友列表控件Word下载.docx](https://file1.bingdoc.com/fileroot1/2023-4/29/6bc68317-f223-4b9f-b581-c9a0738dcb49/6bc68317-f223-4b9f-b581-c9a0738dcb491.gif)
上一个效果图
左边那张图貌似忘了展示一个功能源码下载
个人感觉注释还是比较详细
貌似这是第一次直接继承Control写控件以前都是UserControl对于写控件真没啥经验途中遇到各种问题各种蛋疼所以代码有啥不对的地方多包涵
--!
、、其实这里面有一个问题就是TypeConverter哪里不知道咋搞不不用转换器的话在窗体设计时候向控件添加项的时候
调用默认构造器然后其余属性挨个赋值所以自动生成的代码非常臃肿最开始写了TypeConverter但是没有
果断窗体设计时自己都不生成代码了经测试转换器内的代码压根就没有执行后来不知道咋搞的貌似有点反应了不过编译就报错说属性
代码生成失败无法将XXXX转换为XXXX.InstanceDescriptor我就郁闷了然后XX说把程序集版本改成
100*就可以试了一下确实没有报那个错了不过又有其他问题......
XXXXXXXXXX........!
@@¥%……&
amp;
U总之p话不多说由于没有这方面经验希望哪位会的人士给予一下指点
到底要怎么才能让那个自己写的TypeConverter类正确执行在窗体设计时候自动生成的代码看上去和谐虽然说这东西一般都是
程序运行出来动态加载列表的但是在窗体设计的时候手动添加项的时候自动生成的代码真的很不和谐虽然一般情况下也不去看
但感觉已经不爽了...好吧上面的都是p话只是希望有人能解决一下我的问题现在来说说我做这个控件的时候的感想吧在做之前我在网上参考过一些
也下载过两个不过感觉真不咋滴(我指的是我下载的那两个不代表全部)运行出来亲一色的全是企鹅头这也就算了还亲一色的只有在线状态
XXXX...!
@#¥我总结出来的就是只以做的像为目的压根就没有考虑过投入使用的问题所以我在制作的时候考虑了要投入使用的问题(-
-!
、其实是压根本来就要用所以才考虑到的)比如图片上每一个子项拥有一个布尔值来决定是否闪烁
还有不同的在线状态而且还是按照状态进行排序的......现在来说思路我在里面分Item和SubItemItem就是看到的分组而SubItem就是分组下的子项所以我就搞了两个类ChatListItem和ChatListSubItem这两个类
ChatListItem被控件包含而ChatListSubItem被ChatListItem包含所以大概的代码就是
[csharp]viewplaincopypublicclassChatListBox:
Control{XXXXXXXpublicChatListItem[]Items{get;
set;
}XXXXXXX.......}publicclassChatListItem{XXXXXXXpublicChatListSubItem[]SubItems{get;
}XXXXXXX.......}publicclassChatListSubItem{publicintID{get;
}publicstringNicName{get;
}..................}当然真实情况下不是用的数组这里只是为了方便这样就有了一个层次关系然后就是绘制的问题
绘制的时候在OnPaint中循环Items
[csharp]viewplaincopyprotectedoverridevoidOnPaint(PaintEventArgse){for(inti=0;
i&
lt;
Items.Count;
i++){//循环绘制每一个ItemDrawItem(items[i]);
//绘制Itemif(items[i].IsOpen){//如果Item的列表是展开的那么还得绘制他的子项for(intj=0;
j&
Items[i].SubItem.Count;
j++){DrawSubItems(Items[i].SubItems[j]);
//绘制子项}}}}大概也就这样不过里面还要计算区域不能每个Item或者SubItem绘制的时候都重叠到一起啊所以代码改一个就成这个样子
[csharp]viewplaincopyprotectedoverridevoidOnPaint(PaintEventArgse){RectanglerectItem=newRectangle(0,0,this.Width,20);
//假设每个item的高度是20这是第一个的RectanglerectSubItem=newRectangle(0,21,this.Width,50)//假设每个SubItem高50这个是第一个的21是在Item标题下移动一格像素的for(inti=0;
i++){//循环绘制每一个ItemDrawItem(items[i],rectItem);
//绘制Item增加一个参数if(items[i].IsOpen){//如果Item的列表是展开的那么还得绘制他的子项rectSubItem.Y=rectItem.Bottom+1;
//这个是展开的第一个的子项那么他的位置就该是Item标题的下面一个像素for(intj=0;
j++){DrawSubItems(Items[i].SubItems[j],rectSubItem);
//绘制子项增加一个参数rectSubItem.Y=rectSubItem.Bottom+1;
//计算下一个子项的区域}rectItem.Y=rectSubItem.Bottom+1;
//子项绘制完那么该计算下一个Item的坐标}else{rectItem.Y=rectItem.Bottom;
//如果没有展开那么下一个item的坐标就是上一个Item的下面一个像素}}}这样绘制上基本没有啥问题了不过这也只是绘制出来了而已你还得在上面操作比如鼠标点击一个子项你用什么来判断鼠标点击了一个子项?
所以还得把每个Item和SubItem绘制在什么地方记录下来到时候用鼠标坐标去比对
所以不管是ChatListItem还是ChatListSubItem都要在他们里面加一个Rectangle类型的属性每绘制一个Item或者SubItem的时候就把那块区域赋值过去
到时候就用Rectangle.Contains(Point)来判断鼠标到底在那一块区域内
所以就有了我代码中的
[csharp]viewplaincopyfor(inti=0,lenItem=items.Count;
i&
lenItem;
i++){DrawItem(g,items[i],rectItem,sb);
//绘制列表项if(items[i].IsOpen){//如果列表项展开绘制子项rectSubItem.Y=rectItem.Bottom+1;
for(intj=0,lenSubItem=items[i].SubItems.Count;
j&
lenSubItem;
j++){DrawSubItem(g,items[i].SubItems[j],refrectSubItem,sb);
//绘制子项rectSubItem.Y=rectSubItem.Bottom+1;
//计算下一个子项的区域rectSubItem.Height=(int)iconSizeMode;
//大图标小图标模式的高度}rectItem.Height=rectSubItem.Bottom-rectItem.Top-(int)iconSizeMode-1;
//这里之所以给rectItem高度重新复制是因为SubItem是包含在Item内部的//所以rectItem的高度就是他标题的高度加上他内部所有子项的高度把他所有的子项都圈起来}items[i].Bounds=newRectangle(rectItem.Location,rectItem.Size);
rectItem.Y=rectItem.Bottom+1;
//计算下一个列表项区域rectItem.Height=25;
}每个item和subitem有了一个自己的区域后那么要判断鼠标是否落在他们某一个区域上就方便了
[csharp]viewplaincopyfor(inti=0;
i++){if(items[i].Bounds.Contains(MousePoint)){//如果鼠标位置在某一个item上if(items[i].IsOpen){//判断该Item是否为展开的for(intj=0;
j++){if(Items[i].SubItems[j].Bounds.Contains(MousePoint)){XXXXXXXX....;
return;
//鼠标位置只可能在某一个Item或者SubItem上所以找到一个后处理完事情直接return}}//如果该项又是展开的循环完子项却又没有找到符合条件的子项那么就极有可能鼠标位置在标题上(因为每个Item或者SubItem有一像素间隔)if(newRectangle(0,Items[i].Bounds.Top,this.Height,20).Contains(MousePoint)){........;
}}}}主要思路也就差不多是这些怎么绘制绘制后取得相应区域剩下的就是一些细节上的功能还有滚动条
对对对滚动条所以在绘制的时候要根据滚动条进行y坐标的一个偏移
g.TranslateTransform(0,-chatVScroll.Value);
//根据滚动条的值设置坐标偏移
这个是我控件中完整的OnPaint
[csharp]viewplaincopyprotectedoverridevoidOnPaint(PaintEventArgse){Graphicsg=e.Graphics;
g.TranslateTransform(0,-chatVScroll.Value);
//根据滚动条的值设置坐标偏移RectanglerectItem=newRectangle(0,1,this.Width,25);
//列表项区域RectanglerectSubItem=newRectangle(0,26,this.Width,(int)iconSizeMode);
//子项区域SolidBrushsb=newSolidBrush(this.itemColor);
try{for(inti=0,lenItem=items.Count;
}rectItem.Height=rectSubItem.Bottom-rectItem.Top-(int)iconSizeMode-1;
}items[i].Bounds=newRectangle(rectItem.Location,rectItem.Size);
}g.ResetTransform();
//重置坐标系chatVScroll.VirtualHeight=rectItem.Bottom-26;
//绘制完成计算虚拟高度决定是否绘制滚动条if(chatVScroll.ShouldBeDraw)//是否绘制滚动条chatVScroll.ReDrawScroll(g);
}finally{sb.Dispose();
}base.OnPaint(e);
}也就是在绘制控件的时候坐标就不是00左上角位置开始绘制了而是一个0Y的一个虚拟的坐标了Y的值是由滚动条决定的
所以在判断鼠标落的区域的时候也有点变化了Rectangle.Contains(MousePont.Y+滚动条的值);
还有就是离线离开状态什么的这些都是枚举值然后ChatListSubItem实现一个排序接口按照他们的顺序排序就可以了
其实枚举值也就相当于int值所以在定义枚举类的时候把顺序搞定对SubItem排序的时候按照一个升序排序就搞定了这个和普通的排序没啥区别
还有就是头像闪动的问题在SubItem里面有一个布尔值来决定是否闪动不仅是它它所属的Item也的有关联
(在定义ChatListSubItem类的时候里面有个ChatListItem类型的一个Owner属性来表示该SubItem是属于哪个Item
的Item同理有一个表示属于哪一个ChatListBox控件的因为在这些类中的一些操作要引发控件的重绘所以要把他们一级一级关联)
因为列表关闭的时候头像没有办法闪动所以只有列表标题闪动所以在ChatListItem类里面还定义了一个计数器
保存在它列表下的子项闪动的个数重绘的时候如果列表关闭计数器不为0那么闪动列表标题如果列表打开那么闪动头像
当然在给ChatListSubItem的那个决定是否闪动的属性赋值的时候响应的也要给所属的Item的计数器进行操作
对于这个控件我暂时能想到的就这些控件自身功能然后就是给用户的接口了
我里面只定义了上个自定义事件因为想了一下在实际应用中也就只有这三个有用
双击一个列表中的子项的时候(QQ的话就弹出聊天对话框了)【DoubleClickSubItem】
鼠标移动到一个子项的头像上面(QQ这个时候坐标出现一个小窗体来加载该用户的一些资料)【MouseEnterHead】
鼠标离开该子项的头像(QQ的画那个小窗体就消失了)【MouseLeaveHead】不过我还是希望有谁能解决一下那个TypeConverter的问题虽然感觉没啥用处不过心理上却感觉不爽
源码连接等资源通过在上传上来吧!
、、、、
分享到:
上一篇:
C#制作内存修改器