mirror of
https://github.com/ckaczor/Common.Wpf.git
synced 2026-01-14 01:25:37 -05:00
Initial commit
This commit is contained in:
7
HtmlLabelControl/HtmlLabel.xaml
Normal file
7
HtmlLabelControl/HtmlLabel.xaml
Normal file
@@ -0,0 +1,7 @@
|
||||
<UserControl x:Class="Common.Wpf.HtmlLabelControl.HtmlLabel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Grid>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
122
HtmlLabelControl/HtmlLabel.xaml.cs
Normal file
122
HtmlLabelControl/HtmlLabel.xaml.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Common.Wpf.HtmlLabelControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for HtmlLabel.xaml
|
||||
/// </summary>
|
||||
public partial class HtmlLabel : UserControl
|
||||
{
|
||||
private Collection<TextLine> _textLines;
|
||||
|
||||
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(HtmlLabel), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender));
|
||||
|
||||
public HtmlLabel()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
|
||||
switch (e.Property.Name)
|
||||
{
|
||||
case "FontFamily":
|
||||
case "FontSize":
|
||||
case "Foreground":
|
||||
// Force the control to re-parse
|
||||
Text = Text;
|
||||
break;
|
||||
|
||||
case "HorizontalContentAlignment":
|
||||
case "VerticalContentAlignment":
|
||||
case "Padding":
|
||||
InvalidateVisual();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext drawingContext)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Text))
|
||||
return;
|
||||
|
||||
Point drawPoint = new Point(Padding.Left, Padding.Top);
|
||||
|
||||
switch (VerticalContentAlignment)
|
||||
{
|
||||
case VerticalAlignment.Bottom:
|
||||
drawPoint.Y = ActualHeight - Padding.Bottom;
|
||||
|
||||
foreach (TextLine line in _textLines)
|
||||
{
|
||||
drawPoint.Y -= line.Height;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case VerticalAlignment.Center:
|
||||
double totalHeight = 0;
|
||||
|
||||
foreach (TextLine line in _textLines)
|
||||
{
|
||||
totalHeight += line.Height;
|
||||
}
|
||||
|
||||
drawPoint.Y = ((ActualHeight - (Padding.Top + Padding.Bottom)) / 2) - (totalHeight / 2);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (TextLine line in _textLines)
|
||||
{
|
||||
if (drawPoint.Y < 0)
|
||||
{
|
||||
drawPoint.Y += line.Height;
|
||||
continue;
|
||||
}
|
||||
|
||||
drawPoint.X = Padding.Left;
|
||||
|
||||
switch (HorizontalContentAlignment)
|
||||
{
|
||||
case HorizontalAlignment.Right:
|
||||
drawPoint.X = ActualWidth - line.Width - Padding.Right;
|
||||
break;
|
||||
|
||||
case HorizontalAlignment.Center:
|
||||
drawPoint.X = ((ActualWidth - (Padding.Left + Padding.Right)) / 2) - (line.Width / 2);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (TextFragment fragment in line.FragmentList)
|
||||
{
|
||||
drawingContext.DrawText(fragment.FormattedText, drawPoint);
|
||||
|
||||
drawPoint.X += fragment.FormattedText.WidthIncludingTrailingWhitespace;
|
||||
}
|
||||
|
||||
drawPoint.Y += line.Height;
|
||||
}
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get
|
||||
{
|
||||
return (string) GetValue(TextProperty);
|
||||
}
|
||||
set
|
||||
{
|
||||
TextParser textParser = new TextParser();
|
||||
_textLines = textParser.Parse(this, value);
|
||||
|
||||
SetValue(TextProperty, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
63
HtmlLabelControl/TextFragment.cs
Normal file
63
HtmlLabelControl/TextFragment.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Common.Wpf.HtmlLabelControl
|
||||
{
|
||||
public class TextFragment
|
||||
{
|
||||
private readonly HtmlLabel _parent;
|
||||
|
||||
public Brush Color { get; set; }
|
||||
public FontStyle Style { get; set; }
|
||||
public FontWeight Weight { get; set; }
|
||||
public double Size { get; set; }
|
||||
public string Text { get; set; }
|
||||
public bool Underline { get; set; }
|
||||
|
||||
private Typeface _typeface;
|
||||
public Typeface Typeface
|
||||
{
|
||||
get { return _typeface ?? (_typeface = new Typeface(_parent.FontFamily, Style, Weight, _parent.FontStretch)); }
|
||||
}
|
||||
|
||||
private FormattedText _formattedText;
|
||||
public FormattedText FormattedText
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_formattedText == null)
|
||||
{
|
||||
string measureText = (string.IsNullOrEmpty(Text) ? " " : Text);
|
||||
|
||||
_formattedText = new FormattedText(measureText, CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, Typeface, Size, Color);
|
||||
|
||||
TextDecorationCollection textDecorationCollection = new TextDecorationCollection();
|
||||
|
||||
if (Underline)
|
||||
{
|
||||
TextDecoration underlineDecoration = new TextDecoration { PenThicknessUnit = TextDecorationUnit.FontRecommended };
|
||||
|
||||
textDecorationCollection.Add(underlineDecoration);
|
||||
}
|
||||
|
||||
_formattedText.SetTextDecorations(textDecorationCollection);
|
||||
}
|
||||
|
||||
return _formattedText;
|
||||
}
|
||||
}
|
||||
|
||||
public TextFragment(HtmlLabel parent)
|
||||
{
|
||||
_parent = parent;
|
||||
|
||||
Color = _parent.Foreground;
|
||||
Style = _parent.FontStyle;
|
||||
Weight = _parent.FontWeight;
|
||||
Size = _parent.FontSize;
|
||||
Text = string.Empty;
|
||||
Underline = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
HtmlLabelControl/TextFragmentStyle.cs
Normal file
32
HtmlLabelControl/TextFragmentStyle.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Common.Wpf.HtmlLabelControl
|
||||
{
|
||||
public class TextFragmentStyle
|
||||
{
|
||||
public Brush Color { get; set; }
|
||||
public FontStyle? Style { get; set; }
|
||||
public FontWeight? Weight { get; set; }
|
||||
public double? Size { get; set; }
|
||||
public bool? Underline { get; set; }
|
||||
|
||||
public void Apply(TextFragment fragment)
|
||||
{
|
||||
if (Color != null)
|
||||
fragment.Color = Color;
|
||||
|
||||
if (Style.HasValue)
|
||||
fragment.Style = Style.Value;
|
||||
|
||||
if (Weight.HasValue)
|
||||
fragment.Weight = Weight.Value;
|
||||
|
||||
if (Size.HasValue)
|
||||
fragment.Size = Size.Value;
|
||||
|
||||
if (Underline.HasValue)
|
||||
fragment.Underline = Underline.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
HtmlLabelControl/TextLine.cs
Normal file
49
HtmlLabelControl/TextLine.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Common.Wpf.HtmlLabelControl
|
||||
{
|
||||
public class TextLine
|
||||
{
|
||||
private double _height;
|
||||
public double Height
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_height == 0)
|
||||
{
|
||||
foreach (TextFragment textFragment in FragmentList)
|
||||
{
|
||||
if (textFragment.FormattedText.Height > _height)
|
||||
_height = textFragment.FormattedText.Height;
|
||||
}
|
||||
}
|
||||
|
||||
return _height;
|
||||
}
|
||||
}
|
||||
|
||||
private double _width;
|
||||
public double Width
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_width == 0)
|
||||
{
|
||||
foreach (TextFragment textFragment in FragmentList)
|
||||
{
|
||||
_width += textFragment.FormattedText.Width;
|
||||
}
|
||||
}
|
||||
|
||||
return _width;
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<TextFragment> FragmentList { get; private set; }
|
||||
|
||||
public TextLine()
|
||||
{
|
||||
FragmentList = new Collection<TextFragment>();
|
||||
}
|
||||
}
|
||||
}
|
||||
134
HtmlLabelControl/TextParser.cs
Normal file
134
HtmlLabelControl/TextParser.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Xml;
|
||||
|
||||
namespace Common.Wpf.HtmlLabelControl
|
||||
{
|
||||
public class TextParser
|
||||
{
|
||||
private HtmlLabel _parentControl;
|
||||
|
||||
public Collection<TextLine> Parse(HtmlLabel parentControl, string text)
|
||||
{
|
||||
_parentControl = parentControl;
|
||||
|
||||
// Add a root tag so the parser is happy
|
||||
text = string.Format(CultureInfo.InvariantCulture, "<body>{0}</body>", text);
|
||||
|
||||
// Normalize line endings
|
||||
text = text.Replace("\r\n", "\n");
|
||||
|
||||
// Create an XML document and load it with the text
|
||||
XmlDocument xmlDocument = new XmlDocument();
|
||||
xmlDocument.PreserveWhitespace = true;
|
||||
xmlDocument.LoadXml(text);
|
||||
|
||||
// Create a list of text lines
|
||||
Collection<TextLine> lines = new Collection<TextLine>();
|
||||
|
||||
// Walk over the nodes and build up the fragment list
|
||||
walkNodes(xmlDocument.ChildNodes, lines);
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
private readonly Stack<TextFragmentStyle> _attributeStack = new Stack<TextFragmentStyle>();
|
||||
|
||||
private void walkNodes(XmlNodeList xmlNodeList, Collection<TextLine> textLines)
|
||||
{
|
||||
if (textLines.Count == 0)
|
||||
textLines.Add(new TextLine());
|
||||
|
||||
foreach (XmlNode xmlNode in xmlNodeList)
|
||||
{
|
||||
TextFragmentStyle style;
|
||||
|
||||
switch (xmlNode.Name.ToUpperInvariant())
|
||||
{
|
||||
case "#WHITESPACE":
|
||||
case "#TEXT":
|
||||
|
||||
// Split the fragment and the line endings
|
||||
string[] lines = xmlNode.Value.Split('\n');
|
||||
|
||||
bool firstLine = true;
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
TextLine textLine = (firstLine ? textLines[textLines.Count - 1] : new TextLine());
|
||||
|
||||
// Create a new fragment and fill the style information
|
||||
TextFragment textFragment = new TextFragment(_parentControl);
|
||||
textFragment.Text = line;
|
||||
|
||||
foreach (TextFragmentStyle s in _attributeStack)
|
||||
{
|
||||
s.Apply(textFragment);
|
||||
}
|
||||
|
||||
// Add the fragment to the list
|
||||
textLine.FragmentList.Add(textFragment);
|
||||
|
||||
if (!firstLine)
|
||||
textLines.Add(textLine);
|
||||
|
||||
firstLine = false;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "B":
|
||||
|
||||
style = new TextFragmentStyle { Weight = FontWeights.Bold };
|
||||
_attributeStack.Push(style);
|
||||
|
||||
break;
|
||||
|
||||
case "U":
|
||||
|
||||
style = new TextFragmentStyle { Underline = true };
|
||||
_attributeStack.Push(style);
|
||||
|
||||
break;
|
||||
|
||||
case "CITE":
|
||||
|
||||
style = new TextFragmentStyle { Style = FontStyles.Italic };
|
||||
_attributeStack.Push(style);
|
||||
|
||||
break;
|
||||
|
||||
case "FONT":
|
||||
style = new TextFragmentStyle();
|
||||
|
||||
foreach (XmlAttribute attribute in xmlNode.Attributes)
|
||||
{
|
||||
switch (attribute.Name.ToUpperInvariant())
|
||||
{
|
||||
case "SIZE":
|
||||
style.Size = Convert.ToDouble(attribute.Value, CultureInfo.InvariantCulture);
|
||||
break;
|
||||
|
||||
case "COLOR":
|
||||
style.Color = (Brush) new BrushConverter().ConvertFromString(attribute.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_attributeStack.Push(style);
|
||||
break;
|
||||
}
|
||||
|
||||
if (xmlNode.ChildNodes.Count > 0)
|
||||
walkNodes(xmlNode.ChildNodes, textLines);
|
||||
|
||||
if (_attributeStack.Count > 0)
|
||||
_attributeStack.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user