首页 > 代码库 > WPF下的仿QQ图片查看器

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 += onl oaded;        }        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 onl oaded(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 onm ouseLeftButtonDown(MouseButtonEventArgs e)        {            indicator.Visibility = System.Windows.Visibility.Visible;                     if (e.Source.Equals(indicator))            {                HandleIndicatorMouseDown(e);            }            base.OnMouseLeftButtonDown(e);        }        protected override void onm ouseMove(MouseEventArgs e)        {            if (IsMouseOnThis(e))            {                UpdateSelectionRegion(e, UpdateMaskType.ForMouseMoving);                e.Handled = true;            }            base.OnMouseMove(e);        }        protected override void onm ouseLeftButtonUp(MouseButtonEventArgs e)        {            if (IsMouseOnThis(e))            {                UpdateSelectionRegion(e, UpdateMaskType.ForMouseLeftButtonUp);                FinishShowMask();            }            base.OnMouseLeftButtonUp(e);        }        protected override void onm ouseRightButtonUp(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里面我们加入了鹰眼监控,细心的朋友可以在事件里面看到。