Click here to Skip to main content
15,891,597 members
Articles / Desktop Programming / WPF

C.B.R.

Rate me:
Please Sign up or sign in to vote.
4.96/5 (52 votes)
22 Oct 2012GPL329 min read 124.9K   1.8K   132  
Comic and electronic publication reader with library management, extended file conversion, and devices support.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using BookReader.Common;

namespace BookReader.Controls
{
    public enum AutoFit
    {
        None, Width, Height
    }

    /// <summary>
    /// Interaction logic for PageViewer.xaml
    /// </summary>
    public partial class PageViewer : UserControl
    {
        #region -----------------private and constructor-----------------

        public PageViewer()
        {
            InitializeComponent();
        }

		// should we wait at the end of the page that we press down one more time 
		// to go to the next page
        private bool WaitAtBottom = true;

        // zooming
        private ScaleTransform scaleTransform = new ScaleTransform();
        private double _scale = 1.0;
		private const int FIT_BORDER = 30;


        // moving the image
        private Point _mouseDragStartPoint;
        private Point _scrollStartOffset;

        #endregion

        #region -----------------Properties-----------------

        /// <summary>
        /// The zoom scale
        /// </summary>
        public AutoFit AutoFitMode
        {
            get { return (AutoFit)Properties.Settings.Default.UseAutoFit; }
        }


		/// <summary>
		/// The zoom scale
		/// </summary>
		public double Scale
        {
            get { return _scale; }
			set { _scale = value; UpdateScale(); }
        }

        /// <summary>
        /// The image to display
        /// </summary>
        public ImageSource Source
        {
            get { return this.PageImage.Source; }
            set { this.PageImage.Source = value; }
        }

		/// <summary>
		/// Scroll the page to the top
		/// </summary>
        public void ScrollToHome()
        {
            this.PageContent.ScrollToHome();
            if (AutoFitMode != AutoFit.None)
                Fit();
        }

		/// <summary>
		/// Scroll the page to the bottom
		/// </summary>
        public void ScrollToBottom()
        {
            this.PageContent.ScrollToBottom();
            if (AutoFitMode != AutoFit.None)
                Fit();
        }
		#endregion

		#region -----------------loading and custom event-----------------

		/// <summary>
		/// Init the controls
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void UserControl_Loaded(object sender, RoutedEventArgs e)
		{
			this.PageImage.LayoutTransform = this.scaleTransform;

			//set the content of magnifier
			this.Magnifier.ContentToDisplay = this.PageImage;

			this.PageContent.Focus();
		}

		/// <summary>
		/// Page registered event
		/// </summary>
		public static readonly RoutedEvent PageChangedEvent = EventManager.RegisterRoutedEvent("PageChangedEvent",
		RoutingStrategy.Bubble,
		typeof(PageChangedEventHandler), typeof(PageViewer));

        /// <summary>
		/// page changed event handler delegate
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void PageChangedEventHandler(object sender, PageRoutedEventArgs e);

		/// <summary>
		/// Page chaned event handler
		/// </summary>
        public event PageChangedEventHandler PageChanged
        {
            add { AddHandler(PageChangedEvent, value); }
            remove { RemoveHandler(PageChangedEvent, value); }
        }

		/// <summary>
		/// Raise the page changed event
		/// </summary>
		/// <param name="offset"></param>
        protected void RaisePageChanged(int offset)
        {
            PageRoutedEventArgs args = new PageRoutedEventArgs(offset);
            args.RoutedEvent = PageChangedEvent;
            RaiseEvent(args);
        }

        /// <summary>
        /// PageRoutedEventArgs : a custom event argument class
        /// </summary>
        public class PageRoutedEventArgs : RoutedEventArgs
        {
            private int _PageOffset;

            public PageRoutedEventArgs(int offset)
            {
                this._PageOffset = offset;
            }

            public int PageOffset
            {
                get { return _PageOffset; }
            }
        }

		/// <summary>
		/// Zoom registered event 
		/// </summary>
		public static readonly RoutedEvent ZoomChangedEvent = EventManager.RegisterRoutedEvent("ZoomChangedEvent",
																RoutingStrategy.Bubble,
																typeof(ZoomChangedEventHandler), typeof(PageViewer));

		/// <summary>
		/// the event handler delegate
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public delegate void ZoomChangedEventHandler(object sender, ZoomRoutedEventArgs e);

		/// <summary>
		/// zoom changed event handler
		/// </summary>
		public event ZoomChangedEventHandler ZoomChanged
		{
			add { AddHandler(ZoomChangedEvent, value); }
			remove { RemoveHandler(ZoomChangedEvent, value); }
		}

		/// <summary>
		/// Raise the zoom changed event
		/// </summary>
		protected void RaiseZoomChanged()
		{
			ZoomRoutedEventArgs args = new ZoomRoutedEventArgs(_scale);
			args.RoutedEvent = ZoomChangedEvent;
			RaiseEvent(args);
		}

		/// <summary>
		/// Zoom changed event arguments
		/// </summary>
		public class ZoomRoutedEventArgs : RoutedEventArgs
		{
			private double _Scale;

			public ZoomRoutedEventArgs(double scale)
			{
				this._Scale = scale;
			}

			public double Scale
			{
				get { return _Scale; }
			}
		}
		#endregion

		#region -----------------mouse events-----------------

		/// <summary>
        /// start moving the image
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PageContent_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //Start moving on the left button, let the right for popup menu
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                _mouseDragStartPoint = e.GetPosition(PageContent);
                _scrollStartOffset.X = PageContent.HorizontalOffset;
                _scrollStartOffset.Y = PageContent.VerticalOffset;

                // Update the cursor if scrolling is possible 
                PageImage.Cursor = (PageContent.ExtentWidth > PageContent.ViewportWidth) ||
                    (PageContent.ExtentHeight > PageContent.ViewportHeight) ?
                    Cursors.ScrollAll : Cursors.Arrow;

				this.PageImage.Focus();
				PageImage.CaptureMouse();
                e.Handled = true;
            }
        }

        /// <summary>
        /// update the magnifier or move/scroll the image
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PageContent_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            //manage the magnifier
            if (Keyboard.IsKeyDown(Key.LeftShift) )
            {
                Magnifier.Update(Mouse.GetPosition(PageViewerGrid));
                e.Handled = true;
            }
            else
                // else move the image
				if (PageImage.IsMouseCaptured)
                {
                    // Get the new mouse position. 
                    Point mouseDragCurrentPoint = e.GetPosition(PageContent);

                    // Determine the new amount to scroll. 
                    Point delta = new Point(
                        (mouseDragCurrentPoint.X > this._mouseDragStartPoint.X) ?
                        -(mouseDragCurrentPoint.X - this._mouseDragStartPoint.X) :
                        (this._mouseDragStartPoint.X - mouseDragCurrentPoint.X),
                        (mouseDragCurrentPoint.Y > this._mouseDragStartPoint.Y) ?
                        -(mouseDragCurrentPoint.Y - this._mouseDragStartPoint.Y) :
                        (this._mouseDragStartPoint.Y - mouseDragCurrentPoint.Y));

                    // Scroll to the new position. 
                    PageContent.ScrollToHorizontalOffset(this._scrollStartOffset.X + delta.X);
                    PageContent.ScrollToVerticalOffset(this._scrollStartOffset.Y + delta.Y);
                }
        }

        /// <summary>
        /// Release after image moves
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PageContent_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            //end image move
			if (PageImage.IsMouseCaptured)
            {
				PageImage.ReleaseMouseCapture();
				PageImage.Cursor = Cursors.Arrow;
            }
        }

        #endregion

        #region -----------------manage scrool, zoom and magnifier-----------------

		public void FitWidth()
		{
			_scale = (this.PageContent.ViewportWidth - FIT_BORDER) / this.PageImage.Source.Width;
			UpdateScale();
		}

		public void FitHeight()
		{
			_scale = (this.PageContent.ViewportHeight - FIT_BORDER) / this.PageImage.Source.Height;
			UpdateScale();
		}

        internal void Fit()
        {
            if (AutoFitMode == AutoFit.Height)
                FitHeight();
            else if (AutoFitMode == AutoFit.Width)
                FitWidth();
        }

		/// <summary>
		/// Update the zoom scale of the image control and raise the event
		/// </summary>
        private void UpdateScale()
        {
            this.scaleTransform.ScaleX = _scale;
            this.scaleTransform.ScaleY = _scale;

            this.scaleTransform.CenterX = 0.5;
            this.scaleTransform.CenterY = 0.5;

			RaiseZoomChanged();
        }

        /// <summary>
        /// Calculate the zoom scale of the page
        /// </summary>
        /// <param name="delta"></param>
        private void UpdateContent(bool delta)
        {
            _scale += delta ? 0.01 : -0.01;
            _scale = _scale < 0.01 ? 0.01 : _scale;
            _scale = _scale > 4 ? 4 : _scale;

			UpdateScale();
        }

        /// <summary>
        /// manage the zoom or scrolling
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PageContent_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            //zooming
            if (Keyboard.IsKeyDown(Key.LeftCtrl))
            {
				UpdateContent(e.Delta > 0);
                e.Handled = true;
            }
            else
            {
                if (e.Delta > 0)
                {
                    ManageScroolUp();
                }
                else
                {
                    ManageScroolDown();
                }
            }
        }

        /// <summary>
        /// Display the magnifier if Key.LeftShift
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PageContent_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.LeftShift)
            {
                Magnifier.Update(Mouse.GetPosition(PageViewerGrid));
                Magnifier.Display(Visibility.Visible);
				
				this.PageContent.CaptureMouse();

                e.Handled = true;
            }
        }

        /// <summary>
        /// manage the scroll or magnifier
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PageContent_PreviewKeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.LeftShift)
            {
                Magnifier.Display(Visibility.Hidden);
				
				this.PageContent.ReleaseMouseCapture();
				
				e.Handled = true;
                return;
            }

            if (e.Key == Key.PageDown || e.Key == Key.Down)
            {
                ManageScroolDown();
                e.Handled = true;
				return;
            }
            else if (e.Key == Key.PageUp || e.Key == Key.Up)
            {
                ManageScroolUp();
                e.Handled = true;
				return;
            }
        }

        /// <summary>
        /// Scrool up if at the top of the page
        /// </summary>
        private void ManageScroolUp()
        {
            try
            {
                if (this.PageContent.VerticalOffset == 0)
                {
                    RaisePageChanged(-1); ;
                }
            }
            catch (Exception err)
            {
				ExceptionManagement.Manage("PageViewer:ManageScroolUp", err);
            }
        }

        /// <summary>
        /// Scrool down if at the end of the page
        /// </summary>
        private void ManageScroolDown()
        {
            try
            {
                if (this.PageContent.VerticalOffset + this.PageContent.ViewportHeight >= this.PageContent.ExtentHeight)
                {
                    if (!WaitAtBottom)
                    {
                        WaitAtBottom = true;
                        return;
                    }
                    else WaitAtBottom = false;

                    RaisePageChanged(1);
				}
            }
            catch (Exception err)
            {
                ExceptionManagement.Manage("PageViewer:ManageScroolDown", err);
            }
        }
        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Architect
France France
WPF and MVVM fan, I practice C # in all its forms from the beginning of the NET Framework without mentioning C ++ / MFC and other software packages such as databases, ASP, WCF, Web & Windows services, Application, and now Core and UWP.
In my wasted hours, I am guilty of having fathered C.B.R. and its cousins C.B.R. for WinRT and UWP on the Windows store.
But apart from that, I am a great handyman ... the house, a rocket stove to heat the jacuzzi and the last one: a wood oven for pizza, bread, and everything that goes inside

https://guillaumewaser.wordpress.com/
https://fouretcompagnie.wordpress.com/

Comments and Discussions