WPF下的仿QQ图片查看器

    本例中的大图模式使用图片控件展示,监听控件的鼠标滚轮事件和移动事件,缩略图和鹰眼模式采用装饰器对象IndicatorObject和Canvas布局。百分比使用一个定时器,根据图片的放大倍数计算具体的数值显示。

首先看看效果图:

 

以下开始绘制图片 定义缩略图上白色的矩形,这其实是一个Indicator,它的外围是一个Canvas,然后缩略图是一个Image控件

 internal class IndicatorObject : ContentControl
    {
        private MaskCanvas canvasOwner;

        public IndicatorObject(MaskCanvas canvasOwner)
        {
            this.canvasOwner = canvasOwner;
        }

        static IndicatorObject()
        {
            var ownerType = typeof(IndicatorObject);

            FocusVisualStyleProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(null));
            DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(ownerType));
            MinWidthProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(5.0));
            MinHeightProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(5.0));
        }

        public void Move(System.Windows.Point offset)
        {
            var x = Canvas.GetLeft(this) + offset.X;
            var y = Canvas.GetTop(this) + offset.Y;

            x = x < 0 ? 0 : x;
            y = y < 0 ? 0 : y;

            x = Math.Min(x, this.canvasOwner.Width - this.Width);
            y = Math.Min(y, this.canvasOwner.Height - this.Height);

            Canvas.SetLeft(this, x);
            Canvas.SetTop(this, y);

            canvasOwner.UpdateSelectionRegion(new Rect(x, y, Width, Height), true);
        }



    }
Indicator

查看位置所在的矩形定义好了,然后开始定义外围的Canvas,这个作用是可以在Canvas上选中移动到查看的位置

  public class MaskCanvas : Canvas
    {
        public MaskCanvas()
        {
            Loaded += OnLoaded;
        }

        public System.Windows.Media.Brush SelectionBorderBrush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(255, 255, 255, 255));
        public Thickness SelectionBorderThickness = new Thickness(1);

        public System.Windows.Media.Brush MaskWindowBackground = new SolidColorBrush(System.Windows.Media.Color.FromArgb(5, 0, 0, 0));

        public event EventHandler<LoactionArgs> LoationChanged;

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
           maskRectLeft.Fill = maskRectRight.Fill = maskRectTop.Fill = maskRectBottom.Fill = MaskWindowBackground;

            SetLeft(maskRectLeft, 0);
            SetTop(maskRectLeft, 0);
            SetRight(maskRectRight, 0);
            SetTop(maskRectRight, 0);
            SetTop(maskRectTop, 0);
            SetBottom(maskRectBottom, 0);
            maskRectLeft.Height = ActualHeight;

            Children.Add(maskRectLeft);
            Children.Add(maskRectRight);
            Children.Add(maskRectTop);
            Children.Add(maskRectBottom);

            selectionBorder.Stroke = SelectionBorderBrush;
            selectionBorder.StrokeThickness =  1;
         
            Children.Add(selectionBorder);

            indicator = new IndicatorObject(this);
            indicator.Visibility = System.Windows.Visibility.Hidden;
            Children.Add(indicator);

            CompositionTarget.Rendering += OnCompositionTargetRendering;
           
        }

        private void UpdateSelectionBorderLayout()
        {
            if (!selectionRegion.IsEmpty)
            {
                SetLeft(selectionBorder, selectionRegion.Left);
                SetTop(selectionBorder, selectionRegion.Top);
                selectionBorder.Width = selectionRegion.Width;
                selectionBorder.Height = selectionRegion.Height;
            }
        }

        private void UpdateMaskRectanglesLayout()
        {
            var actualHeight = ActualHeight;
            var actualWidth = ActualWidth;

            if (selectionRegion.IsEmpty)
            {
                SetLeft(maskRectLeft, 0);
                SetTop(maskRectLeft, 0);
                maskRectLeft.Width = actualWidth;
                maskRectLeft.Height = actualHeight;

                maskRectRight.Width = maskRectRight.Height = maskRectTop.Width = maskRectTop.Height = maskRectBottom.Width = maskRectBottom.Height = 0;
            }
            else
            {
                var temp = selectionRegion.Left;
                if (maskRectLeft.Width != temp)
                {
                    maskRectLeft.Width = temp < 0 ? 0 : temp; //Math.Max(0, selectionRegion.Left);
                }

                temp = ActualWidth - selectionRegion.Right;
                if (maskRectRight.Width != temp)
                {
                    maskRectRight.Width = temp < 0 ? 0 : temp; //Math.Max(0, ActualWidth - selectionRegion.Right);
                }

                if (maskRectRight.Height != actualHeight)
                {
                    maskRectRight.Height = actualHeight;
                }

                SetLeft(maskRectTop, maskRectLeft.Width);
                SetLeft(maskRectBottom, maskRectLeft.Width);

                temp = actualWidth - maskRectLeft.Width - maskRectRight.Width;
                if (maskRectTop.Width != temp)
                {
                    maskRectTop.Width = temp < 0 ? 0 : temp; //Math.Max(0, ActualWidth - maskRectLeft.Width - maskRectRight.Width);
                }

                temp = selectionRegion.Top;
                if (maskRectTop.Height != temp)
                {
                    maskRectTop.Height = temp < 0 ? 0 : temp; //Math.Max(0, selectionRegion.Top);
                }

                maskRectBottom.Width = maskRectTop.Width;

                temp = actualHeight - selectionRegion.Bottom;
                if (maskRectBottom.Height != temp)
                {
                    maskRectBottom.Height = temp < 0 ? 0 : temp; //Math.Max(0, ActualHeight - selectionRegion.Bottom);
                }
            }
        }


        #region Fileds & Props
        private Rect selectionRegion = Rect.Empty;
        private bool isMaskDraging;
        public bool MoveState = false;
        private IndicatorObject indicator;
        private System.Windows.Point? selectionStartPoint;
        private System.Windows.Point? selectionEndPoint;

        private readonly System.Windows.Shapes.Rectangle selectionBorder = new System.Windows.Shapes.Rectangle();

        private readonly System.Windows.Shapes.Rectangle maskRectLeft = new System.Windows.Shapes.Rectangle();
        private readonly System.Windows.Shapes.Rectangle maskRectRight = new System.Windows.Shapes.Rectangle();
        private readonly System.Windows.Shapes.Rectangle maskRectTop = new System.Windows.Shapes.Rectangle();
        private readonly System.Windows.Shapes.Rectangle maskRectBottom = new System.Windows.Shapes.Rectangle();


        public System.Drawing.Size? DefaultSize
        {
            get;
            set;
        }
        #endregion

        #region Mouse Managment

        private bool IsMouseOnThis(RoutedEventArgs e)
        {
            return e.Source.Equals(this) || e.Source.Equals(maskRectLeft) || e.Source.Equals(maskRectRight) || e.Source.Equals(maskRectTop) || e.Source.Equals(maskRectBottom);
        }

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            indicator.Visibility = System.Windows.Visibility.Visible;
         
            if (e.Source.Equals(indicator))
            {
                HandleIndicatorMouseDown(e);
            }
            base.OnMouseLeftButtonDown(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (IsMouseOnThis(e))
            {
                UpdateSelectionRegion(e, UpdateMaskType.ForMouseMoving);

                e.Handled = true;
            }
            base.OnMouseMove(e);
        }

        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            if (IsMouseOnThis(e))
            {
                UpdateSelectionRegion(e, UpdateMaskType.ForMouseLeftButtonUp);
                FinishShowMask();
            }
            base.OnMouseLeftButtonUp(e);
        }

        protected override void OnMouseRightButtonUp(MouseButtonEventArgs e)
        {
            indicator.Visibility = Visibility.Collapsed;
            selectionRegion = Rect.Empty;
            selectionBorder.Width = selectionBorder.Height = 0;
           // ClearSelectionData();
            UpdateMaskRectanglesLayout();

            base.OnMouseRightButtonUp(e);
        }


        internal void HandleIndicatorMouseDown(MouseButtonEventArgs e)
        {
            MoveState = true;
        }

        internal void HandleIndicatorMouseUp(MouseButtonEventArgs e)
        {
            MoveState = false;
        }

        private void PrepareShowMask(System.Drawing.Point mouseLoc)
        {
            indicator.Visibility = Visibility.Collapsed;
            selectionBorder.Visibility = Visibility.Visible;
           
        }

        private void UpdateSelectionRegion()
        {
            var startPoint = new System.Drawing.Point(0,0);
            var endPoint = new System.Drawing.Point(190, 130);
            var sX = startPoint.X;
            var sY = startPoint.Y;
            var eX = endPoint.X;
            var eY = endPoint.Y;

            var deltaX = eX - sX;
            var deltaY = eY - sY;

            if (Math.Abs(deltaX) >= SystemParameters.MinimumHorizontalDragDistance ||
                Math.Abs(deltaX) >= SystemParameters.MinimumVerticalDragDistance)
            {
               
                double x = sX < eX ? sX : eX;//Math.Min(sX, eX);
                double y = sY < eY ? sY : eY;//Math.Min(sY, eY);
                double w = deltaX < 0 ? -deltaX : deltaX;//Math.Abs(deltaX);
                double h = deltaY < 0 ? -deltaY : deltaY;//Math.Abs(deltaY);

                selectionRegion = new Rect(x, y, w, h);
            }
            else
            {
               selectionRegion = new Rect(startPoint.X, startPoint.Y, DefaultSize.Value.Width, DefaultSize.Value.Height);
            }
        }

        private void UpdateSelectionRegion(MouseEventArgs e, UpdateMaskType updateType)
        {
            if (updateType == UpdateMaskType.ForMouseMoving && e.LeftButton != MouseButtonState.Pressed)
            {
                selectionStartPoint = null;
            }

            if (selectionStartPoint.HasValue)
            {
                selectionEndPoint = e.GetPosition(this);

                var startPoint = (System.Windows.Point)selectionEndPoint;
                var endPoint = (System.Windows.Point)selectionStartPoint;
                var sX = startPoint.X;
                var sY = startPoint.Y;
                var eX = endPoint.X;
                var eY = endPoint.Y;

                var deltaX = eX - sX;
                var deltaY = eY - sY;

                if (Math.Abs(deltaX) >= SystemParameters.MinimumHorizontalDragDistance ||
                    Math.Abs(deltaX) >= SystemParameters.MinimumVerticalDragDistance)
                {
                    isMaskDraging = true;

                    double x = sX < eX ? sX : eX;//Math.Min(sX, eX);
                    double y = sY < eY ? sY : eY;//Math.Min(sY, eY);
                    double w = deltaX < 0 ? -deltaX : deltaX;//Math.Abs(deltaX);
                    double h = deltaY < 0 ? -deltaY : deltaY;//Math.Abs(deltaY);

                    selectionRegion = new Rect(x, y, w, h);
                }
                else
                {
                    if (DefaultSize.HasValue && updateType == UpdateMaskType.ForMouseLeftButtonUp)
                    {
                        isMaskDraging = true;

                        selectionRegion = new Rect(startPoint.X, startPoint.Y, DefaultSize.Value.Width, DefaultSize.Value.Height);
                    }
                    else
                    {
                        isMaskDraging = false;
                    }
                }
            }

            UpdateIndicator(selectionRegion);
        }

        internal void UpdateSelectionRegion(Rect region, bool flag = false)
        {
            selectionRegion = region;
            UpdateIndicator(selectionRegion);
            if (LoationChanged != null && flag)
            {
                LoationChanged(this, new LoactionArgs(region.Left/this.Width, region.Top/this.Height));
            }
        }


        private void FinishShowMask()
        {
            if (IsMouseCaptured)
            {
                ReleaseMouseCapture();
            }

            if (isMaskDraging)
            {

                UpdateIndicator(selectionRegion);

                ClearSelectionData();
            }
        }

        private void ClearSelectionData()
        {
            isMaskDraging = false;
            selectionBorder.Visibility = Visibility.Collapsed;
            selectionStartPoint = null;
            selectionEndPoint = null;
        }

        private void UpdateIndicator(Rect region)
        {
            if (indicator == null)
                return;

            if (region.Width < indicator.MinWidth || region.Height < indicator.MinHeight)
            {
                return;
            }
 indicator.Visibility = Visibility.Visible;
            indicator.Width = region.Width;
            indicator.Height = region.Height;
            SetLeft(indicator, region.Left);
            SetTop(indicator, region.Top);

           
        }

        private Rect GetIndicatorRegion()
        {
            return new Rect(GetLeft(indicator), GetTop(indicator), indicator.ActualWidth, indicator.ActualHeight);
        }

        #endregion

        #region Render

        private void OnCompositionTargetRendering(object sender, EventArgs e)
        {
            UpdateSelectionBorderLayout();
            UpdateMaskRectanglesLayout();
        }

        #endregion

        #region inner types

        private enum UpdateMaskType
        {
            ForMouseMoving,
            ForMouseLeftButtonUp
        }

        #endregion

    }
Canvas

缩略图很简单,按照比例缩放图片加载上即可

   thumbImage = m_Bitmap.GetThumbnailImage(thumbWidth, thumbHeight, null, IntPtr.Zero) as Bitmap;  //thumbWidth指定宽,thumbHeight指定高度
ThumbnailImage

然后我们为大图加上监听事件ScrollChanged和MouseWheel 以及MouseLeftButtonDown、MouseLeftButtonUp、 MouseMove

ScrollChanged用来计算显示的滚动区域范围

 if (e.ExtentHeight > e.ViewportHeight || e.ExtentWidth > e.ViewportWidth)
            {
                offsetX = (e.ExtentWidth - e.ViewportWidth) / 2;
                offsetY = (e.ExtentHeight - e.ViewportHeight) / 2;

                svImg.ScrollToVerticalOffset(offsetY);
                svImg.ScrollToHorizontalOffset(offsetX);

            }
            
  
            double timeH =  svImg.ViewportHeight/ (svImg.ViewportHeight + svImg.ScrollableHeight);
            double timeW = svImg.ViewportWidth / (svImg.ViewportWidth + svImg.ScrollableWidth);

            double w = thumbWidth * timeW;
            double h = thumbHeight * timeH;

            double offsetx = 0;
            double offsety = 0;
            if (svImg.ScrollableWidth == 0)
            {
                offsetx = 0;
            }
            else
            {
                offsetx = (w - thumbWidth) / svImg.ScrollableWidth * svImg.HorizontalOffset;
            }

            if (svImg.ScrollableHeight == 0)
            {
                offsety = 0;
            }
            else
            {
                offsety = (h - thumbHeight) / svImg.ScrollableHeight * svImg.VerticalOffset;
            }
           

            Rect rect = new Rect( - offsetx,  - offsety, w, h);

            mask.UpdateSelectionRegion(rect);
ScrollChanged

MouseWheel计算滚动比例

  var mosePos = e.GetPosition(img);

            scale = scale * (e.Delta > 0 ? 1.2 : 1 / 1.2);
            scale = Math.Max(scale, 0.15);
            scale = Math.Min(16, scale);
            
            this.txtZoom.Text = ((int)(scale * 100)).ToString();

            img.Width = scale * imgWidth;
            img.Height = scale * imgHeight;

            offsetX = svImg.ScrollableWidth / 2;
            offsetY = svImg.ScrollableHeight / 2;
MouseWheel

MouseLeftButtonDown后三个事件在移动图片时使用

MouseLeftButtonDown、MouseLeftButtonUp、MouseMove

 

以上大部分的工作已经做完了,然后我们加入一个定时器的功能,调节显示百分比的时间。加上一个Timer类即可。

当然在ScrolChanged里面我们加入了鹰眼监控,细心的朋友可以在事件里面看到。

posted on 2014-08-16 19:36  即使很偶然  阅读(3696)  评论(6编辑  收藏  举报