-
-
Save mouadcherkaoui/6a40c23220c762c4e6d95251a2b69d84 to your computer and use it in GitHub Desktop.
WPF Create Visual with border that appears to be only inside of drawn geometry
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<UserControl x:Class="Wpf3DPlayer.InformationsIcon" | |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |
xmlns:local="clr-namespace:Wpf3DPlayer" | |
mc:Ignorable="d" | |
d:DesignHeight="50" d:DesignWidth="50"> | |
<Grid> | |
<Viewbox xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Stretch="Uniform"> | |
<Canvas Name="svg110" Width="50" Height="50" Canvas.Left="0" Canvas.Top="0"> | |
<Canvas.RenderTransform> | |
<TranslateTransform X="0" Y="0"/> | |
</Canvas.RenderTransform> | |
<Canvas.Resources/> | |
<!--Unknown tag: metadata--> | |
<!--Unknown tag: sodipodi:namedview--> | |
<Canvas Name="g108"> | |
<Canvas Name="g76"> | |
<Canvas Name="g74"> | |
<Canvas Name="g72"> | |
<Path Name="path64" Fill="#FF196DA5" StrokeThickness="0.10175"> | |
<Path.Data> | |
<PathGeometry Figures="m 26.183089 19.14069 c 2.397621 0 4.341338 -1.943717 4.341338 -4.341338 0 -2.39762 -1.943717 -4.341337 -4.341338 -4.341337 -2.39762 0 -4.341337 1.943615 -4.341337 4.341337 0.0028 2.396502 1.944937 4.338489 4.341337 4.341338 z m 0 -6.94612 c 1.438533 0 2.604783 1.166149 2.604783 2.604782 0 1.438634 -1.16625 2.604782 -2.604783 2.604782 -1.43853 0 -2.604782 -1.16625 -2.604782 -2.604782 0.0017 -1.437921 1.166862 -2.603154 2.604782 -2.604782 z" FillRule="NonZero"/> | |
</Path.Data> | |
</Path> | |
<Path Name="path66" Fill="#FF196DA5" StrokeThickness="0.10175"> | |
<Path.Data> | |
<PathGeometry Figures="M 32.260983 33.032827 H 30.524427 V 22.613699 c -0.0011 -0.95858 -0.777975 -1.735436 -1.736555 -1.736555 H 22.71008 c -0.95858 0.0011 -1.735435 0.777975 -1.736555 1.736555 v 1.736555 c 0.0011 0.95858 0.777975 1.735436 1.736555 1.736555 l 0.868227 -9.17e-4 v 6.946934 h -1.736555 c -0.95858 0.0011 -1.735437 0.777975 -1.736556 1.736555 v 3.473009 c 0.0011 0.95858 0.777976 1.735436 1.736556 1.736555 h 10.419129 c 0.95858 -0.0011 1.735435 -0.777873 1.736554 -1.736555 v -3.473008 c -0.001 -0.95858 -0.777872 -1.735436 -1.736452 -1.736555 z m 0 5.209564 H 21.841854 v -3.473009 h 1.736555 c 0.95858 -0.0011 1.735436 -0.777975 1.736555 -1.736555 v -6.94612 c -0.0011 -0.95858 -0.777873 -1.735436 -1.736555 -1.736555 h -0.868227 v -1.736453 h 6.077792 v 10.419128 c 0.0011 0.95858 0.777873 1.735436 1.736555 1.736555 h 1.736555 v 3.473009 z" FillRule="NonZero"/> | |
</Path.Data> | |
</Path> | |
<Path Name="path70" Fill="#FF196DA5" StrokeThickness="0.10175"> | |
<Path.Data> | |
<PathGeometry Figures="m 26.183089 5.248553 | |
c -11.508659 0 -20.8382564 9.329596 -20.8382564 20.838256 0.01312 11.503165 9.3350914 20.825131 20.8382564 20.838257 11.50866 0 20.838258 -9.329597 20.838258 -20.838257 0 -11.50866 -9.329598 -20.838256 -20.838258 -20.838256 | |
z m 0 39.939957 | |
c -10.54957 0 -19.1017004 -8.55213 -19.1017004 -19.101701 0.0119 -10.544686 8.5571154 -19.089898 19.1017004 -19.101803 10.549571 0 19.101702 8.552131 19.101702 19.101701 0 10.549571 -8.552028 19.101803 -19.101702 19.101803 z" | |
FillRule="NonZero"/> | |
</Path.Data> | |
</Path> | |
<Path Name="path705" Fill="#FF196DA5" StrokeThickness="0.122099"> | |
<Path.Data> | |
<PathGeometry Figures="m 26.279765 1.274252 | |
c -13.810391 0 -25.0059075 11.195516 -25.0059075 25.005908 0.01575 13.803799 11.2021095 24.990158 25.0059075 25.005908 13.810393 0 25.005909 -11.195516 25.005909 -25.005908 0 -13.810392 -11.195516 -25.005908 -25.005909 -25.005908 | |
z m 0 47.92795 | |
C 13.62028 49.202202 3.3577238 38.939645 3.3577238 26.28016 3.3720088 13.626535 13.626264 3.372282 26.279765 3.357996 | |
c 12.659486 0 22.922042 10.262557 22.922042 22.922042 0 12.659485 -10.262433 22.922164 -22.922042 22.922164 z" | |
FillRule="NonZero"/> | |
</Path.Data> | |
</Path> | |
</Canvas> | |
</Canvas> | |
</Canvas> | |
</Canvas> | |
</Canvas> | |
</Viewbox> | |
</Grid> | |
</UserControl> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using System.Windows; | |
using System.Windows.Markup; | |
using System.Windows.Media; | |
using System.Windows.Shapes; | |
using SysFlowDirection = System.Windows.FlowDirection; | |
using System.ComponentModel; | |
using System.Globalization; | |
using System.Windows.Documents; | |
namespace DemoWPF.Controls | |
{ | |
[ContentProperty("Text")] | |
public class TextPath : Shape | |
{ | |
Geometry _textGeometry = Geometry.Empty; | |
#region dependency properties | |
public string Text | |
{ | |
get { return (string)GetValue(TextProperty); } | |
set { SetValue(TextProperty, value); } | |
} | |
// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty TextProperty = | |
DependencyProperty.Register("Text", typeof(string), typeof(TextPath)); | |
public FontFamily FontFamily | |
{ | |
get { return (FontFamily)GetValue(FontFamilyProperty); } | |
set { SetValue(FontFamilyProperty, value); } | |
} | |
// Using a DependencyProperty as the backing store for FontFamily. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty FontFamilyProperty = | |
DependencyProperty.Register("FontFamily", typeof(FontFamily), typeof(TextPath)); | |
public FontStyle FontStyle | |
{ | |
get { return (FontStyle)GetValue(FontStyleProperty); } | |
set { SetValue(FontStyleProperty, value); } | |
} | |
// Using a DependencyProperty as the backing store for FontStyle. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty FontStyleProperty = | |
DependencyProperty.Register("FontStyle", typeof(FontStyle), typeof(TextPath)); | |
public FontWeight FontWeight | |
{ | |
get { return (FontWeight)GetValue(FontWeightProperty); } | |
set { SetValue(FontWeightProperty, value); } | |
} | |
// Using a DependencyProperty as the backing store for FontWeight. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty FontWeightProperty = | |
DependencyProperty.Register("FontWeight", typeof(FontWeight), typeof(TextPath)); | |
public FontStretch FontStretch | |
{ | |
get { return (FontStretch)GetValue(FontStretchProperty); } | |
set { SetValue(FontStretchProperty, value); } | |
} | |
// Using a DependencyProperty as the backing store for FontStretch. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty FontStretchProperty = | |
DependencyProperty.Register("FontStretch", typeof(FontStretch), typeof(TextPath)); | |
public int FontSize | |
{ | |
get { return (int)GetValue(FontSizeProperty); } | |
set { SetValue(FontSizeProperty, value); } | |
} | |
// Using a DependencyProperty as the backing store for FontSize. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty FontSizeProperty = | |
DependencyProperty.Register("FontSize", typeof(int), typeof(TextPath)); | |
#endregion | |
#region shape members overrides | |
protected override Size MeasureOverride(Size availableSize) | |
{ | |
if (_textGeometry == null) createTextGeometry(); | |
if (_textGeometry.Bounds == Rect.Empty) | |
return new Size(0, 0); | |
// return the desired size | |
return new Size(Math.Min(availableSize.Width, _textGeometry.Bounds.Width), | |
Math.Min(availableSize.Height, _textGeometry.Bounds.Height)); | |
} | |
protected override Geometry DefiningGeometry => _textGeometry ?? Geometry.Empty; | |
private void createTextGeometry() | |
{ | |
var origin = new Point(0, 0); | |
var currentCulture = Thread.CurrentThread.CurrentUICulture; | |
var typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch); | |
var formattedText = new FormattedText(Text, currentCulture, SysFlowDirection.LeftToRight, typeface, FontSize, Brushes.Black); | |
_textGeometry = formattedText.BuildGeometry(origin); | |
} | |
/// <summary> | |
/// OnRender override draws the geometry of the text and optional highlight. | |
/// </summary> | |
/// <param name="drawingContext">Drawing context of the OutlineText control.</param> | |
protected override void OnRender(DrawingContext drawingContext) | |
{ | |
drawingContext.DrawGeometry(Fill, new System.Windows.Media.Pen(Stroke, StrokeThickness), _textGeometry); | |
} | |
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => ((TextPath)d).createTextGeometry(); | |
#endregion | |
} | |
[ContentProperty("Text")] | |
public class OutlinedTextBlock : FrameworkElement | |
{ | |
public static readonly DependencyProperty FillProperty = DependencyProperty.Register( | |
"Fill", | |
typeof(Brush), | |
typeof(OutlinedTextBlock), | |
new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender)); | |
public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register( | |
"Stroke", | |
typeof(Brush), | |
typeof(OutlinedTextBlock), | |
new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender)); | |
public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register( | |
"StrokeThickness", | |
typeof(double), | |
typeof(OutlinedTextBlock), | |
new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsRender)); | |
public static readonly DependencyProperty FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner( | |
typeof(OutlinedTextBlock), | |
new FrameworkPropertyMetadata(OnFormattedTextUpdated)); | |
public static readonly DependencyProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner( | |
typeof(OutlinedTextBlock), | |
new FrameworkPropertyMetadata(OnFormattedTextUpdated)); | |
public static readonly DependencyProperty FontStretchProperty = TextElement.FontStretchProperty.AddOwner( | |
typeof(OutlinedTextBlock), | |
new FrameworkPropertyMetadata(OnFormattedTextUpdated)); | |
public static readonly DependencyProperty FontStyleProperty = TextElement.FontStyleProperty.AddOwner( | |
typeof(OutlinedTextBlock), | |
new FrameworkPropertyMetadata(OnFormattedTextUpdated)); | |
public static readonly DependencyProperty FontWeightProperty = TextElement.FontWeightProperty.AddOwner( | |
typeof(OutlinedTextBlock), | |
new FrameworkPropertyMetadata(OnFormattedTextUpdated)); | |
public static readonly DependencyProperty TextProperty = DependencyProperty.Register( | |
"Text", | |
typeof(string), | |
typeof(OutlinedTextBlock), | |
new FrameworkPropertyMetadata(OnFormattedTextInvalidated)); | |
public static readonly DependencyProperty TextAlignmentProperty = DependencyProperty.Register( | |
"TextAlignment", | |
typeof(TextAlignment), | |
typeof(OutlinedTextBlock), | |
new FrameworkPropertyMetadata(OnFormattedTextUpdated)); | |
public static readonly DependencyProperty TextDecorationsProperty = DependencyProperty.Register( | |
"TextDecorations", | |
typeof(TextDecorationCollection), | |
typeof(OutlinedTextBlock), | |
new FrameworkPropertyMetadata(OnFormattedTextUpdated)); | |
public static readonly DependencyProperty TextTrimmingProperty = DependencyProperty.Register( | |
"TextTrimming", | |
typeof(TextTrimming), | |
typeof(OutlinedTextBlock), | |
new FrameworkPropertyMetadata(OnFormattedTextUpdated)); | |
public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register( | |
"TextWrapping", | |
typeof(TextWrapping), | |
typeof(OutlinedTextBlock), | |
new FrameworkPropertyMetadata(TextWrapping.NoWrap, OnFormattedTextUpdated)); | |
private FormattedText formattedText; | |
private Geometry textGeometry; | |
public OutlinedTextBlock() | |
{ | |
this.TextDecorations = new TextDecorationCollection(); | |
} | |
public Brush Fill | |
{ | |
get { return (Brush)GetValue(FillProperty); } | |
set { SetValue(FillProperty, value); } | |
} | |
public FontFamily FontFamily | |
{ | |
get { return (FontFamily)GetValue(FontFamilyProperty); } | |
set { SetValue(FontFamilyProperty, value); } | |
} | |
[TypeConverter(typeof(FontSizeConverter))] | |
public double FontSize | |
{ | |
get { return (double)GetValue(FontSizeProperty); } | |
set { SetValue(FontSizeProperty, value); } | |
} | |
public FontStretch FontStretch | |
{ | |
get { return (FontStretch)GetValue(FontStretchProperty); } | |
set { SetValue(FontStretchProperty, value); } | |
} | |
public FontStyle FontStyle | |
{ | |
get { return (FontStyle)GetValue(FontStyleProperty); } | |
set { SetValue(FontStyleProperty, value); } | |
} | |
public FontWeight FontWeight | |
{ | |
get { return (FontWeight)GetValue(FontWeightProperty); } | |
set { SetValue(FontWeightProperty, value); } | |
} | |
public Brush Stroke | |
{ | |
get { return (Brush)GetValue(StrokeProperty); } | |
set { SetValue(StrokeProperty, value); } | |
} | |
public double StrokeThickness | |
{ | |
get { return (double)GetValue(StrokeThicknessProperty); } | |
set { SetValue(StrokeThicknessProperty, value); } | |
} | |
public string Text | |
{ | |
get { return (string)GetValue(TextProperty); } | |
set { SetValue(TextProperty, value); } | |
} | |
public TextAlignment TextAlignment | |
{ | |
get { return (TextAlignment)GetValue(TextAlignmentProperty); } | |
set { SetValue(TextAlignmentProperty, value); } | |
} | |
public TextDecorationCollection TextDecorations | |
{ | |
get { return (TextDecorationCollection)this.GetValue(TextDecorationsProperty); } | |
set { this.SetValue(TextDecorationsProperty, value); } | |
} | |
public TextTrimming TextTrimming | |
{ | |
get { return (TextTrimming)GetValue(TextTrimmingProperty); } | |
set { SetValue(TextTrimmingProperty, value); } | |
} | |
public TextWrapping TextWrapping | |
{ | |
get { return (TextWrapping)GetValue(TextWrappingProperty); } | |
set { SetValue(TextWrappingProperty, value); } | |
} | |
protected override void OnRender(DrawingContext drawingContext) | |
{ | |
this.EnsureGeometry(); | |
drawingContext.DrawGeometry(this.Fill, new Pen(this.Stroke, this.StrokeThickness), this.textGeometry); | |
} | |
protected override Size MeasureOverride(Size availableSize) | |
{ | |
this.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 | |
this.formattedText.MaxTextWidth = Math.Min(3579139, availableSize.Width); | |
this.formattedText.MaxTextHeight = Math.Max(0.0001d, availableSize.Height); | |
// return the desired size | |
return new Size(this.formattedText.Width, this.formattedText.Height); | |
} | |
protected override Size ArrangeOverride(Size finalSize) | |
{ | |
this.EnsureFormattedText(); | |
// update the formatted text with the final size | |
this.formattedText.MaxTextWidth = finalSize.Width; | |
this.formattedText.MaxTextHeight = finalSize.Height; | |
// need to re-generate the geometry now that the dimensions have changed | |
this.textGeometry = null; | |
return finalSize; | |
} | |
private static void OnFormattedTextInvalidated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) | |
{ | |
var outlinedTextBlock = (OutlinedTextBlock)dependencyObject; | |
outlinedTextBlock.formattedText = null; | |
outlinedTextBlock.textGeometry = null; | |
outlinedTextBlock.InvalidateMeasure(); | |
outlinedTextBlock.InvalidateVisual(); | |
} | |
private static void OnFormattedTextUpdated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) | |
{ | |
var outlinedTextBlock = (OutlinedTextBlock)dependencyObject; | |
outlinedTextBlock.UpdateFormattedText(); | |
outlinedTextBlock.textGeometry = null; | |
outlinedTextBlock.InvalidateMeasure(); | |
outlinedTextBlock.InvalidateVisual(); | |
} | |
private void EnsureFormattedText() | |
{ | |
if (this.formattedText != null || this.Text == null) | |
{ | |
return; | |
} | |
this.formattedText = new FormattedText( | |
this.Text, | |
CultureInfo.CurrentUICulture, | |
this.FlowDirection, | |
new Typeface(this.FontFamily, this.FontStyle, this.FontWeight, FontStretches.Normal), | |
this.FontSize, | |
Brushes.Black); | |
this.UpdateFormattedText(); | |
} | |
private void UpdateFormattedText() | |
{ | |
if (this.formattedText == null) | |
{ | |
return; | |
} | |
this.formattedText.MaxLineCount = this.TextWrapping == TextWrapping.NoWrap ? 1 : int.MaxValue; | |
this.formattedText.TextAlignment = this.TextAlignment; | |
this.formattedText.Trimming = this.TextTrimming; | |
this.formattedText.SetFontSize(this.FontSize); | |
this.formattedText.SetFontStyle(this.FontStyle); | |
this.formattedText.SetFontWeight(this.FontWeight); | |
this.formattedText.SetFontFamily(this.FontFamily); | |
this.formattedText.SetFontStretch(this.FontStretch); | |
this.formattedText.SetTextDecorations(this.TextDecorations); | |
} | |
private void EnsureGeometry() | |
{ | |
if (this.textGeometry != null) | |
{ | |
return; | |
} | |
this.EnsureFormattedText(); | |
this.textGeometry = this.formattedText.BuildGeometry(new Point(0, 0)); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private static DrawingVisual CreateVisual() | |
{ | |
var noBorderPen = new Pen(new SolidColorBrush(Colors.Transparent), 0); | |
var selectionPen = new Pen(new SolidColorBrush(Colors.Yellow), 20 * 2); | |
var drawingVisual = new DrawingVisual(); | |
using (var dc = drawingVisual.RenderOpen()) | |
{ | |
StreamGeometry geometry = new StreamGeometry(); | |
using (var ctx = geometry.Open()) | |
{ | |
ctx.BeginFigure(new Point(100, 100), true, true); | |
ctx.LineTo(new Point(300, 100), true, false); | |
ctx.LineTo(new Point(300, 200), true, false); | |
} | |
// Draw the geometry | |
dc.DrawGeometry(new SolidColorBrush(Colors.Fuchsia), noBorderPen, geometry); | |
// Apply a clip path to only draw the geometry and hide everything outside | |
dc.PushClip(geometry); | |
// Draw the geometry on top with the selection border | |
dc.DrawGeometry(Brushes.Transparent, selectionPen, geometry); | |
// Remove the clip path | |
dc.Pop(); | |
geometry.Freeze(); | |
} | |
return drawingVisual; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment