diff --git a/HandyControl/Controls/WatermarkTextBox.cs b/HandyControl/Controls/Input/WatermarkTextBox.cs similarity index 100% rename from HandyControl/Controls/WatermarkTextBox.cs rename to HandyControl/Controls/Input/WatermarkTextBox.cs diff --git a/HandyControl/Controls/AnimationPath.cs b/HandyControl/Controls/Other/AnimationPath.cs similarity index 100% rename from HandyControl/Controls/AnimationPath.cs rename to HandyControl/Controls/Other/AnimationPath.cs diff --git a/HandyControl/Controls/Carousel.cs b/HandyControl/Controls/Other/Carousel.cs similarity index 100% rename from HandyControl/Controls/Carousel.cs rename to HandyControl/Controls/Other/Carousel.cs diff --git a/HandyControl/Controls/CirclePanel.cs b/HandyControl/Controls/Other/CirclePanel.cs similarity index 100% rename from HandyControl/Controls/CirclePanel.cs rename to HandyControl/Controls/Other/CirclePanel.cs diff --git a/HandyControl/Controls/CircleProgressBar.cs b/HandyControl/Controls/Other/CircleProgressBar.cs similarity index 100% rename from HandyControl/Controls/CircleProgressBar.cs rename to HandyControl/Controls/Other/CircleProgressBar.cs diff --git a/HandyControl/Controls/ColorPicker.cs b/HandyControl/Controls/Other/ColorPicker.cs similarity index 100% rename from HandyControl/Controls/ColorPicker.cs rename to HandyControl/Controls/Other/ColorPicker.cs diff --git a/HandyControl/Controls/GifImage.cs b/HandyControl/Controls/Other/GifImage.cs similarity index 100% rename from HandyControl/Controls/GifImage.cs rename to HandyControl/Controls/Other/GifImage.cs diff --git a/HandyControl/Controls/Growl.cs b/HandyControl/Controls/Other/Growl.cs similarity index 100% rename from HandyControl/Controls/Growl.cs rename to HandyControl/Controls/Other/Growl.cs diff --git a/HandyControl/Controls/ISingleOpen.cs b/HandyControl/Controls/Other/ISingleOpen.cs similarity index 100% rename from HandyControl/Controls/ISingleOpen.cs rename to HandyControl/Controls/Other/ISingleOpen.cs diff --git a/HandyControl/Controls/ImageViewer.cs b/HandyControl/Controls/Other/ImageViewer.cs similarity index 100% rename from HandyControl/Controls/ImageViewer.cs rename to HandyControl/Controls/Other/ImageViewer.cs diff --git a/HandyControl/Controls/InternalCanvas.cs b/HandyControl/Controls/Other/InternalCanvas.cs similarity index 100% rename from HandyControl/Controls/InternalCanvas.cs rename to HandyControl/Controls/Other/InternalCanvas.cs diff --git a/HandyControl/Controls/Pagination.cs b/HandyControl/Controls/Other/Pagination.cs similarity index 100% rename from HandyControl/Controls/Pagination.cs rename to HandyControl/Controls/Other/Pagination.cs diff --git a/HandyControl/Controls/ScrollViewer.cs b/HandyControl/Controls/Other/ScrollViewer.cs similarity index 100% rename from HandyControl/Controls/ScrollViewer.cs rename to HandyControl/Controls/Other/ScrollViewer.cs diff --git a/HandyControl/Controls/TransitioningContentControl.cs b/HandyControl/Controls/Other/TransitioningContentControl.cs similarity index 100% rename from HandyControl/Controls/TransitioningContentControl.cs rename to HandyControl/Controls/Other/TransitioningContentControl.cs diff --git a/HandyControl/Controls/Text/OutlineText.cs b/HandyControl/Controls/Text/OutlineText.cs new file mode 100644 index 00000000..42161c24 --- /dev/null +++ b/HandyControl/Controls/Text/OutlineText.cs @@ -0,0 +1,236 @@ +using System; +using System.ComponentModel; +using System.Globalization; +using System.Windows; +using System.Windows.Documents; +using System.Windows.Media; +using HandyControl.Data; + +namespace HandyControl.Controls +{ + public class OutlineText : FrameworkElement + { + private FormattedText _formattedText; + + private Geometry _textGeometry; + + static OutlineText() + { + SnapsToDevicePixelsProperty.OverrideMetadata(typeof(OutlineText), new FrameworkPropertyMetadata(ValueBoxes.TrueBox)); + UseLayoutRoundingProperty.OverrideMetadata(typeof(OutlineText), new FrameworkPropertyMetadata(ValueBoxes.TrueBox)); + } + + public static readonly DependencyProperty TextProperty = DependencyProperty.Register( + "Text", typeof(string), typeof(OutlineText), new FrameworkPropertyMetadata( + string.Empty, + FrameworkPropertyMetadataOptions.AffectsMeasure | + FrameworkPropertyMetadataOptions.AffectsRender, OnFormattedTextInvalidated)); + + public string Text + { + get => (string) GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public static readonly DependencyProperty TextAlignmentProperty = DependencyProperty.Register( + "TextAlignment", typeof(TextAlignment), typeof(OutlineText), + new PropertyMetadata(default(TextAlignment), OnFormattedTextUpdated)); + + public TextAlignment TextAlignment + { + get => (TextAlignment) GetValue(TextAlignmentProperty); + set => SetValue(TextAlignmentProperty, value); + } + + public static readonly DependencyProperty TextTrimmingProperty = DependencyProperty.Register( + "TextTrimming", typeof(TextTrimming), typeof(OutlineText), + new PropertyMetadata(default(TextTrimming), OnFormattedTextInvalidated)); + + public TextTrimming TextTrimming + { + get => (TextTrimming) GetValue(TextTrimmingProperty); + set => SetValue(TextTrimmingProperty, value); + } + + public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register( + "TextWrapping", typeof(TextWrapping), typeof(OutlineText), + new PropertyMetadata(TextWrapping.NoWrap, OnFormattedTextInvalidated)); + + public TextWrapping TextWrapping + { + get => (TextWrapping) GetValue(TextWrappingProperty); + set => SetValue(TextWrappingProperty, value); + } + + public static readonly DependencyProperty FillProperty = DependencyProperty.Register( + "Fill", typeof(Brush), typeof(OutlineText), new PropertyMetadata(Brushes.Black, OnFormattedTextUpdated)); + + public Brush Fill + { + get => (Brush) GetValue(FillProperty); + set => SetValue(FillProperty, value); + } + + public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register( + "Stroke", typeof(Brush), typeof(OutlineText), new PropertyMetadata(Brushes.Black, OnFormattedTextUpdated)); + + public Brush Stroke + { + get => (Brush) GetValue(StrokeProperty); + set => SetValue(StrokeProperty, value); + } + + public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register( + "StrokeThickness", typeof(double), typeof(OutlineText), new PropertyMetadata(ValueBoxes.Double0Box, OnFormattedTextUpdated)); + + public double StrokeThickness + { + get => (double) GetValue(StrokeThicknessProperty); + set => SetValue(StrokeThicknessProperty, value); + } + + public static readonly DependencyProperty FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner( + typeof(OutlineText), + new FrameworkPropertyMetadata(OnFormattedTextUpdated)); + + public FontFamily FontFamily + { + get => (FontFamily)GetValue(FontFamilyProperty); + set => SetValue(FontFamilyProperty, value); + } + + public static readonly DependencyProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner( + typeof(OutlineText), + new FrameworkPropertyMetadata(OnFormattedTextUpdated)); + + [TypeConverter(typeof(FontSizeConverter))] + public double FontSize + { + get => (double)GetValue(FontSizeProperty); + set => SetValue(FontSizeProperty, value); + } + + public static readonly DependencyProperty FontStretchProperty = TextElement.FontStretchProperty.AddOwner( + typeof(OutlineText), + new FrameworkPropertyMetadata(OnFormattedTextUpdated)); + + public FontStretch FontStretch + { + get => (FontStretch)GetValue(FontStretchProperty); + set => SetValue(FontStretchProperty, value); + } + + public static readonly DependencyProperty FontStyleProperty = TextElement.FontStyleProperty.AddOwner( + typeof(OutlineText), + new FrameworkPropertyMetadata(OnFormattedTextUpdated)); + + public FontStyle FontStyle + { + get => (FontStyle)GetValue(FontStyleProperty); + set => SetValue(FontStyleProperty, value); + } + + public static readonly DependencyProperty FontWeightProperty = TextElement.FontWeightProperty.AddOwner( + typeof(OutlineText), + new FrameworkPropertyMetadata(OnFormattedTextUpdated)); + + public FontWeight FontWeight + { + get => (FontWeight)GetValue(FontWeightProperty); + set => SetValue(FontWeightProperty, value); + } + + protected override void OnRender(DrawingContext drawingContext) + { + EnsureGeometry(); + drawingContext.DrawGeometry(Fill, new Pen(Stroke, StrokeThickness), _textGeometry); + } + + private void EnsureFormattedText() + { + if (_formattedText != null || Text == null) + { + return; + } + + var source = PresentationSource.FromVisual(this); + var dpi = 1.0; + if (source?.CompositionTarget != null) + { + dpi = 96.0 * source.CompositionTarget.TransformToDevice.M11 / 96.0; + } + + _formattedText = new FormattedText( + Text, + CultureInfo.CurrentUICulture, + FlowDirection, + new Typeface(FontFamily, FontStyle, FontWeight, FontStretch), + FontSize, Fill, dpi); + + UpdateFormattedText(); + } + + private void EnsureGeometry() + { + if (_textGeometry != null) + { + return; + } + + EnsureFormattedText(); + _textGeometry = _formattedText.BuildGeometry(new Point(0, 0)); + } + + private void UpdateFormattedText() + { + if (_formattedText == null) + { + return; + } + + _formattedText.MaxLineCount = TextWrapping == TextWrapping.NoWrap ? 1 : int.MaxValue; + _formattedText.TextAlignment = TextAlignment; + _formattedText.Trimming = TextTrimming; + + _formattedText.SetFontSize(FontSize); + _formattedText.SetFontStyle(FontStyle); + _formattedText.SetFontWeight(FontWeight); + _formattedText.SetFontFamily(FontFamily); + _formattedText.SetFontStretch(FontStretch); + } + + private static void OnFormattedTextUpdated(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var outlinedTextBlock = (OutlineText)d; + outlinedTextBlock.UpdateFormattedText(); + outlinedTextBlock._textGeometry = null; + + outlinedTextBlock.InvalidateMeasure(); + outlinedTextBlock.InvalidateVisual(); + } + + private static void OnFormattedTextInvalidated(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var outlinedTextBlock = (OutlineText)d; + outlinedTextBlock._formattedText = null; + outlinedTextBlock._textGeometry = null; + + outlinedTextBlock.InvalidateMeasure(); + outlinedTextBlock.InvalidateVisual(); + } + + protected override Size MeasureOverride(Size availableSize) + { + EnsureFormattedText(); + + // constrain the formatted text according to the available size + // the Math.Min call is important - without this constraint (which seems arbitrary, but is the maximum allowable text width), things blow up when availableSize is infinite in both directions + // the Math.Max call is to ensure we don't hit zero, which will cause MaxTextHeight to throw + _formattedText.MaxTextWidth = Math.Min(3579139, availableSize.Width); + _formattedText.MaxTextHeight = Math.Max(0.0001d, availableSize.Height); + + // return the desired size + return new Size(_formattedText.Width, _formattedText.Height); + } + } +} \ No newline at end of file diff --git a/HandyControl/Controls/Text/StackText.cs b/HandyControl/Controls/Text/StackText.cs new file mode 100644 index 00000000..1429bd10 --- /dev/null +++ b/HandyControl/Controls/Text/StackText.cs @@ -0,0 +1,9 @@ +using System.Windows; + +namespace HandyControl.Controls +{ + public class StackText : FrameworkElement + { + + } +} \ No newline at end of file diff --git a/HandyControl/HandyControl.csproj b/HandyControl/HandyControl.csproj index 9f632c61..7ce2855a 100644 --- a/HandyControl/HandyControl.csproj +++ b/HandyControl/HandyControl.csproj @@ -33,7 +33,7 @@ bin\Release\HandyControl.xml - + @@ -46,37 +46,39 @@ - - - - + + + + + + - - + + - + - + - + - + - + @@ -140,8 +142,8 @@ - - + + diff --git a/HandyControlDemo/Data/MessageToken.cs b/HandyControlDemo/Data/MessageToken.cs index 8f205798..d5d3ac9a 100644 --- a/HandyControlDemo/Data/MessageToken.cs +++ b/HandyControlDemo/Data/MessageToken.cs @@ -56,6 +56,8 @@ public static readonly string GifImageDemoCtl = nameof(GifImageDemoCtl); + public static readonly string OutlineTextDemoCtl = nameof(OutlineTextDemoCtl); + public static readonly string ButtonDemoCtl = nameof(ButtonDemoCtl); public static readonly string ToggleButtonDemoCtl = nameof(ToggleButtonDemoCtl); diff --git a/HandyControlDemo/Data/MessageToken.tt b/HandyControlDemo/Data/MessageToken.tt index 9bbcd97a..f94dcf6c 100644 --- a/HandyControlDemo/Data/MessageToken.tt +++ b/HandyControlDemo/Data/MessageToken.tt @@ -30,7 +30,8 @@ var controlList = new List "CalendarWithClockDemoCtl", "SearchBarDemoCtl", "TagDemoCtl", - "GifImageDemoCtl" + "GifImageDemoCtl", + "OutlineTextDemoCtl" }; var styleList = new List { diff --git a/HandyControlDemo/HandyControlDemo.csproj b/HandyControlDemo/HandyControlDemo.csproj index 275c70b5..70085e16 100644 --- a/HandyControlDemo/HandyControlDemo.csproj +++ b/HandyControlDemo/HandyControlDemo.csproj @@ -129,6 +129,9 @@ DateTimePickerDemoCtl.xaml + + OutlineTextDemoCtl.xaml + GifImageDemoCtl.xaml @@ -386,6 +389,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/HandyControlDemo/Properties/Langs/Lang.Designer.cs b/HandyControlDemo/Properties/Langs/Lang.Designer.cs index 4c27efc1..ca857118 100644 --- a/HandyControlDemo/Properties/Langs/Lang.Designer.cs +++ b/HandyControlDemo/Properties/Langs/Lang.Designer.cs @@ -627,6 +627,15 @@ namespace HandyControlDemo.Properties.Langs { } } + /// + /// 查找类似 轮廓文本 的本地化字符串。 + /// + public static string OutlineText { + get { + return ResourceManager.GetString("OutlineText", resourceCulture); + } + } + /// /// 查找类似 页码条 的本地化字符串。 /// diff --git a/HandyControlDemo/Properties/Langs/Lang.en.resx b/HandyControlDemo/Properties/Langs/Lang.en.resx index 8e97e24f..a8a342e1 100644 --- a/HandyControlDemo/Properties/Langs/Lang.en.resx +++ b/HandyControlDemo/Properties/Langs/Lang.en.resx @@ -432,4 +432,7 @@ GifImage + + OutlineText + \ No newline at end of file diff --git a/HandyControlDemo/Properties/Langs/Lang.resx b/HandyControlDemo/Properties/Langs/Lang.resx index a81739de..4b8d318a 100644 --- a/HandyControlDemo/Properties/Langs/Lang.resx +++ b/HandyControlDemo/Properties/Langs/Lang.resx @@ -412,4 +412,7 @@ Gif图片 + + 轮廓文本 + \ No newline at end of file diff --git a/HandyControlDemo/UserControl/Controls/OutlineTextDemoCtl.xaml b/HandyControlDemo/UserControl/Controls/OutlineTextDemoCtl.xaml new file mode 100644 index 00000000..66a4f89f --- /dev/null +++ b/HandyControlDemo/UserControl/Controls/OutlineTextDemoCtl.xaml @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/HandyControlDemo/UserControl/Controls/OutlineTextDemoCtl.xaml.cs b/HandyControlDemo/UserControl/Controls/OutlineTextDemoCtl.xaml.cs new file mode 100644 index 00000000..1e637136 --- /dev/null +++ b/HandyControlDemo/UserControl/Controls/OutlineTextDemoCtl.xaml.cs @@ -0,0 +1,10 @@ +namespace HandyControlDemo.UserControl +{ + public partial class OutlineTextDemoCtl + { + public OutlineTextDemoCtl() + { + InitializeComponent(); + } + } +} diff --git a/HandyControlDemo/UserControl/Main/LeftMainContent.xaml b/HandyControlDemo/UserControl/Main/LeftMainContent.xaml index 5e53f45a..6e8664fb 100644 --- a/HandyControlDemo/UserControl/Main/LeftMainContent.xaml +++ b/HandyControlDemo/UserControl/Main/LeftMainContent.xaml @@ -204,6 +204,11 @@ + + + + + diff --git a/README.md b/README.md index 95f1325f..e3b7c4a3 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ ## Latest examples +### OutlineText + +![OutlineText](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/OutlineText.png) + ### Tag ![Tag](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/Tag.png) @@ -22,12 +26,12 @@ ![CircleProgressBar](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/CircleProgressBar.png) +## History publication + ### DataGrid ![DataGrid](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/DataGrid.png) -## History publication - ### Now you can switch to dark theme ![dark theme](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/DarkTheme.png) diff --git a/Resources/OutlineText.png b/Resources/OutlineText.png new file mode 100644 index 00000000..3f5ec7e1 Binary files /dev/null and b/Resources/OutlineText.png differ