Initial commit

This commit is contained in:
2014-04-30 17:33:21 -04:00
commit f965f46fb3
33 changed files with 2949 additions and 0 deletions

View 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>

View 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);
}
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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>();
}
}
}

View 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();
}
}
}
}