16.列表框UIListBox类
列表框要实现的主要功能如下:
- 绘制背景;
- 依次绘制文字项数组中的文字;
- 鼠标移动到文字项上时绘制文字项的高亮背景;
- 鼠标点击文字项时绘制文字项的高亮背景并加以保留,直到选中的文字项发生改变;
- 如果文字项的总高度大于列表框高度,自动显示竖直滚动条;
- 如果文字项的宽度大于宽度,自动显示水平滚动条,这个功能还没有实现,它比较难,看一下xWinForms 3.0,你会发现他的方法是使用一个渲染目标将列表框中的内容进行截图,显示的是截图的图像。
列表框使用的背景图像和文字项高亮图像如下:

代码如下:
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using StunEngine.Controllers;
using StunEngine.SceneManagement;
namespace StunEngine.SceneNodes.UI
{
///
/// 列表框
///
public class UIListBox : UISceneNode
{
#region 成员变量和构造函数
//默认值
private static readonly Vector2 DefaultSize = new Vector2(160, 92);
private static readonly Rectangle DefaultListboxRect = new Rectangle(1, 1, 6, 6);
private static readonly float DefaultScrollBarWidth = 16.0f;
private static readonly Rectangle DefaultSelectionRect = new Rectangle(0, 24, 128, 16);
///
/// 文字项数组
///
private List items = new List();
///
/// 列表框中可见的文字项数量,它是由列表框高度和文字项高度计算得出的
///
int visibleItemsCount;
///
/// 列表框图像矩形
///
Rectangle listboxRect;
///
/// 滚动条宽度,默认为16。高度无需指定,等于列表框高度。
///
private float scrollBarWidth;
///
/// 选择项的背景的目标矩形
///
Rectangle selectionDestinationRect;
///
/// 选择项的背景图像矩形
///
Rectangle selectionRect;
///
/// 鼠标所处的文字项索引,初始值为-1。
///
int hoverIndex = -1;
///
/// 选中的文字项的索引,初始值为-1,表示没有选中。
///
int selectedIndex = -1;
///
/// 选中的文字项的文字,默认为空字符。
///
string selectedItem = string.Empty;
///
/// 文字使用的字体
///
protected SpriteFont font;
///
/// 文字颜色
///
protected Color fontColor;
///
/// 竖直滚动条
///
UIVScrollBar VScrollBar;
///
/// 文字项选中时引发的事件
///
public event EventHandler Select = null;
///
/// 选中的文字项改变时引发的事件
///
public event EventHandler ChangeSelection = null;
///
/// 创建一个默认列表框,不包含文字项
///
/// 引擎
/// 所属场景
public UIListBox(StunXnaGE engine, Scene setScene)
: this(engine, setScene, null)
{ }
///
/// 创建一个列表框,需要指定包含的文字项
///
/// 引擎
/// 所属场景
/// 文字项数组
public UIListBox(StunXnaGE engine, Scene setScene, string[] setItems)
: this(engine, setScene, "Textures/UI/UIListbox", Vector2.Zero, DefaultSize, DefaultListboxRect, engine.DefaultFont, Color.White, setItems, DefaultScrollBarWidth, DefaultSelectionRect)
{ }
///
/// 创建一个列表框,内部方法,只能在引擎内部调用。
///
/// 引擎
/// 所属场景
/// 纹理文件名称
/// 2D屏幕位置
/// 列表框大小
/// 字体
/// 文字颜色
/// 文字项数组
/// 竖直滚动条宽度
/// 被选择的文字项背景图像矩形
internal UIListBox(StunXnaGE engine, Scene setScene, string setTextureName, Vector2 setPosition, Vector2 setSize, Rectangle setListboxRect, SpriteFont setFont, Color setTextColor, string[] setItems, float setScrollBarWidth, Rectangle setSelectionRect)
: base(engine, setScene, setPosition, setTextureName)
{
this.size = setSize;
this.listboxRect = setListboxRect;
this.font = setFont;
this.fontColor = setTextColor;
this.selectionRect = setSelectionRect;
this.scrollBarWidth = setScrollBarWidth;
if (setItems != null)
{
for (int i = 0; i < setItems.Length; i++)
this.items.Add(setItems[i]);
}
VScrollBar = new UIVScrollBar(engine, setScene);
VScrollBar.TabStop = false;
scene.AddNode(VScrollBar);
//绘制列表框
Redraw();
}
#endregion
#region 属性
///
/// 获取或设置列表框图像矩形
///
public Rectangle ListboxRect
{
get { return listboxRect; }
set { listboxRect = value; }
}
///
/// 获取或设置滚动条宽度,默认为16。高度无需指定,等于列表框高度。
///
public float ScrollBarWidth
{
get { return scrollBarWidth; }
set
{
scrollBarWidth = value;
Redraw();
}
}
///
/// 获取或设置选中文字项的文字
///
public string SelectedItem
{
get
{
if (selectedIndex != -1)
return items[selectedIndex];
else
return "null";
}
set
{
for (int i = 0; i < items.Count; i++)
if (items[i] == value)
{
selectedIndex = i;
break;
}
}
}
///
/// 获取或设置文字项数组
///
public List Items
{
get { return items; }
set
{
items = value;
}
}
///
/// 获取或设置文字使用的字体,字体改变时需要重新绘制列表框
///
public SpriteFont Font
{
get { return font; }
set
{
font = value;
Redraw();
}
}
///
/// 获取或设置文字的颜色
///
public Color FontColor
{
get { return fontColor; }
set { fontColor = value; }
}
///
/// 获取选中的文字项的索引
///
public int SelectedIndex { get { return selectedIndex; } set { selectedIndex = value; } }
///
/// 索引器
///
///
///
public string this[int index]
{
get
{
if (index > items.Count)
return "null";
return items[index];
}
set
{
if (index > items.Count)
return;
items[index] = value;
}
}
#endregion
public override void Update(GameTime gameTime)
{
//如果鼠标在控件内部,则计算当前所处的文字项索引
if (isMouseInside)
{
hoverIndex = VScrollBar.Value + (int)Math.Floor((Input.MousePos.Y - position.Y) / font.LineSpacing);
if (hoverIndex >= System.Math.Min(items.Count, VScrollBar.Value + visibleItemsCount) || hoverIndex < VScrollBar.Value)
hoverIndex = -1;
}
//如果鼠标位于一个文字项之上并且点击鼠标左键
if (hoverIndex != -1 && Input.MouseLeftButtonPressed)
{
//存储上一帧选中的文字项索引
int previousIndex = selectedIndex;
//将hoverIndex的值传递给selectedIndex
selectedIndex = hoverIndex;
//引发Select事件
if (Select != null)
Select(items[selectedIndex], null);
//如果改变了选中的文字项则引发ChangeSelection事件
if (selectedIndex != previousIndex && ChangeSelection != null)
ChangeSelection(items[selectedIndex], null);
}
base.Update(gameTime);
}
public override void Draw(GameTime gameTime, bool useReflection)
{
//获取图像淡入淡出的透明颜色
alphaTextureColor = new Color(color, scene.TransitionAlpha);
//绘制背景
StunXnaGE.SpriteBatch.Draw(material.Textures[0], new Rectangle((int)position.X, (int)(position.Y), (int)size.X, (int)size.Y), listboxRect, alphaTextureColor, rotation, origin, spriteEffect, layerDepth);
//则绘制文字项的高亮背景
if (hoverIndex >= VScrollBar.Value && hoverIndex < System.Math.Min(items.Count, VScrollBar.Value + visibleItemsCount))
{
selectionDestinationRect.Y = (int)(position.Y + font.LineSpacing * (hoverIndex - VScrollBar.Value));
StunXnaGE.SpriteBatch.Draw(material.Textures[0], selectionDestinationRect, selectionRect, alphaTextureColor, rotation, origin, spriteEffect, layerDepth);
}
//绘制选中的文字项的高亮背景
if (selectedIndex >= VScrollBar.Value && selectedIndex < System.Math.Min(items.Count, VScrollBar.Value + visibleItemsCount))
{
selectionDestinationRect.Y = (int)(position.Y + font.LineSpacing * (selectedIndex - VScrollBar.Value));
StunXnaGE.SpriteBatch.Draw(material.Textures[0], selectionDestinationRect, selectionRect, alphaTextureColor, rotation, origin, spriteEffect, layerDepth);
}
//绘制文字项
if (items.Count > 0)
{
for (int i = VScrollBar.Value; i < System.Math.Min(items.Count, VScrollBar.Value + visibleItemsCount); i++)
{
Vector2 textOffset = Vector2.Zero;
textOffset.X = position.X;
textOffset.Y = position.Y + (i - VScrollBar.Value) * font.LineSpacing;
StunXnaGE.SpriteBatch.DrawString(font, items[i], textOffset, fontColor);
}
}
}
#region 处理文字项数组的公有方法
///
/// 在文字项数组中添加一个数据项
///
/// 文字项
public void AddItem(string item)
{
//在文字项数组中添加给定文字
items.Add(item);
Redraw();
}
///
/// 根据给定文字项文字移除一个文字项
///
/// 给定的文字项文字
public void Remove(string item)
{
//如果文字项数组中不包含给定的文字则返回
if (!items.Contains(item))
return;
items.Remove(item);
Redraw();
}
///
/// 根据索引移除一个文字项
///
/// 文字项索引
public void RemoveAt(int index)
{
if (index < 0 || index >= items.Count)
return;
items.RemoveAt(index);
Redraw();
}
///
/// 清除文字项数组
///
public void Clear()
{
items.Clear();
Redraw();
}
#endregion
#region 事件相关处理程序
///
/// 重新绘制列表框
///
private void Redraw()
{
//根据列表框高度计算列表框可见的文字项数量
visibleItemsCount = (int)System.Math.Ceiling(size.Y / font.LineSpacing);
//重新设置列表框大小
this.size = new Vector2(size.X, visibleItemsCount * font.LineSpacing);
//重新设置竖直滚动条的相关属性
VScrollBar.MaxStep = items.Count - visibleItemsCount;
VScrollBar.Size = new Vector2(scrollBarWidth, size.Y);
VScrollBar.Position = new Vector2(position.X + size.X - scrollBarWidth, position.Y);
VScrollBar.LargeChange = visibleItemsCount;
//重新设置高亮图像的目标矩形
this.selectionDestinationRect = new Rectangle((int)position.X, (int)position.Y, VScrollBar.IsVisible ? (int)(size.X - VScrollBar.Size.X) : (int)size.X, font.LineSpacing);
}
///
/// 列表框位置变化时需要重新绘制
///
protected override void OnLocationChanged()
{
Redraw();
base.OnLocationChanged();
}
///
/// 列表框大小变化时需要重新绘制
///
protected override void OnSizeChanged()
{
Redraw();
base.OnSizeChanged();
}
protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
VScrollBar.IsVisible = isVisible;
}
#endregion
#region 单元测试
#if DEBUG
///
/// 测试UIListbox类
///
public static void TestUIListbox()
{
UIListBox listbox = null;
UILabel lblMessage1 = null;
UILabel lblMessage2 = null;
TestGame.Start("测试listbox类,可按空格键,下右键,数字键123",
delegate
{
listbox = new UIListBox(TestGame.engine, TestGame.scene);
TestGame.scene.AddNode(listbox);
listbox.AddItem("First");
listbox.AddItem("Second");
listbox.AddItem("Third");
listbox.AddItem("Four");
listbox.AddItem("Five");
listbox.Position = new Vector2(100, 100);
lblMessage1 = new UILabel(TestGame.engine, TestGame.scene);
lblMessage1.Position = new Vector2(280, 100);
TestGame.scene.AddNode(lblMessage1);
lblMessage2 = new UILabel(TestGame.engine, TestGame.scene);
lblMessage2.Position = new Vector2(280, 150);
TestGame.scene.AddNode(lblMessage2);
//关闭相机控制器
TestGame.scene.fpsCamCtrl.Enabled = false;
},
delegate
{
lblMessage1.Text = "SelectItem:" + listbox.hoverIndex.ToString();
lblMessage2.Text = "SelectIndex:" + listbox.SelectedIndex.ToString();
if (Input.KeyboardSpaceJustPressed)
listbox.IsVisible = !listbox.IsVisible;
if (Input.KeyboardDownJustPressed)
listbox.Position += new Vector2(5, 5);
if (Input.KeyboardRightJustPressed)
listbox.Size += new Vector2(5, 5);
if (Input.KeyboardKeyJustPressed(Keys.D1))
listbox.AddItem("NewItem");
if (Input.KeyboardKeyJustPressed(Keys.D2))
listbox.Remove("NewItem");
if (Input.KeyboardKeyJustPressed(Keys.D3))
listbox.RemoveAt(listbox.items.Count - 1);
},
null);
}
#endif
#endregion
}
}
单元测试截图如下:

发布时间:2010/2/23 下午2:54:48 阅读次数:6946
