ImageViewer.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993
  1. using System;
  2. using System.ComponentModel;
  3. using System.IO;
  4. using System.Windows;
  5. using System.Windows.Controls;
  6. using System.Windows.Data;
  7. using System.Windows.Input;
  8. using System.Windows.Markup;
  9. using System.Windows.Media;
  10. using System.Windows.Media.Animation;
  11. using System.Windows.Media.Imaging;
  12. using System.Windows.Threading;
  13. using HandyControl.Data;
  14. using HandyControl.Interactivity;
  15. using HandyControl.Properties.Langs;
  16. using HandyControl.Tools;
  17. using Microsoft.Win32;
  18. namespace HandyControl.Controls;
  19. [TemplatePart(Name = ElementPanelMain, Type = typeof(Panel))]
  20. [TemplatePart(Name = ElementCanvasSmallImg, Type = typeof(Canvas))]
  21. [TemplatePart(Name = ElementBorderMove, Type = typeof(Border))]
  22. [TemplatePart(Name = ElementBorderBottom, Type = typeof(Border))]
  23. [TemplatePart(Name = ElementImageMain, Type = typeof(Image))]
  24. public class ImageViewer : Control
  25. {
  26. #region Constants
  27. private const string ElementPanelMain = "PART_PanelMain";
  28. private const string ElementCanvasSmallImg = "PART_CanvasSmallImg";
  29. private const string ElementBorderMove = "PART_BorderMove";
  30. private const string ElementBorderBottom = "PART_BorderBottom";
  31. private const string ElementImageMain = "PART_ImageMain";
  32. /// <summary>
  33. /// 缩放比间隔
  34. /// </summary>
  35. private const double ScaleInternal = 0.2;
  36. #endregion Constants
  37. #region Data
  38. /// <summary>
  39. /// 图片保存对话框
  40. /// </summary>
  41. private static readonly SaveFileDialog SaveFileDialog = new()
  42. {
  43. Filter = $"{Lang.PngImg}|*.png"
  44. };
  45. private Panel _panelMain;
  46. private Canvas _canvasSmallImg;
  47. private Border _borderMove;
  48. private Border _borderBottom;
  49. private Image _imageMain;
  50. /// <summary>
  51. /// 右下角小图片是否加载过
  52. /// </summary>
  53. private bool _borderSmallIsLoaded;
  54. /// <summary>
  55. /// 图片是否可以在x轴方向移动
  56. /// </summary>
  57. private bool _canMoveX;
  58. /// <summary>
  59. /// 图片是否可以在y轴方向移动
  60. /// </summary>
  61. private bool _canMoveY;
  62. /// <summary>
  63. /// 图片实际位置
  64. /// </summary>
  65. private Thickness _imgActualMargin;
  66. /// <summary>
  67. /// 图片实际旋转角度
  68. /// </summary>
  69. private double _imgActualRotate;
  70. /// <summary>
  71. /// 图片实际旋缩放比
  72. /// </summary>
  73. private double _imgActualScale = 1;
  74. /// <summary>
  75. /// 在图片上鼠标移动时的即时位置
  76. /// </summary>
  77. private Point _imgCurrentPoint;
  78. /// <summary>
  79. /// 鼠标是否在图片上按下
  80. /// </summary>
  81. private bool _imgIsMouseDown;
  82. /// <summary>
  83. /// 在图片上按下时图片的位置
  84. /// </summary>
  85. private Thickness _imgMouseDownMargin;
  86. /// <summary>
  87. /// 在图片上按下时鼠标的位置
  88. /// </summary>
  89. private Point _imgMouseDownPoint;
  90. /// <summary>
  91. /// 在小图片上鼠标移动时的即时位置
  92. /// </summary>
  93. private Point _imgSmallCurrentPoint;
  94. /// <summary>
  95. /// 鼠标是否在小图片上按下
  96. /// </summary>
  97. private bool _imgSmallIsMouseDown;
  98. /// <summary>
  99. /// 在小图片上按下时图片的位置
  100. /// </summary>
  101. private Thickness _imgSmallMouseDownMargin;
  102. /// <summary>
  103. /// 在小图片上按下时鼠标的位置
  104. /// </summary>
  105. private Point _imgSmallMouseDownPoint;
  106. /// <summary>
  107. /// 图片长宽比
  108. /// </summary>
  109. private double _imgWidHeiScale;
  110. /// <summary>
  111. /// 图片是否倾斜
  112. /// </summary>
  113. private bool _isOblique;
  114. /// <summary>
  115. /// 缩放高度间隔
  116. /// </summary>
  117. private double _scaleInternalHeight;
  118. /// <summary>
  119. /// 缩放宽度间隔
  120. /// </summary>
  121. private double _scaleInternalWidth;
  122. /// <summary>
  123. /// 底部BorderBottom(包含一些图片操作)是否显示中
  124. /// </summary>
  125. private bool _showBorderBottom;
  126. private DispatcherTimer _dispatcher;
  127. private bool _isLoaded;
  128. private MouseBinding _mouseMoveBinding;
  129. private ImageBrowser _imageBrowser;
  130. #endregion Data
  131. #region ctor
  132. public ImageViewer()
  133. {
  134. CommandBindings.Add(new CommandBinding(ControlCommands.Save, ButtonSave_OnClick));
  135. CommandBindings.Add(new CommandBinding(ControlCommands.Open, ButtonWindowsOpen_OnClick));
  136. CommandBindings.Add(new CommandBinding(ControlCommands.Restore, ButtonActual_OnClick));
  137. CommandBindings.Add(new CommandBinding(ControlCommands.Reduce, ButtonReduce_OnClick));
  138. CommandBindings.Add(new CommandBinding(ControlCommands.Enlarge, ButtonEnlarge_OnClick));
  139. CommandBindings.Add(new CommandBinding(ControlCommands.RotateLeft, ButtonRotateLeft_OnClick));
  140. CommandBindings.Add(new CommandBinding(ControlCommands.RotateRight, ButtonRotateRight_OnClick));
  141. CommandBindings.Add(new CommandBinding(ControlCommands.MouseMove, ImageMain_OnMouseDown));
  142. OnMoveGestureChanged(MoveGesture);
  143. Loaded += (s, e) =>
  144. {
  145. _isLoaded = true;
  146. Init();
  147. };
  148. }
  149. /// <summary>
  150. /// 带一个图片Uri的构造函数
  151. /// </summary>
  152. /// <param name="uri"></param>
  153. public ImageViewer(Uri uri) : this()
  154. {
  155. Uri = uri;
  156. }
  157. /// <summary>
  158. /// 带一个图片路径的构造函数
  159. /// </summary>
  160. /// <param name="path"></param>
  161. public ImageViewer(string path) : this(new Uri(path))
  162. {
  163. }
  164. #endregion
  165. #region Properties
  166. public static readonly DependencyProperty ShowImgMapProperty = DependencyProperty.Register(
  167. nameof(ShowImgMap), typeof(bool), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.FalseBox));
  168. public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register(
  169. nameof(ImageSource), typeof(BitmapFrame), typeof(ImageViewer), new PropertyMetadata(default(BitmapFrame), OnImageSourceChanged));
  170. public static readonly DependencyProperty UriProperty = DependencyProperty.Register(
  171. nameof(Uri), typeof(Uri), typeof(ImageViewer), new PropertyMetadata(default(Uri), OnUriChanged));
  172. public static readonly DependencyProperty ShowToolBarProperty = DependencyProperty.Register(
  173. nameof(ShowToolBar), typeof(bool), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.TrueBox));
  174. public static readonly DependencyProperty IsFullScreenProperty = DependencyProperty.Register(
  175. nameof(IsFullScreen), typeof(bool), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.FalseBox));
  176. public static readonly DependencyProperty MoveGestureProperty = DependencyProperty.Register(
  177. nameof(MoveGesture), typeof(MouseGesture), typeof(ImageViewer), new UIPropertyMetadata(new MouseGesture(MouseAction.LeftClick), OnMoveGestureChanged));
  178. internal static readonly DependencyProperty ImgPathProperty = DependencyProperty.Register(
  179. nameof(ImgPath), typeof(string), typeof(ImageViewer), new PropertyMetadata(default(string)));
  180. internal static readonly DependencyProperty ImgSizeProperty = DependencyProperty.Register(
  181. nameof(ImgSize), typeof(long), typeof(ImageViewer), new PropertyMetadata(-1L));
  182. internal static readonly DependencyProperty ShowFullScreenButtonProperty = DependencyProperty.Register(
  183. nameof(ShowFullScreenButton), typeof(bool), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.FalseBox));
  184. internal static readonly DependencyProperty ShowCloseButtonProperty = DependencyProperty.Register(
  185. nameof(ShowCloseButton), typeof(bool), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.FalseBox));
  186. internal static readonly DependencyProperty ImageContentProperty = DependencyProperty.Register(
  187. nameof(ImageContent), typeof(object), typeof(ImageViewer), new PropertyMetadata(default(object)));
  188. internal static readonly DependencyProperty ImageMarginProperty = DependencyProperty.Register(
  189. nameof(ImageMargin), typeof(Thickness), typeof(ImageViewer), new PropertyMetadata(default(Thickness)));
  190. internal static readonly DependencyProperty ImageWidthProperty = DependencyProperty.Register(
  191. nameof(ImageWidth), typeof(double), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.Double0Box));
  192. internal static readonly DependencyProperty ImageHeightProperty = DependencyProperty.Register(
  193. nameof(ImageHeight), typeof(double), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.Double0Box));
  194. internal static readonly DependencyProperty ImageScaleProperty = DependencyProperty.Register(
  195. nameof(ImageScale), typeof(double), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.Double1Box, OnImageScaleChanged));
  196. internal static readonly DependencyProperty ScaleStrProperty = DependencyProperty.Register(
  197. nameof(ScaleStr), typeof(string), typeof(ImageViewer), new PropertyMetadata("100%"));
  198. internal static readonly DependencyProperty ImageRotateProperty = DependencyProperty.Register(
  199. nameof(ImageRotate), typeof(double), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.Double0Box));
  200. internal static readonly DependencyProperty ShowSmallImgInternalProperty = DependencyProperty.Register(
  201. nameof(ShowSmallImgInternal), typeof(bool), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.FalseBox));
  202. public bool IsFullScreen
  203. {
  204. get => (bool) GetValue(IsFullScreenProperty);
  205. set => SetValue(IsFullScreenProperty, ValueBoxes.BooleanBox(value));
  206. }
  207. [ValueSerializer(typeof(MouseGestureValueSerializer))]
  208. [TypeConverter(typeof(MouseGestureConverter))]
  209. public MouseGesture MoveGesture
  210. {
  211. get => (MouseGesture) GetValue(MoveGestureProperty);
  212. set => SetValue(MoveGestureProperty, value);
  213. }
  214. public bool ShowImgMap
  215. {
  216. get => (bool) GetValue(ShowImgMapProperty);
  217. set => SetValue(ShowImgMapProperty, ValueBoxes.BooleanBox(value));
  218. }
  219. public BitmapFrame ImageSource
  220. {
  221. get => (BitmapFrame) GetValue(ImageSourceProperty);
  222. set => SetValue(ImageSourceProperty, value);
  223. }
  224. public Uri Uri
  225. {
  226. get => (Uri) GetValue(UriProperty);
  227. set => SetValue(UriProperty, value);
  228. }
  229. public bool ShowToolBar
  230. {
  231. get => (bool) GetValue(ShowToolBarProperty);
  232. set => SetValue(ShowToolBarProperty, ValueBoxes.BooleanBox(value));
  233. }
  234. internal object ImageContent
  235. {
  236. get => GetValue(ImageContentProperty);
  237. set => SetValue(ImageContentProperty, value);
  238. }
  239. internal string ImgPath
  240. {
  241. get => (string) GetValue(ImgPathProperty);
  242. set => SetValue(ImgPathProperty, value);
  243. }
  244. internal long ImgSize
  245. {
  246. get => (long) GetValue(ImgSizeProperty);
  247. set => SetValue(ImgSizeProperty, value);
  248. }
  249. /// <summary>
  250. /// 是否显示全屏按钮
  251. /// </summary>
  252. internal bool ShowFullScreenButton
  253. {
  254. get => (bool) GetValue(ShowFullScreenButtonProperty);
  255. set => SetValue(ShowFullScreenButtonProperty, ValueBoxes.BooleanBox(value));
  256. }
  257. internal Thickness ImageMargin
  258. {
  259. get => (Thickness) GetValue(ImageMarginProperty);
  260. set => SetValue(ImageMarginProperty, value);
  261. }
  262. internal double ImageWidth
  263. {
  264. get => (double) GetValue(ImageWidthProperty);
  265. set => SetValue(ImageWidthProperty, value);
  266. }
  267. internal double ImageHeight
  268. {
  269. get => (double) GetValue(ImageHeightProperty);
  270. set => SetValue(ImageHeightProperty, value);
  271. }
  272. internal double ImageScale
  273. {
  274. get => (double) GetValue(ImageScaleProperty);
  275. set => SetValue(ImageScaleProperty, value);
  276. }
  277. internal string ScaleStr
  278. {
  279. get => (string) GetValue(ScaleStrProperty);
  280. set => SetValue(ScaleStrProperty, value);
  281. }
  282. internal double ImageRotate
  283. {
  284. get => (double) GetValue(ImageRotateProperty);
  285. set => SetValue(ImageRotateProperty, value);
  286. }
  287. internal bool ShowSmallImgInternal
  288. {
  289. get => (bool) GetValue(ShowSmallImgInternalProperty);
  290. set => SetValue(ShowSmallImgInternalProperty, ValueBoxes.BooleanBox(value));
  291. }
  292. /// <summary>
  293. /// 图片原始宽度
  294. /// </summary>
  295. private double ImageOriWidth { get; set; }
  296. /// <summary>
  297. /// 图片原始高度
  298. /// </summary>
  299. private double ImageOriHeight { get; set; }
  300. /// <summary>
  301. /// 关闭按钮是否显示中
  302. /// </summary>
  303. internal bool ShowCloseButton
  304. {
  305. get => (bool) GetValue(ShowCloseButtonProperty);
  306. set => SetValue(ShowCloseButtonProperty, ValueBoxes.BooleanBox(value));
  307. }
  308. /// <summary>
  309. /// 底部BorderBottom(包含一些图片操作)是否显示中
  310. /// </summary>
  311. internal bool ShowBorderBottom
  312. {
  313. get => _showBorderBottom;
  314. set
  315. {
  316. if (_showBorderBottom == value) return;
  317. _borderBottom?.BeginAnimation(OpacityProperty,
  318. value ? AnimationHelper.CreateAnimation(1, 100) : AnimationHelper.CreateAnimation(0, 400));
  319. _showBorderBottom = value;
  320. }
  321. }
  322. #endregion
  323. public override void OnApplyTemplate()
  324. {
  325. if (_canvasSmallImg != null)
  326. {
  327. _canvasSmallImg.MouseLeftButtonDown -= CanvasSmallImg_OnMouseLeftButtonDown;
  328. _canvasSmallImg.MouseLeftButtonUp -= CanvasSmallImg_OnMouseLeftButtonUp;
  329. _canvasSmallImg.MouseMove -= CanvasSmallImg_OnMouseMove;
  330. }
  331. base.OnApplyTemplate();
  332. _panelMain = GetTemplateChild(ElementPanelMain) as Panel;
  333. _canvasSmallImg = GetTemplateChild(ElementCanvasSmallImg) as Canvas;
  334. _borderMove = GetTemplateChild(ElementBorderMove) as Border;
  335. _imageMain = GetTemplateChild(ElementImageMain) as Image;
  336. _borderBottom = GetTemplateChild(ElementBorderBottom) as Border;
  337. if (_imageMain != null)
  338. {
  339. var t = new RotateTransform();
  340. BindingOperations.SetBinding(t, RotateTransform.AngleProperty, new Binding(ImageRotateProperty.Name) { Source = this });
  341. _imageMain.LayoutTransform = t;
  342. }
  343. if (_canvasSmallImg != null)
  344. {
  345. _canvasSmallImg.MouseLeftButtonDown += CanvasSmallImg_OnMouseLeftButtonDown;
  346. _canvasSmallImg.MouseLeftButtonUp += CanvasSmallImg_OnMouseLeftButtonUp;
  347. _canvasSmallImg.MouseMove += CanvasSmallImg_OnMouseMove;
  348. }
  349. _borderSmallIsLoaded = false;
  350. }
  351. private static void OnImageScaleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  352. {
  353. if (d is ImageViewer imageViewer && e.NewValue is double newValue)
  354. {
  355. imageViewer.ImageWidth = imageViewer.ImageOriWidth * newValue;
  356. imageViewer.ImageHeight = imageViewer.ImageOriHeight * newValue;
  357. imageViewer.ScaleStr = $"{newValue * 100:#0}%";
  358. }
  359. }
  360. /// <summary>
  361. /// 初始化
  362. /// </summary>
  363. private void Init()
  364. {
  365. if (ImageSource == null || !_isLoaded) return;
  366. if (ImageSource.IsDownloading)
  367. {
  368. _dispatcher = new DispatcherTimer(DispatcherPriority.ApplicationIdle)
  369. {
  370. Interval = TimeSpan.FromSeconds(1)
  371. };
  372. _dispatcher.Tick += Dispatcher_Tick;
  373. _dispatcher.Start();
  374. return;
  375. }
  376. double width;
  377. double height;
  378. if (!_isOblique)
  379. {
  380. width = ImageSource.PixelWidth;
  381. height = ImageSource.PixelHeight;
  382. }
  383. else
  384. {
  385. width = ImageSource.PixelHeight;
  386. height = ImageSource.PixelWidth;
  387. }
  388. ImageWidth = width;
  389. ImageHeight = height;
  390. ImageOriWidth = width;
  391. ImageOriHeight = height;
  392. _scaleInternalWidth = ImageOriWidth * ScaleInternal;
  393. _scaleInternalHeight = ImageOriHeight * ScaleInternal;
  394. if (Math.Abs(height - 0) < 0.001 || Math.Abs(width - 0) < 0.001)
  395. {
  396. MessageBox.Show(Lang.ErrorImgSize);
  397. return;
  398. }
  399. _imgWidHeiScale = width / height;
  400. var scaleWindow = ActualWidth / ActualHeight;
  401. ImageScale = 1;
  402. if (_imgWidHeiScale > scaleWindow)
  403. {
  404. if (width > ActualWidth)
  405. {
  406. ImageScale = ActualWidth / width;
  407. }
  408. }
  409. else if (height > ActualHeight)
  410. {
  411. ImageScale = ActualHeight / height;
  412. }
  413. ImageMargin = new Thickness((ActualWidth - ImageWidth) / 2, (ActualHeight - ImageHeight) / 2, 0, 0);
  414. _imgActualScale = ImageScale;
  415. _imgActualMargin = ImageMargin;
  416. InitBorderSmall();
  417. }
  418. private void Dispatcher_Tick(object sender, EventArgs e)
  419. {
  420. if (_dispatcher == null) return;
  421. if (ImageSource == null || !_isLoaded)
  422. {
  423. _dispatcher.Stop();
  424. _dispatcher.Tick -= Dispatcher_Tick;
  425. _dispatcher = null;
  426. return;
  427. }
  428. if (!ImageSource.IsDownloading)
  429. {
  430. _dispatcher.Stop();
  431. _dispatcher.Tick -= Dispatcher_Tick;
  432. _dispatcher = null;
  433. Init();
  434. }
  435. }
  436. private void ButtonActual_OnClick(object sender, RoutedEventArgs e)
  437. {
  438. var scaleAnimation = AnimationHelper.CreateAnimation(1);
  439. scaleAnimation.FillBehavior = FillBehavior.Stop;
  440. _imgActualScale = 1;
  441. scaleAnimation.Completed += (s, e1) =>
  442. {
  443. ImageScale = 1;
  444. _canMoveX = ImageWidth > ActualWidth;
  445. _canMoveY = ImageHeight > ActualHeight;
  446. BorderSmallShowSwitch();
  447. };
  448. var thickness = new Thickness((ActualWidth - ImageOriWidth) / 2, (ActualHeight - ImageOriHeight) / 2, 0, 0);
  449. var marginAnimation = AnimationHelper.CreateAnimation(thickness);
  450. marginAnimation.FillBehavior = FillBehavior.Stop;
  451. _imgActualMargin = thickness;
  452. marginAnimation.Completed += (s, e1) => { ImageMargin = thickness; };
  453. BeginAnimation(ImageScaleProperty, scaleAnimation);
  454. BeginAnimation(ImageMarginProperty, marginAnimation);
  455. }
  456. private void ButtonReduce_OnClick(object sender, RoutedEventArgs e) => ScaleImg(false);
  457. private void ButtonEnlarge_OnClick(object sender, RoutedEventArgs e) => ScaleImg(true);
  458. private void ButtonRotateLeft_OnClick(object sender, RoutedEventArgs e) => RotateImg(_imgActualRotate - 90);
  459. private void ButtonRotateRight_OnClick(object sender, RoutedEventArgs e) => RotateImg(_imgActualRotate + 90);
  460. private void ButtonSave_OnClick(object sender, RoutedEventArgs e)
  461. {
  462. if (ImageSource == null) return;
  463. SaveFileDialog.FileName = $"{DateTime.Now:yyyy-M-d-h-m-s.fff}";
  464. if (SaveFileDialog.ShowDialog() == true)
  465. {
  466. var path = SaveFileDialog.FileName;
  467. using var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
  468. var encoder = new PngBitmapEncoder();
  469. encoder.Frames.Add(BitmapFrame.Create(ImageSource));
  470. encoder.Save(fileStream);
  471. }
  472. }
  473. private void ButtonWindowsOpen_OnClick(object sender, RoutedEventArgs e)
  474. {
  475. if (Uri is { } uri)
  476. {
  477. _imageBrowser?.Close();
  478. _imageBrowser = new ImageBrowser(uri);
  479. _imageBrowser.Show();
  480. }
  481. }
  482. protected override void OnMouseMove(MouseEventArgs e) => MoveImg();
  483. protected override void OnMouseLeave(MouseEventArgs e) => ShowBorderBottom = false;
  484. protected override void OnMouseWheel(MouseWheelEventArgs e) => ScaleImg(e.Delta > 0);
  485. protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
  486. {
  487. base.OnRenderSizeChanged(sizeInfo);
  488. OnRenderSizeChanged();
  489. }
  490. private void OnRenderSizeChanged()
  491. {
  492. if (ImageWidth < 0.001 || ImageHeight < 0.001) return;
  493. _canMoveX = true;
  494. _canMoveY = true;
  495. var marginX = ImageMargin.Left;
  496. var marginY = ImageMargin.Top;
  497. if (ImageWidth <= ActualWidth)
  498. {
  499. _canMoveX = false;
  500. marginX = (ActualWidth - ImageWidth) / 2;
  501. }
  502. if (ImageHeight <= ActualHeight)
  503. {
  504. _canMoveY = false;
  505. marginY = (ActualHeight - ImageHeight) / 2;
  506. }
  507. ImageMargin = new Thickness(marginX, marginY, 0, 0);
  508. _imgActualMargin = ImageMargin;
  509. BorderSmallShowSwitch();
  510. _imgSmallMouseDownMargin = _borderMove.Margin;
  511. MoveSmallImg(_imgSmallMouseDownMargin.Left, _imgSmallMouseDownMargin.Top);
  512. }
  513. private void ImageMain_OnMouseDown(object sender, ExecutedRoutedEventArgs e)
  514. {
  515. _imgMouseDownPoint = Mouse.GetPosition(_panelMain);
  516. _imgMouseDownMargin = ImageMargin;
  517. _imgIsMouseDown = true;
  518. }
  519. protected override void OnPreviewMouseUp(MouseButtonEventArgs e) => _imgIsMouseDown = false;
  520. /// <summary>
  521. /// 右下角小图片显示切换
  522. /// </summary>
  523. private void BorderSmallShowSwitch()
  524. {
  525. if (_canMoveX || _canMoveY)
  526. {
  527. if (!_borderSmallIsLoaded)
  528. {
  529. _canvasSmallImg.Background = new VisualBrush(_imageMain);
  530. InitBorderSmall();
  531. _borderSmallIsLoaded = true;
  532. }
  533. ShowSmallImgInternal = true;
  534. UpdateBorderSmall();
  535. }
  536. else
  537. {
  538. ShowSmallImgInternal = false;
  539. }
  540. }
  541. /// <summary>
  542. /// 初始化右下角小图片
  543. /// </summary>
  544. private void InitBorderSmall()
  545. {
  546. if (_canvasSmallImg == null) return;
  547. var scaleWindow = _canvasSmallImg.MaxWidth / _canvasSmallImg.MaxHeight;
  548. if (_imgWidHeiScale > scaleWindow)
  549. {
  550. _canvasSmallImg.Width = _canvasSmallImg.MaxWidth;
  551. _canvasSmallImg.Height = _canvasSmallImg.Width / _imgWidHeiScale;
  552. }
  553. else
  554. {
  555. _canvasSmallImg.Width = _canvasSmallImg.MaxHeight * _imgWidHeiScale;
  556. _canvasSmallImg.Height = _canvasSmallImg.MaxHeight;
  557. }
  558. }
  559. /// <summary>
  560. /// 更新右下角小图片
  561. /// </summary>
  562. private void UpdateBorderSmall()
  563. {
  564. if (!ShowSmallImgInternal) return;
  565. var widthMin = Math.Min(ImageWidth, ActualWidth);
  566. var heightMin = Math.Min(ImageHeight, ActualHeight);
  567. _borderMove.Width = widthMin / ImageWidth * _canvasSmallImg.Width;
  568. _borderMove.Height = heightMin / ImageHeight * _canvasSmallImg.Height;
  569. var marginX = -ImageMargin.Left / ImageWidth * _canvasSmallImg.Width;
  570. var marginY = -ImageMargin.Top / ImageHeight * _canvasSmallImg.Height;
  571. var marginXMax = _canvasSmallImg.Width - _borderMove.Width;
  572. var marginYMax = _canvasSmallImg.Height - _borderMove.Height;
  573. marginX = Math.Max(0, marginX);
  574. marginX = Math.Min(marginXMax, marginX);
  575. marginY = Math.Max(0, marginY);
  576. marginY = Math.Min(marginYMax, marginY);
  577. _borderMove.Margin = new Thickness(marginX, marginY, 0, 0);
  578. }
  579. /// <summary>
  580. /// 缩放图片
  581. /// </summary>
  582. /// <param name="isEnlarge"></param>
  583. private void ScaleImg(bool isEnlarge)
  584. {
  585. if (Mouse.LeftButton == MouseButtonState.Pressed) return;
  586. var oldImageWidth = ImageWidth;
  587. var olgImageHeight = ImageHeight;
  588. var tempScale = isEnlarge ? _imgActualScale + ScaleInternal : _imgActualScale - ScaleInternal;
  589. if (Math.Abs(tempScale) < ScaleInternal)
  590. {
  591. tempScale = ScaleInternal;
  592. }
  593. else if (Math.Abs(tempScale) > 50)
  594. {
  595. tempScale = 50;
  596. }
  597. ImageScale = tempScale;
  598. var posCanvas = Mouse.GetPosition(_panelMain);
  599. var posImg = new Point(posCanvas.X - _imgActualMargin.Left, posCanvas.Y - _imgActualMargin.Top);
  600. var marginX = .5 * _scaleInternalWidth;
  601. var marginY = .5 * _scaleInternalHeight;
  602. if (ImageWidth > ActualWidth)
  603. {
  604. _canMoveX = true;
  605. if (ImageHeight > ActualHeight)
  606. {
  607. _canMoveY = true;
  608. marginX = posImg.X / oldImageWidth * _scaleInternalWidth;
  609. marginY = posImg.Y / olgImageHeight * _scaleInternalHeight;
  610. }
  611. else
  612. {
  613. _canMoveY = false;
  614. }
  615. }
  616. else
  617. {
  618. _canMoveY = ImageHeight > ActualHeight;
  619. _canMoveX = false;
  620. }
  621. Thickness thickness;
  622. if (isEnlarge)
  623. {
  624. thickness = new Thickness(_imgActualMargin.Left - marginX, _imgActualMargin.Top - marginY, 0, 0);
  625. }
  626. else
  627. {
  628. var marginActualX = _imgActualMargin.Left + marginX;
  629. var marginActualY = _imgActualMargin.Top + marginY;
  630. var subX = ImageWidth - ActualWidth;
  631. var subY = ImageHeight - ActualHeight;
  632. var right = Math.Abs(_borderMove.Width - _canvasSmallImg.ActualWidth + _borderMove.Margin.Left);
  633. var top = Math.Abs(_borderMove.Height - _canvasSmallImg.ActualHeight + _borderMove.Margin.Top);
  634. if (Math.Abs(ImageMargin.Left) < 0.001 || right < 0.001)
  635. marginActualX = _imgActualMargin.Left + _borderMove.Margin.Left /
  636. (_canvasSmallImg.ActualWidth - _borderMove.Width) * _scaleInternalWidth;
  637. if (Math.Abs(ImageMargin.Top) < 0.001 || top < 0.001)
  638. marginActualY = _imgActualMargin.Top + _borderMove.Margin.Top /
  639. (_canvasSmallImg.ActualHeight - _borderMove.Height) * _scaleInternalHeight;
  640. if (subX < 0.001) marginActualX = (ActualWidth - ImageWidth) / 2;
  641. if (subY < 0.001) marginActualY = (ActualHeight - ImageHeight) / 2;
  642. thickness = new Thickness(marginActualX, marginActualY, 0, 0);
  643. }
  644. ImageMargin = thickness;
  645. _imgActualScale = tempScale;
  646. _imgActualMargin = thickness;
  647. BorderSmallShowSwitch();
  648. _imgSmallMouseDownMargin = _borderMove.Margin;
  649. MoveSmallImg(_imgSmallMouseDownMargin.Left, _imgSmallMouseDownMargin.Top);
  650. }
  651. /// <summary>
  652. /// 旋转图片
  653. /// </summary>
  654. /// <param name="rotate"></param>
  655. private void RotateImg(double rotate)
  656. {
  657. _imgActualRotate = rotate;
  658. _isOblique = ((int) _imgActualRotate - 90) % 180 == 0;
  659. ShowSmallImgInternal = false;
  660. Init();
  661. InitBorderSmall();
  662. var animation = AnimationHelper.CreateAnimation(rotate);
  663. animation.Completed += (s, e1) => { ImageRotate = rotate; };
  664. animation.FillBehavior = FillBehavior.Stop;
  665. BeginAnimation(ImageRotateProperty, animation);
  666. }
  667. private MouseButtonState GetMouseButtonState() => MoveGesture.MouseAction switch
  668. {
  669. MouseAction.LeftClick => Mouse.LeftButton,
  670. MouseAction.RightClick => Mouse.RightButton,
  671. MouseAction.MiddleClick => Mouse.MiddleButton,
  672. _ => Mouse.LeftButton
  673. };
  674. /// <summary>
  675. /// 移动图片
  676. /// </summary>
  677. private void MoveImg()
  678. {
  679. _imgCurrentPoint = Mouse.GetPosition(_panelMain);
  680. ShowCloseButton = _imgCurrentPoint.Y < 200;
  681. ShowBorderBottom = _imgCurrentPoint.Y > ActualHeight - 200;
  682. if (GetMouseButtonState() == MouseButtonState.Released)
  683. {
  684. return;
  685. }
  686. if (_imgIsMouseDown)
  687. {
  688. var subX = _imgCurrentPoint.X - _imgMouseDownPoint.X;
  689. var subY = _imgCurrentPoint.Y - _imgMouseDownPoint.Y;
  690. var marginX = _imgMouseDownMargin.Left;
  691. if (ImageWidth > ActualWidth)
  692. {
  693. marginX = _imgMouseDownMargin.Left + subX;
  694. if (marginX >= 0)
  695. marginX = 0;
  696. else if (-marginX + ActualWidth >= ImageWidth) marginX = ActualWidth - ImageWidth;
  697. _canMoveX = true;
  698. }
  699. var marginY = _imgMouseDownMargin.Top;
  700. if (ImageHeight > ActualHeight)
  701. {
  702. marginY = _imgMouseDownMargin.Top + subY;
  703. if (marginY >= 0)
  704. marginY = 0;
  705. else if (-marginY + ActualHeight >= ImageHeight) marginY = ActualHeight - ImageHeight;
  706. _canMoveY = true;
  707. }
  708. ImageMargin = new Thickness(marginX, marginY, 0, 0);
  709. _imgActualMargin = ImageMargin;
  710. UpdateBorderSmall();
  711. }
  712. }
  713. /// <summary>
  714. /// 移动小图片
  715. /// </summary>
  716. private void MoveSmallImg()
  717. {
  718. if (!_imgSmallIsMouseDown)
  719. {
  720. return;
  721. }
  722. if (GetMouseButtonState() == MouseButtonState.Released)
  723. {
  724. return;
  725. }
  726. _imgSmallCurrentPoint = Mouse.GetPosition(_canvasSmallImg);
  727. var subX = _imgSmallCurrentPoint.X - _imgSmallMouseDownPoint.X;
  728. var subY = _imgSmallCurrentPoint.Y - _imgSmallMouseDownPoint.Y;
  729. var marginX = _imgSmallMouseDownMargin.Left + subX;
  730. var marginY = _imgSmallMouseDownMargin.Top + subY;
  731. MoveSmallImg(marginX, marginY);
  732. }
  733. private void MoveSmallImg(double marginX, double marginY)
  734. {
  735. if (marginX < 0)
  736. marginX = 0;
  737. else if (marginX + _borderMove.Width >= _canvasSmallImg.Width)
  738. marginX = _canvasSmallImg.Width - _borderMove.Width;
  739. if (marginY < 0)
  740. marginY = 0;
  741. else if (marginY + _borderMove.Height >= _canvasSmallImg.Height)
  742. marginY = _canvasSmallImg.Height - _borderMove.Height;
  743. _borderMove.Margin = new Thickness(marginX, marginY, 0, 0);
  744. var marginActualX = (ActualWidth - ImageWidth) / 2;
  745. var marginActualY = (ActualHeight - ImageHeight) / 2;
  746. if (_canMoveX) marginActualX = -marginX / _canvasSmallImg.Width * ImageWidth;
  747. if (_canMoveY) marginActualY = -marginY / _canvasSmallImg.Height * ImageHeight;
  748. ImageMargin = new Thickness(marginActualX, marginActualY, 0, 0);
  749. _imgActualMargin = ImageMargin;
  750. }
  751. private void CanvasSmallImg_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  752. {
  753. _imgSmallMouseDownPoint = Mouse.GetPosition(_canvasSmallImg);
  754. _imgSmallMouseDownMargin = _borderMove.Margin;
  755. _imgSmallIsMouseDown = true;
  756. e.Handled = true;
  757. }
  758. private void CanvasSmallImg_OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e) => _imgSmallIsMouseDown = false;
  759. private void CanvasSmallImg_OnMouseMove(object sender, MouseEventArgs e) => MoveSmallImg();
  760. private static void OnMoveGestureChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  761. {
  762. ((ImageViewer) d).OnMoveGestureChanged((MouseGesture) e.NewValue);
  763. }
  764. private void OnMoveGestureChanged(MouseGesture newValue)
  765. {
  766. InputBindings.Remove(_mouseMoveBinding);
  767. _mouseMoveBinding = new MouseBinding(ControlCommands.MouseMove, newValue);
  768. InputBindings.Add(_mouseMoveBinding);
  769. }
  770. private static void OnImageSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  771. {
  772. ((ImageViewer) d).OnImageSourceChanged();
  773. }
  774. private void OnImageSourceChanged()
  775. {
  776. Init();
  777. }
  778. private static void OnUriChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  779. {
  780. ((ImageViewer) d).OnUriChanged((Uri) e.NewValue);
  781. }
  782. private void OnUriChanged(Uri newValue)
  783. {
  784. if (newValue is not null)
  785. {
  786. ImageSource = GetBitmapFrame(newValue);
  787. ImgPath = newValue.AbsolutePath;
  788. if (File.Exists(ImgPath))
  789. {
  790. ImgSize = new FileInfo(ImgPath).Length;
  791. }
  792. }
  793. else
  794. {
  795. ImageSource = null;
  796. ImgPath = string.Empty;
  797. }
  798. static BitmapFrame GetBitmapFrame(Uri source)
  799. {
  800. try
  801. {
  802. return BitmapFrame.Create(source);
  803. }
  804. catch
  805. {
  806. return null;
  807. }
  808. }
  809. }
  810. }