C# 高仿腾讯QQ (实现QQ好友列表-基于ListBox的绘法)

    C#实现QQ好友列表,一直是我网上百搜却不得其解的控件,几乎找不到相关的信息,而找到的也不是我想要的那种。不过,曾在CSDN上看过到过网上一位大牛用C++实现QQ好友列表这种效果,当然,对于我这个C++基本没学过的小程序员来说,读他的代码却有点像看天书.....不过还好,C++代码看不懂,但从他的文章中却给我了一个实现QQ好友列表的思路-基于ListBox绘法



    之前曾想过用TreeViewListView来绘,但归根结底还是因为技术不行而流产,因为找不到合适的方法来重写。
    
    ListBox呢,本身有几个好优势
  
    1、可以用OnMeasureItem方法来实现内部子项的不等高,而我们正好可以利用这点来区分我们QQ好友列表,好友分类与好友之间的区别,不过也有缺陷......

    2、就是不能像QQ好友列表那样分类可以收缩、展开,所以我就用了一个折中的办法,在分类展开时把好友用ListBox的Add()追加与Insert()插入方法而添加进来,反之刚用Remove()RemoveAt()方法,把好友移出去....

    好了,思路有了方法也有了,那么唯一的麻烦就是QQ的好友列表他有QQ分类与QQ好友之分,而QQ好友项内又含有头像、网名、备注名、说明、是否开通邮箱、是否带视频、是否开通手机QQ等(其实我们做时没必要这些功能,但在这里还是说一下,因为我也只是为大家提供一种参考思路)

    3、ListBox本身呢它是没有这些项的,它只能添加string字符串,但为了达到我们想要的效果,所以我的方法是,扩展ListBox的Itme的项,让他的功能强大起来,当然这个扩展方法是我在CSwindow程序之窗上看到某位大牛实现带图标的ListBox控件而得来的,这个网站很不错,如果大家想学自定义控件的方法可以去《CSwindow程序之窗》上看看,相信你会收获很多.....

    好了解决这几个问题的方法、思想如果弄清了,我们就可以开始敲代码了......


如:下面代码做的QQ好友列表并不是完整版,完整版可看这个<演示案例>在这里只是为大家演示下,如果有兴趣可以下载下面的源码对比着当做参考一下,如果有不明白可以进 NET技术交流1群57218890(已满) NET技术交流2群57219423 群,里面热心的人很多,群共享学习资源也很多,呵呵废话不说,下面贴源

QQListBox.cs文件

 
//作者:阿龙(Along)
//QQ号:646494711
//QQ群1:57218890
//QQ群2:57219423
//网站:http://www.8timer.com
//博客:http://www.cnblogs.com/Along729/
//声明:未经作者许可,任何人不得发布出售该源码,请尊重别人的劳动成果,谢谢大家支持

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using System.Xml;

namespace QQListBoxDemo
{
/// <summary>
/// Description of QQListBox.
/// </summary>
public partial class QQListBox : ListBox
{
public QQListBoxItem mouseitem;
public string _xmlpath="QQdate.xml";
private QQListBoxItemCollection _items;

public QQListBox()
:
base()
{
_items
= new QQListBoxItemCollection(this);
base.DrawMode = DrawMode.OwnerDrawVariable;
InitializeComponent();
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.DoubleBuffer, true);// 双缓冲
this.SetStyle(ControlStyles.ResizeRedraw, true);//调整大小时重绘
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景.
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);// 双缓冲
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
//this.BackColor=Color.Transparent;
}
[Localizable(
true)]
[MergableProperty(
false)]
[DesignerSerializationVisibility(
DesignerSerializationVisibility.Content)]

public string Xmlpath
{
get { return _xmlpath; }
set {_xmlpath=value; }
}

public new QQListBoxItemCollection Items
{
get { return _items; }
}

internal ListBox.ObjectCollection OldItems
{
get { return base.Items; }
}

protected override void OnPaint(PaintEventArgs e)
{
//Bitmap bg=new Bitmap(this.ClientRectangle.Width,this.ClientRectangle.Height);
//Graphics g =Graphics.FromImage(bg);
Graphics g=e.Graphics;
#region 绘制选中项
//判断是否有选中项
if (this.Focused && this.SelectedItem != null)
{
//得到选中项的区域
Rectangle bounds = this.GetItemRectangle(this.SelectedIndex);
//判断选中项是否为分类
if (Items[this.SelectedIndex].IsClass)
{
//g.FillRectangle(new SolidBrush(Color.FromArgb(230,238,241)),bounds);
//Items[this.SelectedIndex].DrawImage(global::QQListBox.Properties.Resources.MainPanel_FolderNode_collapseTexture, new Rectangle(bounds.X+3,bounds.Y+6,12,12));
g.DrawString(Items[this.SelectedIndex].Title, new Font("微软雅黑", 9), new SolidBrush(Color.Black), bounds.Left + 15, bounds.Top + 4);
}
else
{
g.FillRectangle(
new SolidBrush(Color.FromArgb(252,233,161)),bounds);
}

}
#endregion

//循环绘制每项
for (int i = 0; i < Items.Count; i++)
{
Rectangle bounds
= this.GetItemRectangle(i) ;
QQListBoxItem item
= Items[i];
if (item.IsClass==true)
{
//g.FillRectangle(new SolidBrush(Color.FromArgb(50,230,238,241)),bounds);
//g.DrawImage(global::QQListBox.Properties.Resources.MainPanel_FolderNode_collapseTexture, new Rectangle(bounds.X+3,bounds.Y+6,12,12));
if(mouseitem==Items[i] && mouseitem!=this.SelectedItem)
{
g.FillRectangle(
new SolidBrush(Color.FromArgb(50,230,238,241)),bounds);
}
g.DrawString(item.Title,
new Font("微软雅黑", 9), new SolidBrush(Color.Black), bounds.Left + 15, bounds.Top + 4);
}
else
{

if ( item!=this.SelectedItem)
{

Color backColor
= Color.FromArgb(20,216,211,211);
using (SolidBrush brush = new SolidBrush(backColor))
{
//g.FillRectangle(new SolidBrush(Color.White),bounds);
g.FillRectangle(brush, new Rectangle(bounds.X+1,bounds.Y+1,bounds.Width-2,bounds.Height-1));
}
}
if(mouseitem==Items[i] && mouseitem!=this.SelectedItem)
{
Color backColor
= Color.FromArgb(200,192,224,248);
using (SolidBrush brush = new SolidBrush(backColor))
{
//g.FillRectangle(new SolidBrush(Color.White),bounds);
g.FillRectangle(brush, new Rectangle(bounds.X+1,bounds.Y+1,bounds.Width-2,bounds.Height-1));
}

}
Image image
= item.Image;
string text = item.ToString();

if (image != null)
{
g.InterpolationMode
= InterpolationMode.HighQualityBilinear;
g.DrawImage(image,
new Rectangle(bounds.X+12,bounds.Y+7,image.Size.Width,image.Size.Height));
}
int tw=40;
if(item.Remarks!=null)
{
g.DrawString(item.Remarks,
new Font("微软雅黑", 9), new SolidBrush(this.ForeColor), bounds.Left + tw + 15, bounds.Top + 4);
tw
=item.Remarks.Length+tw+25;
// item.Remarks.
}
// GetTextSize()
g.DrawString("(" + item.Title + ")", /*this.Font*/new Font("微软雅黑", 9), new SolidBrush(Color.FromArgb(128, 128, 128)), bounds.Left + tw + 15, bounds.Top + 4);
g.DrawString(item.Text,
new Font("微软雅黑", 9), new SolidBrush(Color.FromArgb(128, 128, 128)), bounds.Left + 40 + 15, bounds.Top + 19);

}
}
base.OnPaint(e);
// e.Graphics.DrawImage(bg,this.ClientRectangle);
}



protected override void OnMeasureItem(MeasureItemEventArgs e)
{
base.OnMeasureItem(e);
try
{
QQListBoxItem item
= Items[e.Index];
if (item.IsClass == true)
{ e.ItemHeight
= 25; }
else
{ e.ItemHeight
= 54; }
}
catch { }
}
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
//this.Items.Clear();
//while ( this.SelectedItems.Count > 0 )
//this.Items.Remove ( this.SelectedItems[0] );
}
protected override void OnSelectedIndexChanged(EventArgs e)
{
base.OnSelectedIndexChanged(e);
QQListBoxItem item
= Items[this.SelectedIndex];
//MessageBox.Show(this.SelectedIndex.ToString());
if (item.IsClass == true && item.IsExpand == true)
{
for (int i = this._items.Count - 1; i >= 0; i--)
{
if (this._items[i].Classid == item.Classid && this._items[i] != item)
{
this._items.RemoveAt(i);
}
}
item.IsExpand
= false;
}
else if (item.IsClass == true)
{
item.IsExpand
= true;
try
{
XmlDocument doc
= new XmlDocument();
doc.Load(_xmlpath);
XmlNodeList Qd
= doc.DocumentElement.ChildNodes[item.Classid].ChildNodes;
int i = 1;
foreach (XmlNode Qi in Qd)
{
this._items.Insert(
this.SelectedIndex+i,
new QQListBoxItem(
item.Classid,
int.Parse(Qi.Attributes["qq"].Value),
Qi.Attributes[
"title"].Value,
Qi.Attributes[
"remarks"].Value,
Qi.Attributes[
"text"].Value,
new Bitmap(Qi.Attributes["image"].Value),
true,
true,
true));
i
++;

}

}
catch (Exception A)
{
Console.WriteLine(
"Exception: {0}", A.ToString());
}

}
this.Invalidate();
}
protected override void OnDoubleClick(EventArgs e)
{
base.OnDoubleClick(e);
QQListBoxItem item
= Items[this.SelectedIndex];
// MessageBox.Show(this.SelectedIndex.ToString());
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
for(int i = 0; i < Items.Count; i++)
{
Rectangle bounds
= this.GetItemRectangle(i) ;
if(bounds.Contains(e.X,e.Y))
{
if (Items[i]!=mouseitem)
{
mouseitem
=Items[i];
this.Invalidate();
}
}
}
}

#region 拖动
//private ListBox sourcelbl;
protected override void OnDragOver(DragEventArgs e)
{
base.OnDragOver(e);
//拖动源和放置的目的地一定是一个ListBox
if (e.Data.GetDataPresent(typeof(System.String)))
{
e.Effect
= DragDropEffects.Move;
}
else
e.Effect
= DragDropEffects.None;


}
protected override void OnDragDrop(DragEventArgs drgevent)
{
base.OnDragDrop(drgevent);
}
#endregion
//计算文本长度
private int StringLength(string text)
{
int len = 0;

for (int i = 0; i < text.Length; i++)
{
byte[] byte_len = System.Text.Encoding.Default.GetBytes(text.Substring(i, 1));
if (byte_len.Length > 1)
len
+= 2; //如果长度大于1,是中文,占两个字节,+2
else
len
+= 1; //如果长度等于1,是英文,占一个字节,+1
}

return len;
}
}
}

QQListBoxItem.cs

//作者:阿龙(Along)
//QQ号:646494711
//QQ群1:57218890
//QQ群2:57219423
//网站:http://www.8timer.com
//博客:http://www.cnblogs.com/Along729/
//声明:未经作者许可,任何人不得发布出售该源码,请尊重别人的劳动成果,谢谢大家支持

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Drawing;
using System.ComponentModel;

namespace QQListBoxDemo
{
[Serializable]
[TypeConverter(
typeof(ExpandableObjectConverter))]
public class QQListBoxItem : IDisposable
{
private bool _isClass=false;
private bool _isExpand=false;
private int _classid=0;
private int _qq = 646494711;
private string _title ="网络之虫";
private string _remarks = "阿龙";
private string _text = "独在异乡为异客,每逢佳节倍思亲。";
private Image _image;
private enum _status
{
online,
unline
}
private bool _isE_mail;
private bool _isSms;
private bool _isVideo;
private object _tag;

#region 构造函数
public QQListBoxItem()
{
}

/// <summary>
/// QQ好友分类
/// </summary>
/// <param name="IsClass">是否为类别</param>
/// <param name="classid">类别ID</param>
/// <param name="title">分类名</param>
/// <param name="isExpand">是否展开</param>
public QQListBoxItem(
bool isClass,
int classid,
string title,
bool isExpand)
{
_isClass
= isClass;
_classid
= classid;
_title
= title;
_isExpand
= isExpand;
}

/// <summary>
/// QQLlitBox子项
/// </summary>
/// <param name="IsClass">是否为类别</param>
/// <param name="classid">类别ID</param>
/// <param name="qq">QQ</param>
/// <param name="title">标题</param>
/// <param name="remarks">备注</param>
/// <param name="text">说明</param>
/// <param name="image">图标</param>
/// <param name="isE_mail">是否显示E-mail</param>
/// <param name="isSms">是否显示语言</param>
/// <param name="isVideo">是否显示视频</param>
public QQListBoxItem(
int classid,
int qq,
string title,
string remarks,
string text,
Image image,
// _status status,
bool isE_mail,
bool isSms,
bool isVideo)
{
_classid
=classid;
_qq
= qq;
_remarks
= remarks;
_title
= title;
_text
= text;
_image
= image;
// _status = status;
_isE_mail = isE_mail;
_isSms
= isSms;
_isVideo
= isVideo;

}

#endregion

#region 属性
public bool IsClass
{
get{ return _isClass; }
set{ _isClass = value; }
}
public bool IsExpand
{
get { return _isExpand; }
set { _isExpand = value; }
}

public int Classid
{
get{ return _classid; }
set{ _classid = value; }
}

[DefaultValue(
typeof(Image), "null")]
public Image Image
{
get{ return _image; }
set{ _image = value; }
}
public int QQ
{
get{ return _qq; }
set{ _qq = value; }
}
[DefaultValue(
"ImageComboBoxItem")]
[Localizable(
true)]
public string Title
{
get{ return _title; }
set{ _title = value; }
}
public string Remarks
{
get{ return _remarks; }
set{ _remarks = value; }
}

public string Text
{
get{ return _text; }
set{ _text = value; }
}
public bool IsE_mail
{
get{ return _isE_mail; }
set{ _isE_mail = value; }
}
public bool IsSms
{
get{ return _isSms; }
set{ _isSms = value; }
}
public bool isVideo
{
get{ return _isVideo; }
set{ _isVideo = value; }
}

[Bindable(
true)]
[Localizable(
false)]
[DefaultValue(
"")]
[TypeConverter(
typeof(StringConverter))]
[DesignerSerializationVisibility(
DesignerSerializationVisibility.Hidden)]
public object Tag
{
get{ return _tag; }
set{ _tag = value; }
}

#endregion

#region 重写方法

public override string ToString()
{
return _text;
}

#endregion

#region IDisposable 成员

public void Dispose()
{
_image
= null;
_tag
= null;
}

#endregion
}

}

QQListBoxItemCollection.cs

//作者:阿龙(Along)
//QQ号:646494711
//QQ群1:57218890
//QQ群2:57219423
//网站:http://www.8timer.com
//博客:http://www.cnblogs.com/Along729/
//声明:未经作者许可,任何人不得发布出售该源码,请尊重别人的劳动成果,谢谢大家支持

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.ComponentModel;

namespace QQListBoxDemo
{
/// <summary>
/// Description of QQListBoxItemCollection.
/// </summary>
[ListBindable(false)]
public class QQListBoxItemCollection : IList,ICollection,IEnumerable
{
#region Fields

private QQListBox _owner;

#endregion

public QQListBoxItemCollection(QQListBox owner)
{
_owner
= owner;
}

internal QQListBox Owner
{
get{ return _owner; }
}

public QQListBoxItem this[int index]
{
get{ return Owner.OldItems[index] as QQListBoxItem; }
set{ Owner.OldItems[index] = value; }
}

public int Count
{
get { return Owner.OldItems.Count; }
}

public bool IsReadOnly
{
get{ return Owner.OldItems.IsReadOnly; }
}

public int Add(QQListBoxItem item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
return Owner.OldItems.Add(item);
}

public void AddRange(QQListBoxItemCollection value)
{
foreach (QQListBoxItem item in value)
{
Add(item);
}
}

public void AddRange(QQListBoxItem[] items)
{
Owner.OldItems.AddRange(items);
}

public void Clear()
{
Owner.OldItems.Clear();
}

public bool Contains(QQListBoxItem item)
{
return Owner.OldItems.Contains(item);
}


public void CopyTo(QQListBoxItem[] destination, int arrayIndex)
{
Owner.OldItems.CopyTo(destination, arrayIndex);
}

public int IndexOf(QQListBoxItem item)
{
return Owner.OldItems.IndexOf(item);
}

public void Insert(int index, QQListBoxItem item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
Owner.OldItems.Insert(index, item);
}

public void Remove(QQListBoxItem item)
{
Owner.OldItems.Remove(item);
}

public void RemoveAt(int index)
{
Owner.OldItems.RemoveAt(index);
}

public IEnumerator GetEnumerator()
{
return Owner.OldItems.GetEnumerator();
}



int IList.Add(object value)
{
if (!(value is QQListBoxItem))
{
throw new ArgumentException();
}
return Add(value as QQListBoxItem);
}

void IList.Clear()
{
Clear();
}

bool IList.Contains(object value)
{
return Contains(value as QQListBoxItem);
}

int IList.IndexOf(object value)
{
return IndexOf(value as QQListBoxItem);
}

void IList.Insert(int index, object value)
{
if (!(value is QQListBoxItem))
{
throw new ArgumentException();
}
Insert(index, value
as QQListBoxItem);
}

bool IList.IsFixedSize
{
get{ return false; }
}

bool IList.IsReadOnly
{
get{ return IsReadOnly; }
}

void IList.Remove(object value)
{
Remove(value
as QQListBoxItem);
}

void IList.RemoveAt(int index)
{
RemoveAt(index);
}

object IList.this[int index]
{
get
{
return this[index];
}
set
{
if (!(value is QQListBoxItem))
{
throw new ArgumentException();
}
this[index] = value as QQListBoxItem;
}
}



void ICollection.CopyTo(Array array, int index)
{
CopyTo((QQListBoxItem[])array, index);
}

int ICollection.Count
{
get{ return Count; }
}

bool ICollection.IsSynchronized
{
get{ return false; }
}

object ICollection.SyncRoot
{
get{ return this; }
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

}

 声明,我这只是给大家一个实现方法,但真实的QQ决不是用LISTBOX绘的,所以大家可以自己偿试着自己自定义个QQ好友列表控件,有时间我自己也会自定义个完整版的QQ好友列表给大家!

还有就是写这个真的很累,东西好不好,大家也给点力吧,回复一下,支持一下啦

源码下载地址

posted @ 2011-03-25 21:00  ALong(阿龙)  阅读(7270)  评论(11编辑  收藏  举报