6

在没有可用真实库的平台上,除了“在坐标 (x,y) 处显示尺寸 (x,y,xx,yy) 的对象之外的最小图形,我正在尝试创建一个简单的 gui。

有人可以给我指一个参考,我可以在其中理解在屏幕上显示一组对象并突出显示所选对象所涉及的逻辑原理,允许用户在对象之间导航并将突出显示移动到每个对象。看起来应该很简单,但我想了解人们对此的看法。

如何使用 obj.highlight() 之类的方法创建一个对象,其中 obj.highlight 会关闭所有其他对象的突出显示?是否会简单地通过一组对象执行 for next 循环,跳过当前对象,关闭突出显示,然后将当前对象设置为 true?突出显示将通过在具有透明中心的选定对象顶部绘制另一个对象来完成。

这是一个单线程系统,(但允许少量异步处理)。

我正在寻找更多的概念性想法,但 VB 中不使用专有图形调用的代码可能会有用。

4

2 回答 2

3

我编写了一个小型示例应用程序,该应用程序通过使用 .Net C# 在表单上进行绘制来实现自己的控制框架。这个结果很简单:

在此处输入图像描述

我通过递归禁用所有控件并切换单击的控件来完成 IsSelected。请参阅带有 的部分window.MouseUp += (sender, arg) =>

选择可以通过鼠标或 Tab 键。

代码方法应该可以移植到其他语言,并且可以在线翻译到 VB.Net。

相关代码片段:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
using System.Threading;
using System.Threading.Tasks;

namespace CustomGUI
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Form window = new Form1();
            window.BackColor = Color.Gray;

            Graphics api = window.CreateGraphics();

            GUIControl form = new GUIControl();
            form.Location = new Point(30,30);
            form.Size = new Size(200, 300);

            GUIControl control1 = new GUIControl();
            control1.Location = new Point(0, 0);
            control1.Size = new Size(200, 130);
            control1.Background = Color.Blue;

            GUIControl control11 = new GUIControl();
            control11.Location = new Point(140, 30);
            control11.Size = new Size(30, 30);
            control11.Background = Color.Red;

            GUIControl control12 = new GUIControl();
            control12.Location = new Point(30, 30);
            control12.Size = new Size(30, 30);
            control12.Background = Color.Red;
            control12.BorderColor = Color.Green;
            control12.BorderWidth = 5;

            GuiLabel control2 = new GuiLabel();
            control2.Location = new Point(10, 200);
            control2.Size = new Size(180, 30);
            control2.Background = Color.Green;
            control2.Text = "Hello World!";

            control1.AddChild(control11);
            control1.AddChild(control12);

            form.AddChild(control1);
            form.AddChild(control2);

            window.MouseUp += (sender, arg) =>
            {
                // hit test the control where the mouse has landed
                IGUIContainer control = form.HitTest(arg.Location);
                if (control != null)
                {
                    // recursive on all controls
                    foreach (var ct in (new IGUIContainer[] { form }).Traverse(c => c.Controls))
                    {
                        //deselecting all others
                        if (ct != control) ct.IsSelected = false;
                    }
                    control.IsSelected = !control.IsSelected;
                }
                window.Invalidate(); // force paint
            };

            window.KeyUp += (sender, key) =>
            {
                if (key.KeyCode == Keys.Tab && key.Modifiers == Keys.None)
                {
                    var selected = (new IGUIContainer[] { form }).Traverse(c => c.Controls).FirstOrDefault(c => c.IsSelected);

                    IGUIContainer parent;

                    if (selected == null)
                    {
                        parent = form;
                    }
                    else
                    {
                        parent = selected;
                    }

                    IGUIContainer control;

                    if (parent.Controls.Count > 0)
                    {
                        control = parent.Controls[0];
                    }
                    else
                    {
                        control = GUIControl.Next(parent);
                    }

                    if (control == null) control = form;

                    foreach (var ct in (new IGUIContainer[] { form }).Traverse(c => c.Controls))
                    {
                        if (ct != control) ct.IsSelected = false;
                    }

                    control.IsSelected = true;

                    window.Invalidate();
                }
            };

            window.Paint += (sender, args) =>
            {
                form.Draw(api, new Point(0,0));
            };

            Application.Run(window);
        }
    }
}

所有需要的类和接口:

可绘制:

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

namespace CustomGUI
{
    public interface IDrawable
    {
        Point Location { get; set; }
        Size Size { get; set; }
        Rectangle GetRealRect(Point origin);
        void Draw(Graphics gfxApi, Point origin);
    }
}

IGUI容器:

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

namespace CustomGUI
{
    delegate void SelectionChangedHandler(object sender, bool newIsSelected);

    interface IGUIContainer : IUIElement
    {
        IGUIContainer Parent { get; set; }
        List<IGUIContainer> Controls { get; }
        void AddChild(IGUIContainer child);
        bool IsSelected { get; set; }
        event SelectionChangedHandler SelectionChanged;
        IGUIContainer HitTest(Point mouseCoord);
    }
}

界面元素:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Diagnostics;

namespace CustomGUI
{
    abstract class UIElement : IUIElement
    {
        private Point _location;
        private Size _size;
        private Color _background;
        private Color _foreground;
        private Color _borderColor;
        private int _borderWidth;

        public UIElement()
        {
            _foreground = Color.Black;
            _background = Color.White;
            _borderColor = Color.Transparent;
        }

        public Point Location
        {
            get
            {
                return _location;
            }
            set
            {
                _location = value;
            }
        }

        public Size Size
        {
            get
            {
                return _size;
            }
            set
            {
                _size = value;
            }
        }

        public virtual void Draw(Graphics drawingApi, Point origin)
        {

            Rectangle inside = GetRealRect(origin);

            Pen borderPen = new Pen(new SolidBrush(_borderColor), _borderWidth);
            drawingApi.FillRectangle(new SolidBrush(_background), inside);
            drawingApi.DrawRectangle(borderPen, inside);
        }

        public Rectangle ClientRect
        {
            get
            {
                return new Rectangle(_location, _size);
            }
        }


        public Color Background
        {
            get
            {
                return _background;
            }
            set
            {
                _background = value;
            }
        }

        public Color Foreground
        {
            get
            {
                return _foreground;
            }
            set
            {
                _foreground = value;
            }
        }


        public Rectangle GetRealRect(Point origin)
        {
            int left = ClientRect.Left + origin.X;
            int top = ClientRect.Top + origin.Y;
            int width = ClientRect.Width;
            int height = ClientRect.Height;

            Debug.WriteLine("GetRealRect " + left + ", " + top + ", " + width + ", " + height);

            return new Rectangle(left, top, width, height);
        }


        public int BorderWidth
        {
            get
            {
                return _borderWidth;
            }
            set
            {
                _borderWidth = value;
            }
        }

        public Color BorderColor
        {
            get
            {
                return _borderColor;
            }
            set
            {
                _borderColor = value;
            }
        }
    }
}

GUI控制:

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

namespace CustomGUI
{
    class GUIControl : UIElement, IGUIContainer
    {
        private IGUIContainer _parent;
        private List<IGUIContainer> _controls = new List<IGUIContainer>();
        private bool _isSelected;

        public List<IGUIContainer> Controls
        {
            get
            {
                return _controls;
            }
        }

        public override void Draw(Graphics api, Point origin)
        {
            Point original = origin;

            base.Draw(api, origin);

            origin.Offset(this.Location);

            foreach (var ctrl in Controls)
            {
                ctrl.Draw(api, origin);
            }

            if (IsSelected)
            {
                Pen selection = new Pen(Color.Yellow, 3);
                selection.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
                api.DrawRectangle(selection, GetRealRect(original));
            }

        }

        public IGUIContainer HitTest(Point coord)
        {
            Point newOrigin = coord;
            newOrigin.Offset(-this.Location.X, -this.Location.Y);

            foreach (var ctrl in Controls)
            {
                IGUIContainer hit = ctrl.HitTest(newOrigin);
                if (hit != null)
                {
                    return hit;
                }
            }

            return ClientRect.Contains(coord) ? this : null;
        }

        public bool IsSelected
        {
            get
            {
                return _isSelected;
            }
            set
            {
                _isSelected = value;

                if (SelectionChanged != null)
                {
                    SelectionChanged(this, _isSelected);
                }
            }
        }

        public event SelectionChangedHandler SelectionChanged;

        public void AddChild(IGUIContainer child)
        {
            // if you need to implement event propagation this is the place to attach them to children
            child.Parent = this;
            Controls.Add(child);
        }

        public IGUIContainer Parent
        {
            get
            {
                return _parent;
            }
            set
            {
                _parent = value;
            }
        }

        public static IGUIContainer Next(IGUIContainer self)
        {
            if (self.Parent != null &&
                self.Parent.Controls.Count - 1 > self.Parent.Controls.IndexOf(self))
            {
                return self.Parent.Controls[self.Parent.Controls.IndexOf(self) + 1];
            }
            else if (self.Parent != null)
            {
                return Next(self.Parent);
            }
            else
            {
                return null;
            }
        }
    }
}

图形标签:

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

namespace CustomGUI
{
    class GuiLabel : GUIControl
    {
        public string Text { get; set; }
        public Font Font { get; set; }

        public GuiLabel()
        {
            Font = new Font(new FontFamily("Tahoma"), 12, FontStyle.Regular);            
        }

        public override void Draw(System.Drawing.Graphics api, System.Drawing.Point origin)
        {
            base.Draw(api, origin);

            Rectangle controlRect = GetRealRect(origin);
            SizeF size = api.MeasureString(Text, Font);

            Point textPosition = new Point(controlRect.Location.X + (int)(controlRect.Width - size.Width) / 2,
                                        controlRect.Location.Y + (int)(controlRect.Height - size.Height) / 2);

            api.DrawString(Text, Font, new SolidBrush(Foreground), textPosition);
        }
    }
}

扩展(用于 Traverse 方法以展平递归):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CustomGUI
{
    static class Extensions
    {
        public static IEnumerable<T> Traverse<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> fnRecurse)
        {

            foreach (T item in source)
            {

                yield return item;

                IEnumerable<T> seqRecurse = fnRecurse(item);

                if (seqRecurse != null)
                {

                    foreach (T itemRecurse in Traverse(seqRecurse, fnRecurse))
                    {

                        yield return itemRecurse;

                    }

                }

            }

        }
    }
}
于 2011-06-29T03:15:04.920 回答
2

嗯,这是一个可以用一百万种方式回答的问题...... :)

但只要你可以绘制像素(或任何类似的东西),你就可以绘制一个 GUI。如果您手头有面向对象的语言,我不会选择突出显示和取消突出显示当前对象。我会给出焦点并从中移除焦点,并让对象本身决定是否应该重绘以及如何重绘。

如果所有对象都放在某种容器中,您可以自动取消对前一个对象的聚焦。当您按下导航键(如 Tab)或按下鼠标按钮时,该容器可以处理该消息并聚焦下一个对象并取消聚焦最后一个对象。

它需要一些编程,但这个概念很简单。当你希望它表现良好、看起来光滑、有各种动画和转换时,它变得更加困难......但正如我所说,这个概念很简单,你甚至不需要 OO 来做它,尽管它可能会给你的结果更干净。如果需要,我想我可以在下雨的下午在 DOS Batch 中编写基于 ASCII 的 GUI。

于 2011-06-28T22:18:07.863 回答