diff --git a/ImageEx/ImageEx.Members.cs b/ImageEx/ImageEx.Members.cs index 2535fd6..d223342 100644 --- a/ImageEx/ImageEx.Members.cs +++ b/ImageEx/ImageEx.Members.cs @@ -2,40 +2,41 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace ImageEx; - -/// -/// The ImageEx control extends the default Image platform control improving the performance and responsiveness of your Apps. -/// Source images are downloaded asynchronously showing a load indicator while in progress. -/// Once downloaded, the source image is stored in the App local cache to preserve resources and load time next time the image needs to be displayed. -/// -public partial class ImageEx +namespace ImageEx { /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty NineGridProperty = DependencyProperty.Register(nameof(NineGrid), typeof(Thickness), typeof(ImageEx), new PropertyMetadata(default(Thickness))); - - /// - /// Gets or sets the nine-grid used by the image. + /// The ImageEx control extends the default Image platform control improving the performance and responsiveness of your Apps. + /// Source images are downloaded asynchronously showing a load indicator while in progress. + /// Once downloaded, the source image is stored in the App local cache to preserve resources and load time next time the image needs to be displayed. /// - public Thickness NineGrid + public partial class ImageEx { - get { return (Thickness)GetValue(NineGridProperty); } - set { SetValue(NineGridProperty, value); } - } + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty NineGridProperty = DependencyProperty.Register(nameof(NineGrid), typeof(Thickness), typeof(ImageEx), new PropertyMetadata(default(Thickness))); - /// - /// Returns the image as a . - /// - /// The image as a . - public CastingSource GetAsCastingSource() - { - if (IsInitialized && Image is Image image) + /// + /// Gets or sets the nine-grid used by the image. + /// + public Thickness NineGrid { - return image.GetAsCastingSource(); + get { return (Thickness)GetValue(NineGridProperty); } + set { SetValue(NineGridProperty, value); } } - return null; + /// + /// Returns the image as a . + /// + /// The image as a . + public CastingSource GetAsCastingSource() + { + if (IsInitialized && Image is Image image) + { + return image.GetAsCastingSource(); + } + + return null; + } } } \ No newline at end of file diff --git a/ImageEx/ImageEx.cs b/ImageEx/ImageEx.cs index 0ad9a43..1a20c18 100644 --- a/ImageEx/ImageEx.cs +++ b/ImageEx/ImageEx.cs @@ -2,20 +2,25 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace ImageEx; +using System.Diagnostics; -/// -/// The ImageEx control extends the default Image platform control improving the performance and responsiveness of your Apps. -/// Source images are downloaded asynchronously showing a load indicator while in progress. -/// Once downloaded, the source image is stored in the App local cache to preserve resources and load time next time the image needs to be displayed. -/// -public partial class ImageEx : ImageExBase +namespace ImageEx { /// - /// Initializes a new instance of the class. + /// The ImageEx control extends the default Image platform control improving the performance and responsiveness of your Apps. + /// Source images are downloaded asynchronously showing a load indicator while in progress. + /// Once downloaded, the source image is stored in the App local cache to preserve resources and load time next time the image needs to be displayed. /// - public ImageEx() : base() + public partial class ImageEx : ImageExBase { - DefaultStyleKey = typeof(ImageEx); + /// + /// Initializes a new instance of the class. + /// + public ImageEx() + : base() + { + DefaultStyleKey = typeof(ImageEx); + Debug.WriteLine(DefaultStyleKey.ToString()); + } } } \ No newline at end of file diff --git a/ImageEx/ImageEx.csproj b/ImageEx/ImageEx.csproj index c8be374..ef04ab9 100644 --- a/ImageEx/ImageEx.csproj +++ b/ImageEx/ImageEx.csproj @@ -1,18 +1,17 @@  - uap10.0.22621;net8.0-windows10.0.22621 + uap10.0.19041;net6.0-windows10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 10.0.17763.0 Library False - 12.0 + 11.0 NETSDK1023;CA1416 true embedded - true win-x86;win-x64;win-arm64 diff --git a/ImageEx/ImageExBase.Members.cs b/ImageEx/ImageExBase.Members.cs index 7727cdf..f536ea2 100644 --- a/ImageEx/ImageExBase.Members.cs +++ b/ImageEx/ImageExBase.Members.cs @@ -2,155 +2,156 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace ImageEx; - -/// -/// Base Code for ImageEx -/// -public partial class ImageExBase +namespace ImageEx { /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty StretchProperty = DependencyProperty.Register(nameof(Stretch), typeof(Stretch), typeof(ImageExBase), new PropertyMetadata(Stretch.Uniform)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty DecodePixelHeightProperty = DependencyProperty.Register(nameof(DecodePixelHeight), typeof(int), typeof(ImageExBase), new PropertyMetadata(0)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty DecodePixelTypeProperty = DependencyProperty.Register(nameof(DecodePixelType), typeof(int), typeof(ImageExBase), new PropertyMetadata(DecodePixelType.Physical)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty DecodePixelWidthProperty = DependencyProperty.Register(nameof(DecodePixelWidth), typeof(int), typeof(ImageExBase), new PropertyMetadata(0)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty IsCacheEnabledProperty = DependencyProperty.Register(nameof(IsCacheEnabled), typeof(bool), typeof(ImageExBase), new PropertyMetadata(false)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty EnableLazyLoadingProperty = DependencyProperty.Register(nameof(EnableLazyLoading), typeof(bool), typeof(ImageExBase), new PropertyMetadata(false, EnableLazyLoadingChanged)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty LazyLoadingThresholdProperty = DependencyProperty.Register(nameof(LazyLoadingThreshold), typeof(double), typeof(ImageExBase), new PropertyMetadata(default(double), LazyLoadingThresholdChanged)); - - /// - /// Event raised if the image failed loading. - /// - public event ImageExFailedEventHandler ImageExFailed; - - /// - /// Event raised when the image is successfully loaded and opened. + /// Base Code for ImageEx /// - public event ImageExOpenedEventHandler ImageExOpened; - - /// - /// Event raised when the control is initialized. - /// - public event EventHandler ImageExInitialized; - - /// - /// Gets a value indicating whether control has been initialized. - /// - public bool IsInitialized { get; private set; } - - /// - /// Gets or sets DecodePixelHeight for underlying bitmap - /// - public int DecodePixelHeight - { - get { return (int)GetValue(DecodePixelHeightProperty); } - set { SetValue(DecodePixelHeightProperty, value); } - } - - /// - /// Gets or sets DecodePixelType for underlying bitmap - /// - public DecodePixelType DecodePixelType + public partial class ImageExBase { - get { return (DecodePixelType)GetValue(DecodePixelTypeProperty); } - set { SetValue(DecodePixelTypeProperty, value); } - } + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty StretchProperty = DependencyProperty.Register(nameof(Stretch), typeof(Stretch), typeof(ImageExBase), new PropertyMetadata(Stretch.Uniform)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty DecodePixelHeightProperty = DependencyProperty.Register(nameof(DecodePixelHeight), typeof(int), typeof(ImageExBase), new PropertyMetadata(0)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty DecodePixelTypeProperty = DependencyProperty.Register(nameof(DecodePixelType), typeof(int), typeof(ImageExBase), new PropertyMetadata(DecodePixelType.Physical)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty DecodePixelWidthProperty = DependencyProperty.Register(nameof(DecodePixelWidth), typeof(int), typeof(ImageExBase), new PropertyMetadata(0)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty IsCacheEnabledProperty = DependencyProperty.Register(nameof(IsCacheEnabled), typeof(bool), typeof(ImageExBase), new PropertyMetadata(false)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty EnableLazyLoadingProperty = DependencyProperty.Register(nameof(EnableLazyLoading), typeof(bool), typeof(ImageExBase), new PropertyMetadata(false, EnableLazyLoadingChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty LazyLoadingThresholdProperty = DependencyProperty.Register(nameof(LazyLoadingThreshold), typeof(double), typeof(ImageExBase), new PropertyMetadata(default(double), LazyLoadingThresholdChanged)); + + /// + /// Event raised if the image failed loading. + /// + public event ImageExFailedEventHandler ImageExFailed; + + /// + /// Event raised when the image is successfully loaded and opened. + /// + public event ImageExOpenedEventHandler ImageExOpened; + + /// + /// Event raised when the control is initialized. + /// + public event EventHandler ImageExInitialized; + + /// + /// Gets a value indicating whether control has been initialized. + /// + public bool IsInitialized { get; private set; } + + /// + /// Gets or sets DecodePixelHeight for underlying bitmap + /// + public int DecodePixelHeight + { + get { return (int)GetValue(DecodePixelHeightProperty); } + set { SetValue(DecodePixelHeightProperty, value); } + } - /// - /// Gets or sets DecodePixelWidth for underlying bitmap - /// - public int DecodePixelWidth - { - get { return (int)GetValue(DecodePixelWidthProperty); } - set { SetValue(DecodePixelWidthProperty, value); } - } + /// + /// Gets or sets DecodePixelType for underlying bitmap + /// + public DecodePixelType DecodePixelType + { + get { return (DecodePixelType)GetValue(DecodePixelTypeProperty); } + set { SetValue(DecodePixelTypeProperty, value); } + } - /// - /// Gets or sets the stretch behavior of the image - /// - public Stretch Stretch - { - get { return (Stretch)GetValue(StretchProperty); } - set { SetValue(StretchProperty, value); } - } + /// + /// Gets or sets DecodePixelWidth for underlying bitmap + /// + public int DecodePixelWidth + { + get { return (int)GetValue(DecodePixelWidthProperty); } + set { SetValue(DecodePixelWidthProperty, value); } + } - /// - /// Gets or sets a value indicating whether gets or sets cache state - /// - public bool IsCacheEnabled - { - get { return (bool)GetValue(IsCacheEnabledProperty); } - set { SetValue(IsCacheEnabledProperty, value); } - } + /// + /// Gets or sets the stretch behavior of the image + /// + public Stretch Stretch + { + get { return (Stretch)GetValue(StretchProperty); } + set { SetValue(StretchProperty, value); } + } - /// - /// Gets or sets a value indicating whether gets or sets is lazy loading enable. (17763 or higher supported) - /// - /// Windows 10 build 17763 or higher required. - public bool EnableLazyLoading - { - get { return (bool)GetValue(EnableLazyLoadingProperty); } - set { SetValue(EnableLazyLoadingProperty, value); } - } + /// + /// Gets or sets a value indicating whether gets or sets cache state + /// + public bool IsCacheEnabled + { + get { return (bool)GetValue(IsCacheEnabledProperty); } + set { SetValue(IsCacheEnabledProperty, value); } + } - /// - /// Gets or sets a value indicating the threshold for triggering lazy loading. - /// - public double LazyLoadingThreshold - { - get { return (double)GetValue(LazyLoadingThresholdProperty); } - set { SetValue(LazyLoadingThresholdProperty, value); } - } + /// + /// Gets or sets a value indicating whether gets or sets is lazy loading enable. (17763 or higher supported) + /// + /// Windows 10 build 17763 or higher required. + public bool EnableLazyLoading + { + get { return (bool)GetValue(EnableLazyLoadingProperty); } + set { SetValue(EnableLazyLoadingProperty, value); } + } - private static void EnableLazyLoadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is ImageExBase control) + /// + /// Gets or sets a value indicating the threshold for triggering lazy loading. + /// + public double LazyLoadingThreshold { - var value = (bool)e.NewValue; - if (value) - { - control.LayoutUpdated += control.ImageExBase_LayoutUpdated; + get { return (double)GetValue(LazyLoadingThresholdProperty); } + set { SetValue(LazyLoadingThresholdProperty, value); } + } - control.InvalidateLazyLoading(); - } - else + private static void EnableLazyLoadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is ImageExBase control) { - control.LayoutUpdated -= control.ImageExBase_LayoutUpdated; + var value = (bool)e.NewValue; + if (value) + { + control.LayoutUpdated += control.ImageExBase_LayoutUpdated; + + control.InvalidateLazyLoading(); + } + else + { + control.LayoutUpdated -= control.ImageExBase_LayoutUpdated; + } } } - } - private static void LazyLoadingThresholdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is ImageExBase control && control.EnableLazyLoading) + private static void LazyLoadingThresholdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - control.InvalidateLazyLoading(); + if (d is ImageExBase control && control.EnableLazyLoading) + { + control.InvalidateLazyLoading(); + } } } } \ No newline at end of file diff --git a/ImageEx/ImageExBase.Placeholder.cs b/ImageEx/ImageExBase.Placeholder.cs index 963fc98..1becfc4 100644 --- a/ImageEx/ImageExBase.Placeholder.cs +++ b/ImageEx/ImageExBase.Placeholder.cs @@ -2,68 +2,69 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace ImageEx; - -/// -/// Base code for ImageEx -/// -public partial class ImageExBase +namespace ImageEx { /// - /// Identifies the dependency property. + /// Base code for ImageEx /// - public static readonly DependencyProperty PlaceholderSourceProperty = DependencyProperty.Register( - nameof(PlaceholderSource), - typeof(ImageSource), - typeof(ImageExBase), - new PropertyMetadata(default(ImageSource), PlaceholderSourceChanged)); + public partial class ImageExBase + { + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty PlaceholderSourceProperty = DependencyProperty.Register( + nameof(PlaceholderSource), + typeof(ImageSource), + typeof(ImageExBase), + new PropertyMetadata(default(ImageSource), PlaceholderSourceChanged)); - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty PlaceholderStretchProperty = DependencyProperty.Register( - nameof(PlaceholderStretch), - typeof(Stretch), - typeof(ImageExBase), - new PropertyMetadata(default(Stretch))); + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty PlaceholderStretchProperty = DependencyProperty.Register( + nameof(PlaceholderStretch), + typeof(Stretch), + typeof(ImageExBase), + new PropertyMetadata(default(Stretch))); - /// - /// Gets or sets the placeholder source. - /// - /// - /// The placeholder source. - /// - public ImageSource PlaceholderSource - { - get { return (ImageSource)GetValue(PlaceholderSourceProperty); } - set { SetValue(PlaceholderSourceProperty, value); } - } + /// + /// Gets or sets the placeholder source. + /// + /// + /// The placeholder source. + /// + public ImageSource PlaceholderSource + { + get { return (ImageSource)GetValue(PlaceholderSourceProperty); } + set { SetValue(PlaceholderSourceProperty, value); } + } - private static void PlaceholderSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is ImageExBase control) + private static void PlaceholderSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - control.OnPlaceholderSourceChanged(e); + if (d is ImageExBase control) + { + control.OnPlaceholderSourceChanged(e); + } } - } - /// - /// Invoked when Placeholder source has changed - /// - /// Event args - protected virtual void OnPlaceholderSourceChanged(DependencyPropertyChangedEventArgs e) - { - } + /// + /// Invoked when Placeholder source has changed + /// + /// Event args + protected virtual void OnPlaceholderSourceChanged(DependencyPropertyChangedEventArgs e) + { + } - /// - /// Gets or sets the placeholder stretch. - /// - /// - /// The placeholder stretch. - /// - public Stretch PlaceholderStretch - { - get { return (Stretch)GetValue(PlaceholderStretchProperty); } - set { SetValue(PlaceholderStretchProperty, value); } + /// + /// Gets or sets the placeholder stretch. + /// + /// + /// The placeholder stretch. + /// + public Stretch PlaceholderStretch + { + get { return (Stretch)GetValue(PlaceholderStretchProperty); } + set { SetValue(PlaceholderStretchProperty, value); } + } } } \ No newline at end of file diff --git a/ImageEx/ImageExBase.Source.cs b/ImageEx/ImageExBase.Source.cs index 3df1442..718817c 100644 --- a/ImageEx/ImageExBase.Source.cs +++ b/ImageEx/ImageExBase.Source.cs @@ -2,224 +2,240 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace ImageEx; +using System.Diagnostics; -/// -/// Base code for ImageEx -/// -public partial class ImageExBase +namespace ImageEx { /// - /// Identifies the dependency property. + /// Base code for ImageEx /// - public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(nameof(Source), typeof(object), typeof(ImageExBase), new PropertyMetadata(null, SourceChanged)); - - //// Used to track if we get a new request, so we can cancel any potential custom cache loading. - private CancellationTokenSource _tokenSource; + public partial class ImageExBase + { + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(nameof(Source), typeof(object), typeof(ImageExBase), new PropertyMetadata(null, SourceChanged)); - private object _lazyLoadingSource; + //// Used to track if we get a new request, so we can cancel any potential custom cache loading. + private CancellationTokenSource _tokenSource; - /// - /// Gets or sets the source used by the image - /// - public object Source - { - get { return GetValue(SourceProperty); } - set { SetValue(SourceProperty, value); } - } + private object _lazyLoadingSource; - private static void SourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is not ImageExBase control) + /// + /// Gets or sets the source used by the image + /// + public object Source { - return; + get { return GetValue(SourceProperty); } + set { Debug.WriteLine("test3"); SetValue(SourceProperty, value); } } - if (e.OldValue == null || e.NewValue == null || !e.OldValue.Equals(e.NewValue)) + private static void SourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - if (e.NewValue == null || !control.EnableLazyLoading || control._isInViewport) + var control = d as ImageExBase; + + if (control == null) { - control._lazyLoadingSource = null; - control.SetSource(e.NewValue); + return; } - else + + if (e.OldValue == null || e.NewValue == null || !e.OldValue.Equals(e.NewValue)) { - control._lazyLoadingSource = e.NewValue; + if (e.NewValue == null || !control.EnableLazyLoading || control._isInViewport) + { + control._lazyLoadingSource = null; + control.SetSource(e.NewValue); + } + else + { + control._lazyLoadingSource = e.NewValue; + } } } - } - - private static bool IsHttpUri(Uri uri) - { - return uri.IsAbsoluteUri && (uri.Scheme == "http" || uri.Scheme == "https"); - } - /// - /// Method to call to assign an value to the underlying powering . - /// - /// to assign to the image. - private void AttachSource(ImageSource source) - { - // Setting the source at this point should call ImageExOpened/VisualStateManager.GoToState - // as we register to both the ImageOpened/ImageFailed events of the underlying control. - // We only need to call those methods if we fail in other cases before we get here. - if (Image is Image image) + private static bool IsHttpUri(Uri uri) { - image.Source = source; - } - else if (Image is ImageBrush brush) - { - brush.ImageSource = source; + return uri.IsAbsoluteUri && (uri.Scheme == "http" || uri.Scheme == "https"); } - if (source == null) + /// + /// Method to call to assign an value to the underlying powering . + /// + /// to assign to the image. + private void AttachSource(ImageSource source) { - VisualStateManager.GoToState(this, UnloadedState, true); - } - else if (source is BitmapSource { PixelHeight: > 0, PixelWidth: > 0 }) - { - VisualStateManager.GoToState(this, LoadedState, true); - ImageExOpened?.Invoke(this, new ImageExOpenedEventArgs()); - } - } + // Setting the source at this point should call ImageExOpened/VisualStateManager.GoToState + // as we register to both the ImageOpened/ImageFailed events of the underlying control. + // We only need to call those methods if we fail in other cases before we get here. + if (Image is Image image) + { + image.Source = source; + } + else if (Image is ImageBrush brush) + { + brush.ImageSource = source; + } - private async void SetSource(object source) - { - if (!IsInitialized) - { - return; + if (source == null) + { + VisualStateManager.GoToState(this, UnloadedState, true); + } + else if (source is BitmapSource { PixelHeight: > 0, PixelWidth: > 0 }) + { + VisualStateManager.GoToState(this, LoadedState, true); + ImageExOpened?.Invoke(this, new ImageExOpenedEventArgs()); + } + Debug.WriteLine("test4"); } - _tokenSource?.Cancel(); - - _tokenSource = new CancellationTokenSource(); - - AttachSource(null); - - if (source == null) + private async void SetSource(object source) { - return; - } + Debug.WriteLine("test5_1"); + if (!IsInitialized) + { + return; + } - VisualStateManager.GoToState(this, LoadingState, true); + Debug.WriteLine("test5_2"); + _tokenSource?.Cancel(); - if (source is ImageSource imageSource) - { - AttachSource(imageSource); - return; - } + _tokenSource = new CancellationTokenSource(); - var uri = source as Uri; - if (uri == null) - { - var url = source as string ?? source.ToString(); - if (!Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out uri)) + AttachSource(null); + + if (source == null) { - VisualStateManager.GoToState(this, FailedState, true); - ImageExFailed?.Invoke(this, new ImageExFailedEventArgs(new UriFormatException("Invalid uri specified."))); return; } - } - if (!IsHttpUri(uri) && !uri.IsAbsoluteUri) - { - uri = new Uri("ms-appx:///" + uri.OriginalString.TrimStart('/')); - } + Debug.WriteLine("test5_3"); - try - { - await LoadImageAsync(uri, _tokenSource.Token); - } - catch (OperationCanceledException) - { - // nothing to do as cancellation has been requested. - } - catch (Exception e) - { - VisualStateManager.GoToState(this, FailedState, true); - ImageExFailed?.Invoke(this, new ImageExFailedEventArgs(e)); - } - } + VisualStateManager.GoToState(this, LoadingState, true); - private async Task LoadImageAsync(Uri imageUri, CancellationToken token) - { - if (imageUri != null) - { - if (IsCacheEnabled) + var imageSource = source as ImageSource; + if (imageSource != null) { - var img = await ProvideCachedResourceAsync(imageUri, token); + AttachSource(imageSource); - if (!_tokenSource.IsCancellationRequested) + return; + } + Debug.WriteLine("test5_4"); + var uri = source as Uri; + if (uri == null) + { + var url = source as string ?? source.ToString(); + if (!Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out uri)) { - // Only attach our image if we still have a valid request. - AttachSource(img); + VisualStateManager.GoToState(this, FailedState, true); + ImageExFailed?.Invoke(this, new ImageExFailedEventArgs(new UriFormatException("Invalid uri specified."))); + return; } } - else if (string.Equals(imageUri.Scheme, "data", StringComparison.OrdinalIgnoreCase)) + + Debug.WriteLine("test5_6"); + if (!IsHttpUri(uri) && !uri.IsAbsoluteUri) + { + uri = new Uri("ms-appx:///" + uri.OriginalString.TrimStart('/')); + } + + Debug.WriteLine("test5_7"); + try + { + await LoadImageAsync(uri, _tokenSource.Token); + } + catch (OperationCanceledException) { - var source = imageUri.OriginalString; - const string base64Head = "base64,"; - var index = source.IndexOf(base64Head); - if (index >= 0) + // nothing to do as cancellation has been requested. + } + catch (Exception e) + { + VisualStateManager.GoToState(this, FailedState, true); + ImageExFailed?.Invoke(this, new ImageExFailedEventArgs(e)); + } + + Debug.WriteLine("test5"); + } + + private async Task LoadImageAsync(Uri imageUri, CancellationToken token) + { + if (imageUri != null) + { + if (IsCacheEnabled) { - var bytes = Convert.FromBase64String(source.Substring(index + base64Head.Length)); - var bitmap = new BitmapImage(); - await bitmap.SetSourceAsync(new MemoryStream(bytes).AsRandomAccessStream()); + var img = await ProvideCachedResourceAsync(imageUri, token); if (!_tokenSource.IsCancellationRequested) { - AttachSource(bitmap); + // Only attach our image if we still have a valid request. + AttachSource(img); } } - } - else - { - AttachSource(new BitmapImage(imageUri) + else if (string.Equals(imageUri.Scheme, "data", StringComparison.OrdinalIgnoreCase)) { - CreateOptions = BitmapCreateOptions.IgnoreImageCache - }); + var source = imageUri.OriginalString; + const string base64Head = "base64,"; + var index = source.IndexOf(base64Head); + if (index >= 0) + { + var bytes = Convert.FromBase64String(source.Substring(index + base64Head.Length)); + var bitmap = new BitmapImage(); + await bitmap.SetSourceAsync(new MemoryStream(bytes).AsRandomAccessStream()); + + if (!_tokenSource.IsCancellationRequested) + { + AttachSource(bitmap); + } + } + } + else + { + AttachSource(new BitmapImage(imageUri) + { + CreateOptions = BitmapCreateOptions.IgnoreImageCache + }); + } } } - } - /// - /// This method is provided in case a developer would like their own custom caching strategy for . - /// By default it uses the built-in UWP cache provided by and - /// the control itself. This method should return an - /// value of the image specified by the provided uri parameter. - /// A is provided in case the current request is invalidated - /// (e.g. the container is recycled before the original image is loaded). - /// The Toolkit also has an image cache helper which can be used as well: - /// in . - /// - /// - /// - /// var propValues = new List<KeyValuePair<string, object>>(); - /// - /// if (DecodePixelHeight > 0) - /// { - /// propValues.Add(new KeyValuePair<string, object>(nameof(DecodePixelHeight), DecodePixelHeight)); - /// } - /// if (DecodePixelWidth > 0) - /// { - /// propValues.Add(new KeyValuePair<string, object>(nameof(DecodePixelWidth), DecodePixelWidth)); - /// } - /// if (propValues.Count > 0) - /// { - /// propValues.Add(new KeyValuePair<string, object>(nameof(DecodePixelType), DecodePixelType)); - /// } - /// - /// // A token is provided here as well to cancel the request to the cache, - /// // if a new image is requested. - /// return await ImageCache.Instance.GetFromCacheAsync(imageUri, true, token, propValues); - /// - /// - /// of the image to load from the cache. - /// A which is used to signal when the current request is outdated. - /// - protected virtual Task ProvideCachedResourceAsync(Uri imageUri, CancellationToken token) - { - // By default we just use the built-in UWP image cache provided within the Image control. - return Task.FromResult((ImageSource)new BitmapImage(imageUri)); + /// + /// This method is provided in case a developer would like their own custom caching strategy for . + /// By default it uses the built-in UWP cache provided by and + /// the control itself. This method should return an + /// value of the image specified by the provided uri parameter. + /// A is provided in case the current request is invalidated + /// (e.g. the container is recycled before the original image is loaded). + /// The Toolkit also has an image cache helper which can be used as well: + /// in . + /// + /// + /// + /// var propValues = new List<KeyValuePair<string, object>>(); + /// + /// if (DecodePixelHeight > 0) + /// { + /// propValues.Add(new KeyValuePair<string, object>(nameof(DecodePixelHeight), DecodePixelHeight)); + /// } + /// if (DecodePixelWidth > 0) + /// { + /// propValues.Add(new KeyValuePair<string, object>(nameof(DecodePixelWidth), DecodePixelWidth)); + /// } + /// if (propValues.Count > 0) + /// { + /// propValues.Add(new KeyValuePair<string, object>(nameof(DecodePixelType), DecodePixelType)); + /// } + /// + /// // A token is provided here as well to cancel the request to the cache, + /// // if a new image is requested. + /// return await ImageCache.Instance.GetFromCacheAsync(imageUri, true, token, propValues); + /// + /// + /// of the image to load from the cache. + /// A which is used to signal when the current request is outdated. + /// + protected virtual Task ProvideCachedResourceAsync(Uri imageUri, CancellationToken token) + { + // By default we just use the built-in UWP image cache provided within the Image control. + return Task.FromResult((ImageSource)new BitmapImage(imageUri)); + } } } \ No newline at end of file diff --git a/ImageEx/ImageExBase.cs b/ImageEx/ImageExBase.cs index 0bdbbbe..f50b9fd 100644 --- a/ImageEx/ImageExBase.cs +++ b/ImageEx/ImageExBase.cs @@ -2,254 +2,260 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace ImageEx; +using System.Diagnostics; -public static class Extensions +namespace ImageEx { - /// - /// Determines if a rectangle intersects with another rectangle. - /// - /// The first rectangle to test. - /// The second rectangle to test. - /// This method returns if there is any intersection, otherwise . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IntersectsWith(this Rect rect1, Rect rect2) + public static class Extensions { - if (rect1.IsEmpty || rect2.IsEmpty) + /// + /// Determines if a rectangle intersects with another rectangle. + /// + /// The first rectangle to test. + /// The second rectangle to test. + /// This method returns if there is any intersection, otherwise . + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IntersectsWith(this Rect rect1, Rect rect2) { - return false; - } - - return (rect1.Left <= rect2.Right) && - (rect1.Right >= rect2.Left) && - (rect1.Top <= rect2.Bottom) && - (rect1.Bottom >= rect2.Top); - } -} - -/// -/// Base Code for ImageEx -/// -[TemplateVisualState(Name = LoadingState, GroupName = CommonGroup)] -[TemplateVisualState(Name = LoadedState, GroupName = CommonGroup)] -[TemplateVisualState(Name = UnloadedState, GroupName = CommonGroup)] -[TemplateVisualState(Name = FailedState, GroupName = CommonGroup)] -[TemplatePart(Name = PartImage, Type = typeof(object))] -public abstract partial class ImageExBase : Control -{ - private bool _isInViewport; - - /// - /// Image name in template - /// - protected const string PartImage = "Image"; - - /// - /// VisualStates name in template - /// - protected const string CommonGroup = "CommonStates"; - - /// - /// Loading state name in template - /// - protected const string LoadingState = "Loading"; - - /// - /// Loaded state name in template - /// - protected const string LoadedState = "Loaded"; - - /// - /// Unloaded state name in template - /// - protected const string UnloadedState = "Unloaded"; - - /// - /// Failed name in template - /// - protected const string FailedState = "Failed"; - - /// - /// Gets the backing image object - /// - protected object Image { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public ImageExBase() { } + if (rect1.IsEmpty || rect2.IsEmpty) + { + return false; + } - /// - /// Attach image opened event handler - /// - /// Routed Event Handler - protected void AttachImageOpened(RoutedEventHandler handler) - { - if (Image is Image image) - { - image.ImageOpened += handler; - } - else if (Image is ImageBrush brush) - { - brush.ImageOpened += handler; + return (rect1.Left <= rect2.Right) && + (rect1.Right >= rect2.Left) && + (rect1.Top <= rect2.Bottom) && + (rect1.Bottom >= rect2.Top); } } /// - /// Remove image opened handler + /// Base Code for ImageEx /// - /// RoutedEventHandler - protected void RemoveImageOpened(RoutedEventHandler handler) + [TemplateVisualState(Name = LoadingState, GroupName = CommonGroup)] + [TemplateVisualState(Name = LoadedState, GroupName = CommonGroup)] + [TemplateVisualState(Name = UnloadedState, GroupName = CommonGroup)] + [TemplateVisualState(Name = FailedState, GroupName = CommonGroup)] + [TemplatePart(Name = PartImage, Type = typeof(object))] + public abstract partial class ImageExBase : Control { - if (Image is Image image) + private bool _isInViewport; + + /// + /// Image name in template + /// + protected const string PartImage = "Image"; + + /// + /// VisualStates name in template + /// + protected const string CommonGroup = "CommonStates"; + + /// + /// Loading state name in template + /// + protected const string LoadingState = "Loading"; + + /// + /// Loaded state name in template + /// + protected const string LoadedState = "Loaded"; + + /// + /// Unloaded state name in template + /// + protected const string UnloadedState = "Unloaded"; + + /// + /// Failed name in template + /// + protected const string FailedState = "Failed"; + + /// + /// Gets the backing image object + /// + protected object Image { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public ImageExBase() { - image.ImageOpened -= handler; + Debug.WriteLine("test2"); } - else if (Image is ImageBrush brush) + + /// + /// Attach image opened event handler + /// + /// Routed Event Handler + protected void AttachImageOpened(RoutedEventHandler handler) { - brush.ImageOpened -= handler; + if (Image is Image image) + { + image.ImageOpened += handler; + } + else if (Image is ImageBrush brush) + { + brush.ImageOpened += handler; + } } - } - /// - /// Attach image failed event handler - /// - /// Exception Routed Event Handler - protected void AttachImageFailed(ExceptionRoutedEventHandler handler) - { - if (Image is Image image) + /// + /// Remove image opened handler + /// + /// RoutedEventHandler + protected void RemoveImageOpened(RoutedEventHandler handler) { - image.ImageFailed += handler; + if (Image is Image image) + { + image.ImageOpened -= handler; + } + else if (Image is ImageBrush brush) + { + brush.ImageOpened -= handler; + } } - else if (Image is ImageBrush brush) + + /// + /// Attach image failed event handler + /// + /// Exception Routed Event Handler + protected void AttachImageFailed(ExceptionRoutedEventHandler handler) { - brush.ImageFailed += handler; + if (Image is Image image) + { + image.ImageFailed += handler; + } + else if (Image is ImageBrush brush) + { + brush.ImageFailed += handler; + } } - } - /// - /// Remove Image Failed handler - /// - /// Exception Routed Event Handler - protected void RemoveImageFailed(ExceptionRoutedEventHandler handler) - { - if (Image is Image image) + /// + /// Remove Image Failed handler + /// + /// Exception Routed Event Handler + protected void RemoveImageFailed(ExceptionRoutedEventHandler handler) { - image.ImageFailed -= handler; + if (Image is Image image) + { + image.ImageFailed -= handler; + } + else if (Image is ImageBrush brush) + { + brush.ImageFailed -= handler; + } } - else if (Image is ImageBrush brush) + + /// + /// Update the visual state of the control when its template is changed. + /// + protected override void OnApplyTemplate() { - brush.ImageFailed -= handler; - } - } + RemoveImageOpened(OnImageOpened); + RemoveImageFailed(OnImageFailed); - /// - /// Update the visual state of the control when its template is changed. - /// - protected override void OnApplyTemplate() - { - RemoveImageOpened(OnImageOpened); - RemoveImageFailed(OnImageFailed); + Image = GetTemplateChild(PartImage) as object; - Image = GetTemplateChild(PartImage) as object; + IsInitialized = true; - IsInitialized = true; + ImageExInitialized?.Invoke(this, EventArgs.Empty); - ImageExInitialized?.Invoke(this, EventArgs.Empty); + if (Source == null || !EnableLazyLoading || _isInViewport) + { + _lazyLoadingSource = null; + SetSource(Source); + } + else + { + _lazyLoadingSource = Source; + } - if (Source == null || !EnableLazyLoading || _isInViewport) - { - _lazyLoadingSource = null; - SetSource(Source); + AttachImageOpened(OnImageOpened); + AttachImageFailed(OnImageFailed); + + base.OnApplyTemplate(); } - else + + /// + /// Underlying event handler. + /// + /// Image + /// Event Arguments + protected virtual void OnImageOpened(object sender, RoutedEventArgs e) { - _lazyLoadingSource = Source; + VisualStateManager.GoToState(this, LoadedState, true); + ImageExOpened?.Invoke(this, new ImageExOpenedEventArgs()); } - AttachImageOpened(OnImageOpened); - AttachImageFailed(OnImageFailed); - - base.OnApplyTemplate(); - } - - /// - /// Underlying event handler. - /// - /// Image - /// Event Arguments - protected virtual void OnImageOpened(object sender, RoutedEventArgs e) - { - VisualStateManager.GoToState(this, LoadedState, true); - ImageExOpened?.Invoke(this, new ImageExOpenedEventArgs()); - } - - /// - /// Underlying event handler. - /// - /// Image - /// Event Arguments - protected virtual void OnImageFailed(object sender, ExceptionRoutedEventArgs e) - { - VisualStateManager.GoToState(this, FailedState, true); - ImageExFailed?.Invoke(this, new ImageExFailedEventArgs(new Exception(e.ErrorMessage))); - } - - private void ImageExBase_LayoutUpdated(object sender, object e) - { - InvalidateLazyLoading(); - } + /// + /// Underlying event handler. + /// + /// Image + /// Event Arguments + protected virtual void OnImageFailed(object sender, ExceptionRoutedEventArgs e) + { + VisualStateManager.GoToState(this, FailedState, true); + ImageExFailed?.Invoke(this, new ImageExFailedEventArgs(new Exception(e.ErrorMessage))); + } - private void InvalidateLazyLoading() - { - if (!IsLoaded) + private void ImageExBase_LayoutUpdated(object sender, object e) { - _isInViewport = false; - return; + InvalidateLazyLoading(); } - // Find the first ascendant ScrollViewer, if not found, use the root element. - FrameworkElement hostElement = null; - var ascendants = this.FindAscendants().OfType(); - foreach (var ascendant in ascendants) + private void InvalidateLazyLoading() { - hostElement = ascendant; - if (hostElement is ScrollViewer) + if (!IsLoaded) { - break; + _isInViewport = false; + return; } - } - if (hostElement == null) - { - _isInViewport = false; - return; - } + // Find the first ascendant ScrollViewer, if not found, use the root element. + FrameworkElement hostElement = null; + var ascendants = this.FindAscendants().OfType(); + foreach (var ascendant in ascendants) + { + hostElement = ascendant; + if (hostElement is ScrollViewer) + { + break; + } + } - var controlRect = TransformToVisual(hostElement) - .TransformBounds(new Rect(0, 0, ActualWidth, ActualHeight)); - var lazyLoadingThreshold = LazyLoadingThreshold; - var hostRect = new Rect( - 0 - lazyLoadingThreshold, - 0 - lazyLoadingThreshold, - hostElement.ActualWidth + (2 * lazyLoadingThreshold), - hostElement.ActualHeight + (2 * lazyLoadingThreshold)); + if (hostElement == null) + { + _isInViewport = false; + return; + } - if (controlRect.IntersectsWith(hostRect)) - { - _isInViewport = true; + var controlRect = TransformToVisual(hostElement) + .TransformBounds(new Rect(0, 0, ActualWidth, ActualHeight)); + var lazyLoadingThreshold = LazyLoadingThreshold; + var hostRect = new Rect( + 0 - lazyLoadingThreshold, + 0 - lazyLoadingThreshold, + hostElement.ActualWidth + (2 * lazyLoadingThreshold), + hostElement.ActualHeight + (2 * lazyLoadingThreshold)); - if (_lazyLoadingSource != null) + if (controlRect.IntersectsWith(hostRect)) { - var source = _lazyLoadingSource; - _lazyLoadingSource = null; - SetSource(source); + _isInViewport = true; + + if (_lazyLoadingSource != null) + { + var source = _lazyLoadingSource; + _lazyLoadingSource = null; + SetSource(source); + } + } + else + { + _isInViewport = false; } - } - else - { - _isInViewport = false; } } } \ No newline at end of file diff --git a/ImageEx/ImageExFailedEventArgs.cs b/ImageEx/ImageExFailedEventArgs.cs index 58c6fec..20089d3 100644 --- a/ImageEx/ImageExFailedEventArgs.cs +++ b/ImageEx/ImageExFailedEventArgs.cs @@ -2,37 +2,38 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace ImageEx; - -/// -/// A delegate for failed. -/// -/// The sender. -/// The event arguments. -public delegate void ImageExFailedEventHandler(object sender, ImageExFailedEventArgs e); - -/// -/// Provides data for the ImageFailed event. -/// -public class ImageExFailedEventArgs : EventArgs +namespace ImageEx { /// - /// Initializes a new instance of the class. + /// A delegate for failed. /// - /// exception that caused the error condition - public ImageExFailedEventArgs(Exception errorException) - { - ErrorException = errorException; - ErrorMessage = ErrorException?.Message; - } + /// The sender. + /// The event arguments. + public delegate void ImageExFailedEventHandler(object sender, ImageExFailedEventArgs e); /// - /// Gets the exception that caused the error condition. + /// Provides data for the ImageFailed event. /// - public Exception ErrorException { get; private set; } + public class ImageExFailedEventArgs : EventArgs + { + /// + /// Initializes a new instance of the class. + /// + /// exception that caused the error condition + public ImageExFailedEventArgs(Exception errorException) + { + ErrorException = errorException; + ErrorMessage = ErrorException?.Message; + } - /// - /// Gets the reason for the error condition. - /// - public string ErrorMessage { get; private set; } + /// + /// Gets the exception that caused the error condition. + /// + public Exception ErrorException { get; private set; } + + /// + /// Gets the reason for the error condition. + /// + public string ErrorMessage { get; private set; } + } } \ No newline at end of file diff --git a/ImageEx/ImageExOpenedEventArgs.cs b/ImageEx/ImageExOpenedEventArgs.cs index 0b8dff7..792adbf 100644 --- a/ImageEx/ImageExOpenedEventArgs.cs +++ b/ImageEx/ImageExOpenedEventArgs.cs @@ -2,16 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace ImageEx; +namespace ImageEx +{ + /// + /// A delegate for opened. + /// + /// The sender. + /// The event arguments. + public delegate void ImageExOpenedEventHandler(object sender, ImageExOpenedEventArgs e); -/// -/// A delegate for opened. -/// -/// The sender. -/// The event arguments. -public delegate void ImageExOpenedEventHandler(object sender, ImageExOpenedEventArgs e); - -/// -/// Provides data for the ImageOpened event. -/// -public class ImageExOpenedEventArgs : EventArgs { } \ No newline at end of file + /// + /// Provides data for the ImageOpened event. + /// + public class ImageExOpenedEventArgs : EventArgs + { + } +} \ No newline at end of file diff --git a/ImageEx/Properties/AssemblyInfo.cs b/ImageEx/Properties/AssemblyInfo.cs index 74ba45e..9331db5 100644 --- a/ImageEx/Properties/AssemblyInfo.cs +++ b/ImageEx/Properties/AssemblyInfo.cs @@ -4,8 +4,8 @@ [assembly: AssemblyTitle("ImageEx")] [assembly: AssemblyDescription("Extended Image Control for UWP and WinUI apps")] [assembly: AssemblyProduct("ImageEx")] -[assembly: AssemblyCopyright("FourSoft © 2024")] +[assembly: AssemblyCopyright("FourSoft © 2023")] -[assembly: AssemblyVersion("3.0.0.0")] -[assembly: AssemblyFileVersion("3.0.0.0")] +[assembly: AssemblyVersion("2.1.0.0")] +[assembly: AssemblyFileVersion("2.1.0.0")] [assembly: ComVisible(false)] \ No newline at end of file