wpf 可縮放,可顯示遠程圖片的Image
阿新 • • 發佈:2017-09-29
bsp bin eas tel borde med urn art ride
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net; using System.Net.Cache; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks;using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; namespace Controls { [TemplatePart(Name = PART_Image, Type = typeof(Image))] public class ScalableImage : UserControl { #regionFields private const string PART_Image = "PART_Image"; #endregion private ScaleTransform _scaleTransform; private TranslateTransform _translateTransform; private TransformGroup _transfromGroup; private Image _image; Point _start; Point _origin;/// <summary> /// 父容器 /// </summary> private IInputElement _parentElement; static ScalableImage() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ScalableImage), new FrameworkPropertyMetadata(typeof(ScalableImage))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); _image = GetTemplateChild(PART_Image) as Image; _image.RenderTransform = _transfromGroup; } public ScalableImage() { Init(); } private void Init() { _transfromGroup = new TransformGroup(); _scaleTransform = new ScaleTransform(); _translateTransform = new TranslateTransform(); _transfromGroup.Children.Add(_scaleTransform); _transfromGroup.Children.Add(_translateTransform); } protected override void OnRender(DrawingContext dc) { base.OnRender(dc); _parentElement = _image.Parent as IInputElement; } public string ImageUrl { get { return (string)GetValue(ImageUrlProperty); } set { SetValue(ImageUrlProperty, value); } } /// <summary> /// 圖片數據源 /// </summary> public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(ImageSource), typeof(ScalableImage), new PropertyMetadata(null, SourceChangedCallback)); /// <summary> /// 圖片數據源 /// </summary> public ImageSource Source { get { return (ImageSource)GetValue(SourceProperty); } set { SetValue(SourceProperty, value); } } /// <summary> /// 是否加載中 /// </summary> public static readonly DependencyProperty IsLoadingProperty = DependencyProperty.Register("IsLoading", typeof(bool), typeof(ScalableImage), new PropertyMetadata(false)); /// <summary> /// 是否加載中 /// </summary> public bool IsLoading { get { return (bool)GetValue(IsLoadingProperty); } set { SetValue(IsLoadingProperty, value); } } private static void SourceChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { ScalableImage dimg = sender as ScalableImage; dimg._image.Source = (ImageSource)e.NewValue; } public BitmapCreateOptions CreateOptions { get { return ((BitmapCreateOptions)(base.GetValue(CreateOptionsProperty))); } set { base.SetValue(CreateOptionsProperty, value); } } private static async void ImageUrlPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { var url = e.NewValue as string; if (string.IsNullOrEmpty(url)) return; var cachedImage = (ScalableImage)obj; var bitmapImage = new BitmapImage(); switch (FileCache.AppCacheMode) { case FileCache.CacheMode.WinINet: bitmapImage.BeginInit(); bitmapImage.CreateOptions = cachedImage.CreateOptions; bitmapImage.UriSource = new Uri(url); bitmapImage.UriCachePolicy = new RequestCachePolicy(RequestCacheLevel.Default); bitmapImage.EndInit(); cachedImage.Source = bitmapImage; break; case FileCache.CacheMode.Dedicated: try { FileCache.DownloadStateChangedAction = (downloadState) => { switch (downloadState) { case DownloadState.Process: cachedImage.IsLoading = true; break; case DownloadState.Failed: cachedImage.IsLoading = false; break; case DownloadState.Success: cachedImage.IsLoading = false; break; } }; var memoryStream = await FileCache.HitAsync(url); if (memoryStream == null) return; bitmapImage.BeginInit(); bitmapImage.CreateOptions = cachedImage.CreateOptions; bitmapImage.StreamSource = memoryStream; bitmapImage.EndInit(); cachedImage.Source = bitmapImage; } catch (Exception ex) { } break; default: throw new ArgumentOutOfRangeException(); } } public static readonly DependencyProperty ImageUrlProperty = DependencyProperty.Register("ImageUrl", typeof(string), typeof(ScalableImage), new PropertyMetadata("", ImageUrlPropertyChanged)); public static readonly DependencyProperty CreateOptionsProperty = DependencyProperty.Register("CreateOptions", typeof(BitmapCreateOptions), typeof(ScalableImage)); /// <summary> /// 是否可以縮放 /// </summary> public static readonly DependencyProperty CanScaleProperty = DependencyProperty.Register("CanScale", typeof(bool), typeof(ScalableImage), new PropertyMetadata(false)); /// <summary> /// 是否可以縮放 /// </summary> public bool CanScale { get { return (bool)GetValue(CanScaleProperty); } set { SetValue(CanScaleProperty, value); } } /// <summary> /// 是否可以縮小 /// </summary> public static readonly DependencyProperty CanScaleSmallerProperty = DependencyProperty.Register("CanScaleSmaller", typeof(bool), typeof(ScalableImage), new PropertyMetadata(false)); /// <summary> /// 是否可以縮小 /// </summary> public bool CanScaleSmaller { get { return (bool)GetValue(CanScaleSmallerProperty); } set { SetValue(CanScaleSmallerProperty, value); } } protected override void OnMouseDown(MouseButtonEventArgs e) { _start = e.GetPosition(_parentElement); _origin = new Point(_translateTransform.X, _translateTransform.Y); this.CaptureMouse(); } protected override void OnMouseLeave(MouseEventArgs e) { ReleaseMouseCapture(); } protected override void OnMouseUp(MouseButtonEventArgs e) { ReleaseMouseCapture(); } protected override void OnMouseWheel(MouseWheelEventArgs e) { if (!CanScale) return; var point = e.GetPosition(_parentElement); // 實際點擊的點 var actualPoint = _transfromGroup.Inverse.Transform(point); // 想要縮放的點 double zoom = e.Delta > 0 ? .2 : -.2; var scaleValue = _scaleTransform.ScaleX + zoom; // 是否可以縮到比默認大小還要小 if (!CanScaleSmaller) { if (scaleValue < 1) { return; } } // 縮放幅度控制 if (scaleValue < 0.2 || scaleValue > 1.8) { return; } _scaleTransform.ScaleX = scaleValue; _scaleTransform.ScaleY = scaleValue; _translateTransform.X = -((actualPoint.X * (scaleValue - 1))) + point.X - actualPoint.X; _translateTransform.Y = -((actualPoint.Y * (scaleValue - 1))) + point.Y - actualPoint.Y; } protected override void OnMouseMove(MouseEventArgs e) { if (IsMouseCaptured) { Vector v = _start - e.GetPosition(_parentElement); _translateTransform.X = _origin.X - v.X; _translateTransform.Y = _origin.Y - v.Y; } } } public static class FileCache { public static Action<DownloadState> DownloadStateChangedAction; public enum CacheMode { WinINet, Dedicated } // Record whether a file is being written. private static readonly Dictionary<string, bool> IsWritingFile = new Dictionary<string, bool>(); static FileCache() { // default cache directory - can be changed if needed from App.xaml AppCacheDirectory = string.Format("{0}\\{1}\\Cache\\", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Process.GetCurrentProcess().ProcessName); AppCacheMode = CacheMode.Dedicated; } /// <summary> /// Gets or sets the path to the folder that stores the cache file. Only works when AppCacheMode is /// CacheMode.Dedicated. /// </summary> public static string AppCacheDirectory { get; set; } /// <summary> /// Gets or sets the cache mode. WinINet is recommended, it‘s provided by .Net Framework and uses the Temporary Files /// of IE and the same cache policy of IE. /// </summary> public static CacheMode AppCacheMode { get; set; } public static async Task<MemoryStream> HitAsync(string url) { DownloadStateChangedAction?.Invoke(DownloadState.Process); if (!Directory.Exists(AppCacheDirectory)) { Directory.CreateDirectory(AppCacheDirectory); } var uri = new Uri(url); var fileNameBuilder = new StringBuilder(); using (var sha1 = new SHA1Managed()) { var canonicalUrl = uri.ToString(); var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(canonicalUrl)); fileNameBuilder.Append(BitConverter.ToString(hash).Replace("-", "").ToLower()); if (Path.HasExtension(canonicalUrl)) fileNameBuilder.Append(Path.GetExtension(canonicalUrl)); } var fileName = fileNameBuilder.ToString(); var localFile = string.Format("{0}\\{1}", AppCacheDirectory, fileName); var memoryStream = new MemoryStream(); FileStream fileStream = null; if (!IsWritingFile.ContainsKey(fileName) && File.Exists(localFile)) { using (fileStream = new FileStream(localFile, FileMode.Open, FileAccess.Read)) { await fileStream.CopyToAsync(memoryStream); } memoryStream.Seek(0, SeekOrigin.Begin); DownloadStateChangedAction?.Invoke(DownloadState.Success); return memoryStream; } var request = WebRequest.Create(uri); request.Timeout = 30; try { var response = await request.GetResponseAsync(); var responseStream = response.GetResponseStream(); if (responseStream == null) return null; if (!IsWritingFile.ContainsKey(fileName)) { IsWritingFile[fileName] = true; fileStream = new FileStream(localFile, FileMode.Create, FileAccess.Write); } using (responseStream) { var bytebuffer = new byte[100]; int bytesRead; do { bytesRead = await responseStream.ReadAsync(bytebuffer, 0, 100); if (fileStream != null) await fileStream.WriteAsync(bytebuffer, 0, bytesRead); await memoryStream.WriteAsync(bytebuffer, 0, bytesRead); } while (bytesRead > 0); if (fileStream != null) { await fileStream.FlushAsync(); fileStream.Dispose(); IsWritingFile.Remove(fileName); } } memoryStream.Seek(0, SeekOrigin.Begin); DownloadStateChangedAction?.Invoke(DownloadState.Success); return memoryStream; } catch (WebException) { DownloadStateChangedAction?.Invoke(DownloadState.Failed); return null; } } } public enum DownloadState { Process, Failed, Success } }
<Style TargetType="{x:Type ScalableImage}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type dui:ScalableImage}"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <DuiBusy x:Name="duiBusy" BorderThickness="0" BusyText="請稍後..." IsBusy="{Binding IsLoading,RelativeSource={RelativeSource TemplatedParent}}" IsShowBusyMark="True"> <Grid Background="Transparent"> <Image x:Name="PART_Image"></Image> </Grid> </DuiBusy> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style>
wpf 可縮放,可顯示遠程圖片的Image