From edef9894af198da690c0381bf43d4dafddf16f0d Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Fri, 1 Feb 2013 04:13:36 +0400 Subject: replace RichTextBox and Attached Property with subclassed RichTextBox and plain Dependency Property. Somehow fixes #11 --- Juick/Classes/ParagraphBindingBehavior.cs | 36 -------------- Juick/Classes/RichTextConverter.cs | 78 ------------------------------ Juick/Controls/HyperLinkRichTextBox.cs | 80 +++++++++++++++++++++++++++++++ Juick/Juick.csproj | 3 +- Juick/MainPage.xaml | 21 +++----- Juick/ThreadView.xaml | 16 ++----- 6 files changed, 91 insertions(+), 143 deletions(-) delete mode 100644 Juick/Classes/ParagraphBindingBehavior.cs delete mode 100644 Juick/Classes/RichTextConverter.cs create mode 100644 Juick/Controls/HyperLinkRichTextBox.cs diff --git a/Juick/Classes/ParagraphBindingBehavior.cs b/Juick/Classes/ParagraphBindingBehavior.cs deleted file mode 100644 index 524f4db..0000000 --- a/Juick/Classes/ParagraphBindingBehavior.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using System.Windows; -using System.Windows.Documents; - -namespace Juick.Classes -{ - public static class ParagraphBindingBehavior - { - public static readonly DependencyProperty AssignedInlinesProperty = - DependencyProperty.RegisterAttached("AssignedInlines", typeof(IEnumerable), typeof(Paragraph), new PropertyMetadata(null, AssignedInlinesCallback)); - - static void AssignedInlinesCallback(DependencyObject target, DependencyPropertyChangedEventArgs e) - { - var inlines = ((Paragraph)target).Inlines; - inlines.Clear(); - var value = e.NewValue as IEnumerable; - if (value != null) - { - foreach (var inline in value) - { - inlines.Add(inline); - } - } - } - - public static IEnumerable GetAssignedInlines(DependencyObject obj) - { - return (IEnumerable)obj.GetValue(AssignedInlinesProperty); - } - - public static void SetAssignedInlines(DependencyObject obj, IEnumerable value) - { - obj.SetValue(AssignedInlinesProperty, value); - } - } -} diff --git a/Juick/Classes/RichTextConverter.cs b/Juick/Classes/RichTextConverter.cs deleted file mode 100644 index 4f78b96..0000000 --- a/Juick/Classes/RichTextConverter.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text.RegularExpressions; -using System.Windows.Data; -using System.Windows.Documents; - -namespace Juick.Classes -{ - public class RichTextConverter : IValueConverter - { - static readonly Regex UrlRegex = new Regex(@"http(s)?://([\w+?\.\w+])+([a-zA-Z0-9\~\!\@\#\$\%\^\&\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?", RegexOptions.Compiled); - // TODO: Add more entities - static readonly Regex JuickEntityRegex = new Regex(@"#(\d+)(/\d+)?", RegexOptions.Compiled); - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - var stringValue = (string)value; - if (string.IsNullOrEmpty(stringValue)) - { - return Enumerable.Empty(); - } - - var result = new List(); - var index = 0; - foreach (var match in UrlRegex.Matches(stringValue).OfType().Union(JuickEntityRegex.Matches(stringValue).OfType())) - { - Uri uri = null; - if (!Uri.TryCreate(match.Value, UriKind.Absolute, out uri)) - { - // Juick entity - uri = new Uri(string.Format("/ThreadView.xaml?mid={0}", JuickEntityRegex.Replace(match.Value, "$1")), UriKind.Relative); - } - if (match.Index > 0) - { - var length = match.Index - index; - if (length > 0) - { - result.Add(new Run { Text = stringValue.Substring(index, length) }); - } - } - - var hyperLink = new Hyperlink - { - NavigateUri = uri - }; - if (uri.IsAbsoluteUri) - { - hyperLink.TargetName = "_blank"; - hyperLink.Inlines.Add(uri.Host); - } - else - { - hyperLink.Inlines.Add(match.Value); - } - result.Add(hyperLink); - - index = match.Index + match.Length; - } - - if (index == 0 || index < stringValue.Length - 1) - { - var lastRunText = stringValue.Substring(index); - if (lastRunText.Length > 0) - { - result.Add(new Run { Text = lastRunText }); - } - } - return result; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return value; - } - } -} diff --git a/Juick/Controls/HyperLinkRichTextBox.cs b/Juick/Controls/HyperLinkRichTextBox.cs new file mode 100644 index 0000000..6ec6883 --- /dev/null +++ b/Juick/Controls/HyperLinkRichTextBox.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Media; + +namespace Juick.Controls +{ + public class HyperLinkRichTextBox : RichTextBox + { + static readonly Regex UrlRegex = new Regex(@"http(s)?://([\w+?\.\w+])+([a-zA-Z0-9\~\!\@\#\$\%\^\&\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?", RegexOptions.Compiled); + // TODO: Add more entities + static readonly Regex JuickEntityRegex = new Regex(@"#(\d+)(/\d+)?", RegexOptions.Compiled); + public static readonly DependencyProperty TextProperty = + DependencyProperty.Register("Text", typeof(string), typeof(HyperLinkRichTextBox), new PropertyMetadata(default(string), TextPropertyChanged)); + + public string Text + { + get { return (string)GetValue(TextProperty); } + set { SetValue(TextProperty, value); } + } + + private static void TextPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) + { + var richTextBox = (HyperLinkRichTextBox)dependencyObject; + var stringValue = (string)dependencyPropertyChangedEventArgs.NewValue; + var paragraph = new Paragraph(); + + var index = 0; + foreach (var match in UrlRegex.Matches(stringValue).OfType().Union(JuickEntityRegex.Matches(stringValue).OfType())) + { + Uri uri = null; + if (!Uri.TryCreate(match.Value, UriKind.Absolute, out uri)) + { + // Juick entity + uri = new Uri(string.Format("/ThreadView.xaml?mid={0}", JuickEntityRegex.Replace(match.Value, "$1")), UriKind.Relative); + } + if (match.Index > 0) + { + var length = match.Index - index; + if (length > 0) + { + paragraph.Inlines.Add(new Run { Text = stringValue.Substring(index, length) }); + } + } + + var hyperLink = new Hyperlink + { + NavigateUri = uri + }; + if (uri.IsAbsoluteUri) + { + hyperLink.TargetName = "_blank"; + hyperLink.Inlines.Add(uri.Host); + } + else + { + hyperLink.Inlines.Add(match.Value); + } + paragraph.Inlines.Add(hyperLink); + + index = match.Index + match.Length; + } + + if (index == 0 || index < stringValue.Length - 1) + { + var lastRunText = stringValue.Substring(index); + if (lastRunText.Length > 0) + { + paragraph.Inlines.Add(new Run { Text = lastRunText }); + } + } + richTextBox.Blocks.Clear(); + richTextBox.Blocks.Add(paragraph); + } + } +} diff --git a/Juick/Juick.csproj b/Juick/Juick.csproj index 7bd452a..d161046 100644 --- a/Juick/Juick.csproj +++ b/Juick/Juick.csproj @@ -76,9 +76,8 @@ - - + LoginView.xaml diff --git a/Juick/MainPage.xaml b/Juick/MainPage.xaml index bffd94f..ded6d33 100644 --- a/Juick/MainPage.xaml +++ b/Juick/MainPage.xaml @@ -7,7 +7,7 @@ xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:bindings="clr-namespace:Juick.Classes" + xmlns:bindings="clr-namespace:Juick.Classes" xmlns:controls1="clr-namespace:Juick.Controls" mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="728" d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}" FontFamily="{StaticResource PhoneFontFamilyNormal}" @@ -15,12 +15,7 @@ Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="PortraitOrLandscape" Orientation="Portrait" phoneshell:SystemTray.IsVisible="True"> - - - - - - + @@ -80,13 +75,11 @@ FontSize="{StaticResource PhoneFontSizeLarge}" Style="{StaticResource PhoneTextAccentStyle}" /> - - - + IsReadOnly="True" Text="{Binding MessageText}" /> - - - + IsReadOnly="True" Text="{Binding MessageText}" /> - - - - - - - + @@ -62,13 +56,11 @@ FontSize="{StaticResource PhoneFontSizeLarge}" Style="{StaticResource PhoneTextAccentStyle}" /> - - - + IsReadOnly="True" Text="{Binding MessageText}" />