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
+
+
+
### Tag

@@ -22,12 +26,12 @@

+## History publication
+
### DataGrid

-## History publication
-
### Now you can switch to dark theme

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