-2

我想创建一个派生的 PictureBox 类,支持缩放、平移、滚动功能和一些选择工具(圆形、矩形、环形)。

我想知道我应该使用哪种设计模式(我只是考虑一个用于进行撤消/重做的命令模式)以及一些关于开始正确设计我的控件的建议。

4

2 回答 2

0

可以使用 MVVM,但是当您处理过大的图片时,不值得为此付出代价。

我实现了自己的控制,受到旧代码项目的强烈启发:http: //www.codeproject.com/Articles/15743/Pan-and-Zoom-Very-Large-Images

要利用的技巧之一是复制给定的图片作为输入,它会使过程更快。它没有完全实现,因为我没有那么多时间来处理它。

下面是我的一个实现

public class ARTImageControl : Control
{
    public ARTImageControl()
    {
        this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        this.SetStyle(ControlStyles.DoubleBuffer, true);

        this.DoubleBuffered = true;

        this.InternalScrollbarHorizontal = new HScrollBar();
        this.InternalScrollbarVertical = new VScrollBar();
        this.ScrollBarsVisibility = ScrollBarVisibility.Auto;

        this.ZoomToFit = false;
        this.ZoomFactor = 1.0;
        this.ZoomOnMouseWheel = true;

        this.BorderColor = Color.Black;
        this.BorderWidth = 1;
        this.BorderStyle = ButtonBorderStyle.Solid;

        this.PixelOffsetMode = PixelOffsetMode.Half;
        this.SmoothingMode = SmoothingMode.None;
        this.InterpolationMode = InterpolationMode.NearestNeighbor;

        this.ActiveTool = ARTImageControlToolType.Pan;

        Resize += ARTImageControl_Resize;
        MouseMove += ARTImageControl_MouseMove;
        MouseDown += ARTImageControl_MouseDown;
        MouseWheel += ARTImageControl_MouseWheel;
    }

    protected void UpdateSizeDrawing()
    {
        this.SizeDrawing = new Size(Convert.ToInt32(this.Width / this.ZoomFactor), Convert.ToInt32(this.Height / this.ZoomFactor));
    }

    protected HScrollBar InternalScrollbarHorizontal { get; set; }
    protected VScrollBar InternalScrollbarVertical { get; set; }

    public ScrollBarVisibility ScrollBarsVisibility { get; set; }

    #region Overrides

    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.Clear(this.BackColor);

        if (this.Image != null)
        {
            e.Graphics.PixelOffsetMode = this.PixelOffsetMode;
            e.Graphics.SmoothingMode = this.SmoothingMode;
            e.Graphics.InterpolationMode = this.InterpolationMode;

            if (this.ZoomToFit)
            {
                this.RectangleSource = new Rectangle(0, 0, this.Image.Width, this.Image.Height);
            }
            else
            {
                this.RectangleSource = new Rectangle(this.InternalPointDrawingOrigin, this.SizeDrawing);
            }

            Rectangle rectangleDestination = new Rectangle(this.BorderWidth, this.BorderWidth, this.RectangleDestination.Width - 2 * this.BorderWidth, this.RectangleDestination.Height - 2 * this.BorderWidth);

            e.Graphics.DrawImage(this.Image, rectangleDestination, this.RectangleSource, GraphicsUnit.Pixel);
        }


        if ((this.BorderWidth > 0) && (this.BorderStyle != ButtonBorderStyle.None))
        {
            ControlPaint.DrawBorder(e.Graphics, this.RectangleDestination,
                                    this.BorderColor, this.BorderWidth, this.BorderStyle,
                                    this.BorderColor, this.BorderWidth, this.BorderStyle,
                                    this.BorderColor, this.BorderWidth, this.BorderStyle,
                                    this.BorderColor, this.BorderWidth, this.BorderStyle);
        }

        //base.OnPaint(e);
    }
    protected override void OnSizeChanged(EventArgs e)
    {
        this.RectangleDestination = new Rectangle(0, 0, ClientSize.Width, ClientSize.Height);
        this.UpdateSizeDrawing();
        base.OnSizeChanged(e);
        this.Invalidate();
    }

    #endregion

    #region Methods
    public void ZoomFitToClient()
    {
        if (this.Image != null)
        {
            this.ZoomToFit = false;
            this.ZoomFactor = Math.Min(ClientSize.Width / InternalImage.Width, ClientSize.Height / InternalImage.Height);
        }
    }
    public void ZoomIn()
    {
        this.ZoomFactor *= 1.1f;
    }
    public void ZoomOut()
    {
        this.ZoomFactor *= 0.9f;
    }

    protected void CheckBoundaries()
    {
        if (this.Image != null)
        {
            Int32 boundaryX = this.Image.Width - Convert.ToInt32(this.ClientSize.Width / this.ZoomFactor);
            Int32 boundaryY = this.Image.Height - Convert.ToInt32(this.ClientSize.Height / this.ZoomFactor);

            if (InternalPointDrawingOrigin.X > boundaryX)
            {
                this.InternalPointDrawingOrigin = new Point(boundaryX, this.InternalPointDrawingOrigin.Y);
            }
            if (InternalPointDrawingOrigin.Y > boundaryY)
            {
                this.InternalPointDrawingOrigin = new Point(this.InternalPointDrawingOrigin.X, boundaryY);
            }

            if (InternalPointDrawingOrigin.X < 0)
            {
                this.InternalPointDrawingOrigin = new Point(0, this.InternalPointDrawingOrigin.Y);
            }
            if (InternalPointDrawingOrigin.Y < 0)
            {
                this.InternalPointDrawingOrigin = new Point(this.InternalPointDrawingOrigin.X, 0);
            }
        }
    }
    #endregion

    #region Properties



    protected Image InternalImage { get; set; }
    public Image Image
    {
        get { return this.InternalImage; }
        set
        {
            if (value != null)
            {
                if (this.InternalImage != null)
                {
                    this.InternalImage.Dispose();
                    this.InternalPointDrawingOrigin = new Point(0, 0);
                    this.ZoomFactor = 1;
                    GC.Collect();
                }

                Rectangle rectangle = new Rectangle(0, 0, value.Width, value.Height);
                this.InternalImage = new Bitmap(value).Clone(rectangle, PixelFormat.Format32bppPArgb);
                this.Invalidate();
            }
            else
            {
                this.InternalImage = null;
                this.Invalidate();
            }
        }
    }

    public PixelOffsetMode PixelOffsetMode { get; set; }
    public SmoothingMode SmoothingMode { get; set; }
    public InterpolationMode InterpolationMode { get; set; }

    protected Double InternalZoomFactor { get; set; }
    public Double ZoomFactor
    {
        get { return this.InternalZoomFactor; }
        set
        {
            this.InternalZoomFactor = value;

            if (this.InternalZoomFactor > 15)
            {
                this.InternalZoomFactor = 15;
            }

            if (this.InternalZoomFactor < 0.05)
            {
                this.InternalZoomFactor = 0.05;
            }

            if ((this.Image != null))
            {
                this.UpdateSizeDrawing();
                this.CheckBoundaries();
            }

            this.Invalidate();
        }
    }

    public Boolean InternalZoomToFit { get; set; }
    public Boolean ZoomToFit
    {
        get { return this.InternalZoomToFit; }
        set
        {
            this.InternalZoomToFit = value;
            this.Invalidate();
        }
    }

    public ButtonBorderStyle BorderStyle { get; set; }
    public Byte BorderWidth { get; set; }
    public Color BorderColor { get; set; }

    public Boolean DoubleBuffering
    {
        get { return this.DoubleBuffered; }
        set { this.DoubleBuffered = value; }
    }

    protected Point InternalPointMouseDownStart { get; set; }
    protected Point InternalPointDrawingOrigin { get; set; }

    protected Rectangle RectangleSource { get; set; }
    protected Rectangle RectangleDestination { get; set; }

    protected Size SizeDrawing { get; set; }

    public Boolean ZoomOnMouseWheel { get; set; }

    #endregion

    #region Events
    private void ARTImageControl_Resize(Object sender, EventArgs e)
    {
        this.UpdateSizeDrawing();

        if (this.ZoomToFit)
        {
            this.Invalidate();
        }
    }
    private void ARTImageControl_MouseDown(Object sender, MouseEventArgs e)
    {
        if (this.InternalImage != null)
        {
            this.InternalPointMouseDownStart = new Point(e.X, e.Y);
            this.Focus();
        }
    }

    private void ARTImageControl_MouseMove(Object sender, MouseEventArgs e)
    {
        if (this.Image != null)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (this.ActiveTool == ARTImageControlToolType.Pan)
                {
                    Int32 DeltaX = InternalPointMouseDownStart.X - e.X;
                    Int32 DeltaY = InternalPointMouseDownStart.Y - e.Y;

                    Int32 pointOriginX = Convert.ToInt32(InternalPointDrawingOrigin.X + (DeltaX / InternalZoomFactor));
                    Int32 pointOriginY = Convert.ToInt32(InternalPointDrawingOrigin.Y + (DeltaY / InternalZoomFactor));

                    this.InternalPointDrawingOrigin = new Point(pointOriginX, pointOriginY);

                    this.CheckBoundaries();

                    this.InternalPointMouseDownStart = new Point(e.X, e.Y);
                }
                else
                {

                }

                this.Invalidate();
            }
        }
    }
    private void ARTImageControl_MouseWheel(Object sender, MouseEventArgs e)
    {
        if (ZoomOnMouseWheel)
        {
            if (e.Delta > 0)
            {
                this.ZoomIn();
            }
            else if (e.Delta < 0)
            {
                this.ZoomOut();
            }
        }
    }
    #endregion

    public ARTImageControlToolType ActiveTool { get; set; }
}

public enum ARTImageControlToolType
{
    Pan = 0,
    Rectangle = 1,
}

public abstract class ARTShape
{
    public String Name { get; protected set; }
    public abstract Rectangle GetBoundingRectangle();
}

public class ARTRectangle : ARTShape
{
    public ARTRectangle(UInt32 width, UInt32 height)
    {
        this.Width = width;
        this.Height = height;
    }

    public UInt32 Width { get; protected set; }
    public UInt32 Height { get; protected set; }

    public override Rectangle GetBoundingRectangle()
    {
        throw new NotImplementedException();
    }

    public Color ColorInactive { get; protected set; }
    public Color ColorActive { get; protected set; }
}

public enum ScrollBarVisibility
{
    Auto = 0,
    Always = 1,
    Never = 2,
}
于 2013-11-06T06:07:02.460 回答
0

您没有提及您正在使用的 UI 技术,也没有定义“PictureBox”是什么,所以我假设您的意思是您正在构建一个“Photoshop”的精简版应用程序,如果您是做一个桌面应用程序我会选择 WPF。

控件是使用 UI 技术在 Visual Studio 中开发的,因此说它是“在 Visual Studio 中”并没有真正的帮助,因为这可能意味着 WPF、Silverlight、ASP.NET、WebForms、Windows 窗体、Javascript、Mono 等......

鉴于这些假设,我总是选择使用 MVVM 作为应用程序 UI 的主要架构。它促进松耦合并利用 WPF 数据绑定技术的优势。

也就是说,如果您指的是 Windows 窗体,那么值得一看

于 2013-05-15T10:38:59.003 回答