diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/ImageSelectorDemoCtl.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/ImageSelectorDemoCtl.xaml index a3286d5b..2456d963 100644 --- a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/ImageSelectorDemoCtl.xaml +++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/ImageSelectorDemoCtl.xaml @@ -4,6 +4,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:hc="https://handyorg.github.io/handycontrol"> - + diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml.cs b/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml.cs index 3ddd5898..f7102bec 100644 --- a/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml.cs +++ b/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml.cs @@ -30,6 +30,7 @@ namespace HandyControlDemo.UserControl var selectedIndex = demoInfo.SelectedIndex; demoInfo.SelectedIndex = -1; demoInfo.SelectedIndex = selectedIndex; + FilterItems(); } } @@ -51,6 +52,11 @@ namespace HandyControlDemo.UserControl private void SearchBar_OnSearchStarted(object sender, FunctionEventArgs e) { _searchKey = e.Info; + FilterItems(); + } + + private void FilterItems() + { if (string.IsNullOrEmpty(_searchKey)) { foreach (var item in ViewModelLocator.Instance.Main.DemoInfoCurrent.DemoItemList) @@ -67,7 +73,7 @@ namespace HandyControlDemo.UserControl { item.IsVisible = true; } - else if(item.TargetCtlName.Replace("DemoCtl", "").ToLower().Contains(key)) + else if (item.TargetCtlName.Replace("DemoCtl", "").ToLower().Contains(key)) { item.IsVisible = true; } diff --git a/src/Shared/HandyControl_Shared/Controls/Input/ImageSelector.cs b/src/Shared/HandyControl_Shared/Controls/Input/ImageSelector.cs index f6b34b8d..8d53d501 100644 --- a/src/Shared/HandyControl_Shared/Controls/Input/ImageSelector.cs +++ b/src/Shared/HandyControl_Shared/Controls/Input/ImageSelector.cs @@ -1,38 +1,126 @@ using System; using System.Windows; using System.Windows.Controls; +using System.Windows.Input; using System.Windows.Media; +using System.Windows.Media.Imaging; using HandyControl.Data; +using HandyControl.Interactivity; +using Microsoft.Win32; namespace HandyControl.Controls { public class ImageSelector : Control { - public static readonly DependencyProperty UriProperty = DependencyProperty.Register( + public ImageSelector() => CommandBindings.Add(new CommandBinding(ControlCommands.Switch, SwitchImage)); + + private void SwitchImage(object sender, ExecutedRoutedEventArgs e) + { + if (!HasValue) + { + var dialog = new OpenFileDialog + { + RestoreDirectory = true, + Filter = Filter, + DefaultExt = DefaultExt + }; + + if (dialog.ShowDialog() == true) + { + SetValue(UriPropertyKey, new Uri(dialog.FileName, UriKind.RelativeOrAbsolute)); + SetValue(PreviewBrushPropertyKey, new ImageBrush(BitmapFrame.Create(Uri, BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.None)) + { + Stretch = Stretch + }); + SetValue(HasValuePropertyKey, ValueBoxes.TrueBox); + SetCurrentValue(ToolTipProperty, dialog.FileName); + } + } + else + { + SetValue(UriPropertyKey, default(Uri)); + SetValue(PreviewBrushPropertyKey, default(Brush)); + SetValue(HasValuePropertyKey, ValueBoxes.FalseBox); + SetCurrentValue(ToolTipProperty, default); + } + } + + public static readonly DependencyProperty StretchProperty = DependencyProperty.Register( + "Stretch", typeof(Stretch), typeof(ImageSelector), new PropertyMetadata(default(Stretch))); + + public Stretch Stretch + { + get => (Stretch) GetValue(StretchProperty); + set => SetValue(StretchProperty, value); + } + + public static readonly DependencyPropertyKey UriPropertyKey = DependencyProperty.RegisterReadOnly( "Uri", typeof(Uri), typeof(ImageSelector), new PropertyMetadata(default(Uri))); + public static readonly DependencyProperty UriProperty = UriPropertyKey.DependencyProperty; + public Uri Uri { get => (Uri) GetValue(UriProperty); set => SetValue(UriProperty, value); } - internal static readonly DependencyProperty SourceProperty = DependencyProperty.Register( - "Source", typeof(ImageSource), typeof(ImageSelector), new PropertyMetadata(default(ImageSource))); + public static readonly DependencyPropertyKey PreviewBrushPropertyKey = DependencyProperty.RegisterReadOnly( + "PreviewBrush", typeof(Brush), typeof(ImageSelector), new PropertyMetadata(default(Brush))); - internal ImageSource Source + public static readonly DependencyProperty PreviewBrushProperty = PreviewBrushPropertyKey.DependencyProperty; + + public Brush PreviewBrush { - get => (ImageSource) GetValue(SourceProperty); - set => SetValue(SourceProperty, value); + get => (Brush) GetValue(PreviewBrushProperty); + set => SetValue(PreviewBrushProperty, value); } - public static readonly DependencyProperty IsPreviewEnabledProperty = DependencyProperty.Register( - "IsPreviewEnabled", typeof(bool), typeof(ImageSelector), new PropertyMetadata(ValueBoxes.FalseBox)); + public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register( + "StrokeThickness", typeof(double), typeof(ImageSelector), new FrameworkPropertyMetadata(ValueBoxes.Double1Box, FrameworkPropertyMetadataOptions.AffectsRender)); - public bool IsPreviewEnabled + public double StrokeThickness { - get => (bool) GetValue(IsPreviewEnabledProperty); - set => SetValue(IsPreviewEnabledProperty, value); + get => (double) GetValue(StrokeThicknessProperty); + set => SetValue(StrokeThicknessProperty, value); + } + + public static readonly DependencyProperty StrokeDashArrayProperty = DependencyProperty.Register( + "StrokeDashArray", typeof(DoubleCollection), typeof(ImageSelector), new FrameworkPropertyMetadata(default(DoubleCollection), FrameworkPropertyMetadataOptions.AffectsRender)); + + public DoubleCollection StrokeDashArray + { + get => (DoubleCollection) GetValue(StrokeDashArrayProperty); + set => SetValue(StrokeDashArrayProperty, value); + } + + public static readonly DependencyProperty DefaultExtProperty = DependencyProperty.Register( + "DefaultExt", typeof(string), typeof(ImageSelector), new PropertyMetadata(".jpg")); + + public string DefaultExt + { + get => (string) GetValue(DefaultExtProperty); + set => SetValue(DefaultExtProperty, value); + } + + public static readonly DependencyProperty FilterProperty = DependencyProperty.Register( + "Filter", typeof(string), typeof(ImageSelector), new PropertyMetadata("(.jpg)|*.jpg|(.png)|*.png")); + + public string Filter + { + get => (string) GetValue(FilterProperty); + set => SetValue(FilterProperty, value); + } + + public static readonly DependencyPropertyKey HasValuePropertyKey = DependencyProperty.RegisterReadOnly( + "HasValue", typeof(bool), typeof(ImageSelector), new PropertyMetadata(ValueBoxes.FalseBox)); + + public static readonly DependencyProperty HasValueProperty = HasValuePropertyKey.DependencyProperty; + + public bool HasValue + { + get => (bool) GetValue(HasValueProperty); + set => SetValue(HasValueProperty, value); } } } \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Controls/Panel/SimplePanel.cs b/src/Shared/HandyControl_Shared/Controls/Panel/SimplePanel.cs index 412bb0e2..7cd99696 100644 --- a/src/Shared/HandyControl_Shared/Controls/Panel/SimplePanel.cs +++ b/src/Shared/HandyControl_Shared/Controls/Panel/SimplePanel.cs @@ -12,7 +12,7 @@ namespace HandyControl.Controls /// public class SimplePanel : Panel { - protected override Size MeasureOverride(Size availableSize) + protected override Size MeasureOverride(Size constraint) { var maxSize = new Size(); @@ -20,7 +20,7 @@ namespace HandyControl.Controls { if (child != null) { - child.Measure(availableSize); + child.Measure(constraint); maxSize.Width = Math.Max(maxSize.Width, child.DesiredSize.Width); maxSize.Height = Math.Max(maxSize.Height, child.DesiredSize.Height); } diff --git a/src/Shared/HandyControl_Shared/Data/Gif/GifImageInfo.cs b/src/Shared/HandyControl_Shared/Data/Gif/GifImageInfo.cs index 3da1c3b8..7edab111 100644 --- a/src/Shared/HandyControl_Shared/Data/Gif/GifImageInfo.cs +++ b/src/Shared/HandyControl_Shared/Data/Gif/GifImageInfo.cs @@ -76,10 +76,8 @@ namespace HandyControl.Data { FrameCount = 1; } - if (_frameDelay == null) - { - _frameDelay = new int[FrameCount]; - } + + _frameDelay ??= new int[FrameCount]; } public int FrameDelay(int frame) => _frameDelay[frame]; diff --git a/src/Shared/HandyControl_Shared/Expression/Drawing/MathHelper.cs b/src/Shared/HandyControl_Shared/Expression/Drawing/MathHelper.cs index 2db4f04e..c8c820a1 100644 --- a/src/Shared/HandyControl_Shared/Expression/Drawing/MathHelper.cs +++ b/src/Shared/HandyControl_Shared/Expression/Drawing/MathHelper.cs @@ -4,6 +4,8 @@ namespace HandyControl.Expression.Drawing { internal static class MathHelper { + internal const double DBL_EPSILON = 2.2204460492503131e-016; + public static bool AreClose(double value1, double value2) => // ReSharper disable once CompareOfFloatsByEqualityOperator value1 == value2 || IsVerySmall(value1 - value2); @@ -12,6 +14,8 @@ namespace HandyControl.Expression.Drawing public static bool IsVerySmall(double value) => Math.Abs(value) < 1E-06; + public static bool IsZero(double value) => Math.Abs(value) < 10.0 * DBL_EPSILON; + public static bool IsFiniteDouble(double x) => !double.IsInfinity(x) && !double.IsNaN(x); public static double DoubleFromMantissaAndExponent(double x, int exp) => x * Math.Pow(2.0, exp); diff --git a/src/Shared/HandyControl_Shared/Themes/Basic/Geometries.xaml b/src/Shared/HandyControl_Shared/Themes/Basic/Geometries.xaml index fc1049b7..78e2cd02 100644 --- a/src/Shared/HandyControl_Shared/Themes/Basic/Geometries.xaml +++ b/src/Shared/HandyControl_Shared/Themes/Basic/Geometries.xaml @@ -42,7 +42,8 @@ M627.52666696 962L550.66666696 890.47999971c116.82-92.99999971 191.09999971-230.94 191.09999971-385.2 0-146.99999971-67.61999971-279-174.96-371.57999942L643.84666667 62C770.86666638 172.99999971 850.66666667 330.2 850.66666667 505.09999971c0 182.27999971-86.58 345.42-223.13999971 456.90000029zM405.64666638 782l-76.32-77.22a227.88 227.88 0 0 0 116.40000058-199.26A228.06 228.06 0 0 0 347.86666638 317.84000029L422.86666696 242A334.37999971 334.37999971 0 0 1 550.66666696 505.52 334.62 334.62 0 0 1 405.64666638 782zM205.66666638 596.6C164.26666638 596.6 130.66666667 562.51999971 130.66666667 520.64 130.66666667 478.76000029 164.26666638 444.79999971 205.66666638 444.79999971s74.99999971 33.95999971 75.00000058 75.90000058c0 41.87999971-33.59999971 75.84000029-75.00000058 75.83999942z M0,0L28,0 28,14.0000006258488 28,15.0010000765324 28.0151405334473,15.0010000765324 28.0455417633057,15.8010965883732C28.4084091186524,22.9596563875675,30.92209815979,29.5507589876652,34.9532623291016,34.9410597383976L34.9984359741211,34.9999815523624 34.0966339111328,34.9885679781437C15.1841707229614,34.5094191133976,2.0535663480814E-07,19.0279375612736,0,0z M535.9 165.2l96.5 195.4c3.9 7.9 11.4 13.3 20.1 14.6l215.7 31.3c21.9 3.2 30.6 30 14.8 45.5L726.8 604.1c-6.3 6.1-9.1 14.9-7.7 23.6L756 842.5c3.7 21.8-19.1 38.4-38.7 28.1L524.4 769.2c-7.8-4.1-17-4.1-24.8 0L306.7 870.6c-19.6 10.3-42.4-6.3-38.7-28.1l36.8-214.8c1.5-8.6-1.4-17.5-7.7-23.6L141.1 452c-15.8-15.4-7.1-42.3 14.8-45.5l215.7-31.3c8.7-1.3 16.2-6.7 20.1-14.6l96.5-195.4c9.7-19.9 37.9-19.9 47.7 0z - M938.666667 426.666667h-341.333334V85.333333c0-46.933333-38.4-85.333333-85.333333-85.333333s-85.333333 38.4-85.333333 85.333333v341.333334H85.333333c-46.933333 0-85.333333 38.4-85.333333 85.333333s38.4 85.333333 85.333333 85.333333h341.333334v341.333334c0 46.933333 38.4 85.333333 85.333333 85.333333s85.333333-38.4 85.333333-85.333333v-341.333334h341.333334c46.933333 0 85.333333-38.4 85.333333-85.333333s-38.4-85.333333-85.333333-85.333333z + M8,0 C8.5522847,0 9,0.44771525 9,1 L9,7 15,7 C15.552285,7 16,7.4477153 16,8 16,8.5522852 15.552285,9 15,9 L9,9 9,15 C9,15.552285 8.5522847,16 8,16 7.4477153,16 7,15.552285 7,15 L7,9 1,9 C0.44771528,9 0,8.5522852 0,8 0,7.4477153 0.44771528,7 1,7 L7,7 7,1 C7,0.44771525 7.4477153,0 8,0 z + M10.899495,0 C11.155417,0 11.411339,0.097630501 11.606602,0.29289246 11.997126,0.68341732 11.997126,1.3165817 11.606602,1.7071066 L7.3639612,5.9497471 11.606602,10.192388 C11.997126,10.582912 11.997126,11.216077 11.606602,11.606601 11.216077,11.997126 10.582912,11.997126 10.192388,11.606601 L5.9497476,7.3639607 1.7071068,11.606601 C1.3165827,11.997126 0.68341756,11.997126 0.29289341,11.606601 -0.097630978,11.216077 -0.097630978,10.582912 0.29289341,10.192388 L4.5355338,5.9497471 0.29289341,1.7071066 C-0.097630978,1.3165817 -0.097630978,0.68341732 0.29289341,0.29289246 0.48815536,0.097630501 0.74407768,0 1,0 1.2559223,0 1.5118446,0.097630501 1.7071068,0.29289246 L5.9497476,4.5355334 10.192388,0.29289246 C10.38765,0.097630501 10.643572,0 10.899495,0 z M62.5797 488.9508c0.2632 42.5185 34.985 77.2116 77.5035 77.5035l743.0216-0.6728c42.5185 0.3215 76.7437-33.9333 76.4518-76.4805l0 0c-0.2918-42.5175-34.985-77.2106-77.5045-77.5035l-743.0216-0.087C96.513 411.4473 62.2879 446.4323 62.5797 488.9508L62.5797 488.9508z M943.644188 827.215696l-351.176649-608.204749c-42.945473-74.36249-113.147387-74.36249-156.092861 0l-351.176649 608.204749c-42.946498 74.431167-7.811716 135.14955 78.012605 135.14955l702.420949 0C951.455904 962.36422 986.555836 901.645838 943.644188 827.215696zM466.187532 391.579035c12.621133-13.644108 28.66175-20.466675 48.233578-20.466675 19.580028 0 35.612444 6.75389 48.241778 20.194018 12.544256 13.473954 18.820484 30.325365 18.820484 50.587035 0 17.430551-26.19759 145.621205-34.929778 238.882082l-63.105666 0c-7.666162-93.259852-36.090106-221.450507-36.090106-238.882082C447.358847 421.938226 453.643275 405.155491 466.187532 391.579035zM561.76804 835.026386c-13.268949 12.928641-29.062535 19.375023-47.345906 19.375023-18.275171 0-34.076957-6.447407-47.346931-19.375023-13.235123-12.89379-19.818859-28.517221-19.818859-46.869269 0-18.249546 6.583736-34.043131 19.818859-47.278254 13.268949-13.235123 29.07176-19.852685 47.346931-19.852685 18.283371 0 34.076957 6.617562 47.345906 19.852685 13.235123 13.235123 19.827059 29.028709 19.827059 47.278254C581.595099 806.51019 575.003163 822.132597 561.76804 835.026386z M505.6512 39.0144c-261.2224 3.4816-470.1184 218.112-466.6368 479.4368 3.4816 261.12 218.112 470.1184 479.3344 466.6368 261.2224-3.4816 470.1184-218.112 466.7392-479.3344C981.504 244.4288 766.8736 35.5328 505.6512 39.0144zM558.08 196.608c48.128 0 62.2592 27.9552 62.2592 59.8016 0 39.8336-31.9488 76.6976-86.3232 76.6976-45.568 0-67.1744-22.9376-65.9456-60.8256C468.0704 240.4352 494.7968 196.608 558.08 196.608zM434.7904 807.6288c-32.8704 0-56.9344-19.968-33.8944-107.6224l37.6832-155.5456c6.5536-24.8832 7.68-34.9184 0-34.9184-9.8304 0-52.5312 17.2032-77.7216 34.2016l-16.384-26.9312c79.9744-66.7648 171.8272-105.8816 211.2512-105.8816 32.8704 0 38.2976 38.912 21.9136 98.6112l-43.2128 163.5328c-7.68 28.8768-4.4032 38.912 3.2768 38.912 9.9328 0 42.1888-11.9808 73.9328-36.9664l18.6368 24.8832C552.5504 777.728 467.6608 807.6288 434.7904 807.6288z diff --git a/src/Shared/HandyControl_Shared/Themes/Styles/Base/ImageSelectorBaseStyle.xaml b/src/Shared/HandyControl_Shared/Themes/Styles/Base/ImageSelectorBaseStyle.xaml index 9e7d50cb..9d74e652 100644 --- a/src/Shared/HandyControl_Shared/Themes/Styles/Base/ImageSelectorBaseStyle.xaml +++ b/src/Shared/HandyControl_Shared/Themes/Styles/Base/ImageSelectorBaseStyle.xaml @@ -1,7 +1,55 @@  + xmlns:hc="clr-namespace:HandyControl.Controls" + xmlns:themes="clr-namespace:HandyControl.Themes" + xmlns:interactivity="clr-namespace:HandyControl.Interactivity"> - \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Themes/Styles/Image.xaml b/src/Shared/HandyControl_Shared/Themes/Styles/Image.xaml index 12c5dff7..1e115576 100644 --- a/src/Shared/HandyControl_Shared/Themes/Styles/Image.xaml +++ b/src/Shared/HandyControl_Shared/Themes/Styles/Image.xaml @@ -2,7 +2,7 @@ \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Tools/Extension/ValueExtension.cs b/src/Shared/HandyControl_Shared/Tools/Extension/ValueExtension.cs index c4606424..78afef86 100644 --- a/src/Shared/HandyControl_Shared/Tools/Extension/ValueExtension.cs +++ b/src/Shared/HandyControl_Shared/Tools/Extension/ValueExtension.cs @@ -1,9 +1,21 @@ using System.Windows; +using HandyControl.Expression.Drawing; namespace HandyControl.Tools.Extension { public static class ValueExtension { public static Thickness Add(this Thickness a, Thickness b) => new Thickness(a.Left + b.Left, a.Top + b.Top, a.Right + b.Right, a.Bottom + b.Bottom); + + public static bool IsZero(this Thickness thickness) => + MathHelper.IsZero(thickness.Left) + && MathHelper.IsZero(thickness.Top) + && MathHelper.IsZero(thickness.Right) + && MathHelper.IsZero(thickness.Bottom); + + public static bool IsUniform(this Thickness thickness) => + MathHelper.AreClose(thickness.Left, thickness.Top) + && MathHelper.AreClose(thickness.Left, thickness.Right) + && MathHelper.AreClose(thickness.Left, thickness.Bottom); } } \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Tools/GifImageAnimator.cs b/src/Shared/HandyControl_Shared/Tools/GifImageAnimator.cs index 2c6a58b3..9f3a6671 100644 --- a/src/Shared/HandyControl_Shared/Tools/GifImageAnimator.cs +++ b/src/Shared/HandyControl_Shared/Tools/GifImageAnimator.cs @@ -92,10 +92,7 @@ namespace HandyControl.Tools { // Construct the image array // - if (ImageInfoList == null) - { - ImageInfoList = new List(); - } + ImageInfoList ??= new List(); // Add the new image // @@ -108,7 +105,7 @@ namespace HandyControl.Tools { AnimationThread = new Thread(AnimateImages50Ms) { - Name = typeof(ImageAnimator).Name, + Name = nameof(ImageAnimator), IsBackground = true }; AnimationThread.Start(); @@ -168,13 +165,18 @@ namespace HandyControl.Tools if (Equals(image, imageInfo.Image)) { - if ((onFrameChangedHandler == imageInfo.FrameChangedHandler) || (onFrameChangedHandler != null && onFrameChangedHandler.Equals(imageInfo.FrameChangedHandler))) + if (onFrameChangedHandler == imageInfo.FrameChangedHandler || onFrameChangedHandler != null && onFrameChangedHandler.Equals(imageInfo.FrameChangedHandler)) { ImageInfoList.Remove(imageInfo); } break; } } + + if (!ImageInfoList.Any()) + { + AnimationThread = null; + } } finally { @@ -224,7 +226,7 @@ namespace HandyControl.Tools [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals")] private static void AnimateImages50Ms() { - while (true) + while (ImageInfoList.Any()) { // Acquire reader-lock to access imageInfoList, elemens in the list can be modified w/o needing a writer-lock. // Observe that we don't need to check if the thread is waiting or a writer lock here since the thread this diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/DpiHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/DpiHelper.cs index 623bd29a..7f56eb4b 100644 --- a/src/Shared/HandyControl_Shared/Tools/Helper/DpiHelper.cs +++ b/src/Shared/HandyControl_Shared/Tools/Helper/DpiHelper.cs @@ -1,6 +1,7 @@ using System; using System.Windows; using System.Windows.Media; +using HandyControl.Expression.Drawing; using HandyControl.Tools.Interop; namespace HandyControl.Tools @@ -79,5 +80,25 @@ namespace HandyControl.Tools result.Transform(TransformFromDevice.Matrix); return result; } + + public static double RoundLayoutValue(double value, double dpiScale) + { + double newValue; + + if (!MathHelper.AreClose(dpiScale, 1.0)) + { + newValue = Math.Round(value * dpiScale) / dpiScale; + if (double.IsNaN(newValue) || double.IsInfinity(newValue) || MathHelper.AreClose(newValue, double.MaxValue)) + { + newValue = value; + } + } + else + { + newValue = Math.Round(value); + } + + return newValue; + } } } \ No newline at end of file