16.列表框UIListBox类

列表框要实现的主要功能如下:

列表框使用的背景图像和文字项高亮图像如下:

列表框背景图像

代码如下:

 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  阅读次数:5868

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号