diff --git a/ExtendedWPFToolkitSolution/ExtendedWPFToolkit.vssscc b/ExtendedWPFToolkitSolution/ExtendedWPFToolkit.vssscc new file mode 100644 index 00000000..794f014c --- /dev/null +++ b/ExtendedWPFToolkitSolution/ExtendedWPFToolkit.vssscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT" +} diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/FormatToolbar.xaml b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/FormatToolbar.xaml new file mode 100644 index 00000000..314e9102 --- /dev/null +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/FormatToolbar.xaml @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/FormatToolbar.xaml.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/FormatToolbar.xaml.cs new file mode 100644 index 00000000..a0cfef85 --- /dev/null +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/FormatToolbar.xaml.cs @@ -0,0 +1,148 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Media; +using System.Windows.Controls.Primitives; + +namespace Microsoft.Windows.Controls.Formatting +{ + /// + /// Interaction logic for FormatToolbar.xaml + /// + public partial class FormatToolbar : UserControl + { + #region Properties + + public static readonly DependencyProperty RichTextBoxProperty = DependencyProperty.Register("RichTextBox", typeof(RichTextBox), typeof(FormatToolbar)); + public RichTextBox RichTextBox + { + get { return (RichTextBox)GetValue(RichTextBoxProperty); } + set { SetValue(RichTextBoxProperty, value); } + } + + public double[] FontSizes + { + get + { + return new double[] { + 3.0, 4.0, 5.0, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, + 10.0, 10.5, 11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 15.0, + 16.0, 17.0, 18.0, 19.0, 20.0, 22.0, 24.0, 26.0, 28.0, 30.0, + 32.0, 34.0, 36.0, 38.0, 40.0, 44.0, 48.0, 52.0, 56.0, 60.0, 64.0, 68.0, 72.0, 76.0, + 80.0, 88.0, 96.0, 104.0, 112.0, 120.0, 128.0, 136.0, 144.0 + }; + } + } + + #endregion + + #region Constructors + + public FormatToolbar(RichTextBox richTextBox) + { + InitializeComponent(); + Loaded += FormatToolbar_Loaded; + RichTextBox = richTextBox; + RichTextBox.SelectionChanged += RichTextBox_SelectionChanged; + } + + #endregion //Constructors + + #region Event Hanlders + + void FormatToolbar_Loaded(object sender, RoutedEventArgs e) + { + _cmbFontFamilies.ItemsSource = System.Windows.Media.Fonts.SystemFontFamilies; + _cmbFontSizes.ItemsSource = FontSizes; + } + + private void FontFamily_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (e.AddedItems.Count == 0) + return; + + FontFamily editValue = (FontFamily)e.AddedItems[0]; + ApplyPropertyValueToSelectedText(TextElement.FontFamilyProperty, editValue); + } + + private void FontSize_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (e.AddedItems.Count == 0) + return; + + ApplyPropertyValueToSelectedText(TextElement.FontSizeProperty, e.AddedItems[0]); + } + + void RichTextBox_SelectionChanged(object sender, RoutedEventArgs e) + { + UpdateVisualState(); + } + + private void DragWidget_DragDelta(object sender, DragDeltaEventArgs e) + { + ProcessMove(e); + } + + #endregion //Event Hanlders + + #region Methods + + private void UpdateVisualState() + { + UpdateToggleButtonState(); + UpdateSelectedFontFamily(); + UpdateSelectedFontSize(); + } + + private void UpdateToggleButtonState() + { + UpdateItemCheckedState(_btnBold, TextElement.FontWeightProperty, FontWeights.Bold); + UpdateItemCheckedState(_btnItalic, TextElement.FontStyleProperty, FontStyles.Italic); + UpdateItemCheckedState(_btnUnderline, Inline.TextDecorationsProperty, TextDecorations.Underline); + + UpdateItemCheckedState(_btnAlignLeft, Paragraph.TextAlignmentProperty, TextAlignment.Left); + UpdateItemCheckedState(_btnAlignCenter, Paragraph.TextAlignmentProperty, TextAlignment.Center); + UpdateItemCheckedState(_btnAlignRight, Paragraph.TextAlignmentProperty, TextAlignment.Right); + } + + void UpdateItemCheckedState(ToggleButton button, DependencyProperty formattingProperty, object expectedValue) + { + object currentValue = RichTextBox.Selection.GetPropertyValue(formattingProperty); + button.IsChecked = (currentValue == DependencyProperty.UnsetValue) ? false : currentValue != null && currentValue.Equals(expectedValue); + } + + private void UpdateSelectedFontFamily() + { + object value = RichTextBox.Selection.GetPropertyValue(TextElement.FontFamilyProperty); + FontFamily currentFontFamily = (FontFamily)((value == DependencyProperty.UnsetValue) ? null : value); + if (currentFontFamily != null) + { + _cmbFontFamilies.SelectedItem = currentFontFamily; + } + } + + private void UpdateSelectedFontSize() + { + object value = RichTextBox.Selection.GetPropertyValue(TextElement.FontSizeProperty); + _cmbFontSizes.SelectedValue = (value == DependencyProperty.UnsetValue) ? null : value; + } + + void ApplyPropertyValueToSelectedText(DependencyProperty formattingProperty, object value) + { + if (value == null) + return; + + RichTextBox.Selection.ApplyPropertyValue(formattingProperty, value); + } + + private void ProcessMove(DragDeltaEventArgs e) + { + AdornerLayer layer = AdornerLayer.GetAdornerLayer(RichTextBox); + UIElementAdorner adorner = layer.GetAdorners(RichTextBox)[0] as UIElementAdorner; + adorner.SetOffsets(adorner.OffsetLeft + e.HorizontalChange, adorner.OffsetTop + e.VerticalChange); + } + + #endregion //Methods + } +} diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/FormatToolbarManager.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/FormatToolbarManager.cs new file mode 100644 index 00000000..295e419a --- /dev/null +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/FormatToolbarManager.cs @@ -0,0 +1,107 @@ +using System; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows; + +namespace Microsoft.Windows.Controls.Formatting +{ + internal class FormatToolbarManager + { + RichTextBox _richTextBox; + readonly UIElementAdorner _adorner; + FormatToolbar toolbar; + + bool AdornerIsVisible + { + get { return _adorner.Visibility == Visibility.Visible; } + } + + public FormatToolbarManager(RichTextBox richTextBox) + { + _richTextBox = richTextBox; + _adorner = new UIElementAdorner(richTextBox); + toolbar = new FormatToolbar(richTextBox); + AttachToRichtextBox(); + } + + private void AttachToRichtextBox() + { + _richTextBox.Selection.Changed += Selection_Changed; + } + + void Selection_Changed(object sender, EventArgs e) + { + TextRange selectedText = new TextRange(_richTextBox.Selection.Start, _richTextBox.Selection.End); + if (selectedText.Text.Length > 0) + { + VerifyAdorner(); + } + else + { + HideAdorner(); + } + } + + //TODO: refactor + void VerifyAdorner() + { + VerifyAdornerLayer(); + + Control adorningEditor = toolbar; + _adorner.Child = adorningEditor; + _adorner.Visibility = Visibility.Visible; + + Rect wordBoundary = _richTextBox.Selection.End.GetPositionAtOffset(0, LogicalDirection.Backward).GetCharacterRect(LogicalDirection.Backward); + + double left = wordBoundary.X; + double top = (wordBoundary.Y + wordBoundary.Height) - toolbar.ActualHeight; + + //top boundary + if (top < 0) + { + top = wordBoundary.Y + wordBoundary.Height; + } + + //right boundary + if (left + toolbar.ActualWidth > _richTextBox.ActualWidth - 20) + { + left = left - toolbar.ActualWidth; + top = wordBoundary.Y + wordBoundary.Height + 5; + } + + //bottom boundary + if (top + toolbar.ActualHeight > _richTextBox.ActualHeight - 20) + { + top = wordBoundary.Y - (toolbar.ActualHeight + wordBoundary.Height); + } + + _adorner.SetOffsets(left, top); + } + + /// + /// Ensures that the adorner is in the adorner layer. + /// + /// True if the adorner is in the adorner layer, else false. + bool VerifyAdornerLayer() + { + if (_adorner.Parent != null) + return true; + + AdornerLayer layer = AdornerLayer.GetAdornerLayer(_richTextBox); + if (layer == null) + return false; + + layer.Add(_adorner); + return true; + } + + void HideAdorner() + { + if (this.AdornerIsVisible) + { + _adorner.Visibility = Visibility.Collapsed; + _adorner.Child = null; + } + } + } +} diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/Bold16.png b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/Bold16.png new file mode 100644 index 00000000..8fb36a1a Binary files /dev/null and b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/Bold16.png differ diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/CenterAlign16.png b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/CenterAlign16.png new file mode 100644 index 00000000..1030a021 Binary files /dev/null and b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/CenterAlign16.png differ diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/Italic16.png b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/Italic16.png new file mode 100644 index 00000000..8259a9e1 Binary files /dev/null and b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/Italic16.png differ diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/JustifyAlign16.png b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/JustifyAlign16.png new file mode 100644 index 00000000..c5499bce Binary files /dev/null and b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/JustifyAlign16.png differ diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/LeftAlign16.png b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/LeftAlign16.png new file mode 100644 index 00000000..51dffb73 Binary files /dev/null and b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/LeftAlign16.png differ diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/RightAlign16.png b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/RightAlign16.png new file mode 100644 index 00000000..cb8e400a Binary files /dev/null and b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/RightAlign16.png differ diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/Underline16.png b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/Underline16.png new file mode 100644 index 00000000..72689482 Binary files /dev/null and b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/Images/Underline16.png differ diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/UIElementAdorner.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/UIElementAdorner.cs new file mode 100644 index 00000000..7b601b47 --- /dev/null +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/FormatToolbar/UIElementAdorner.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections; +using System.Windows.Media; +using System.Windows.Documents; +using System.Windows; + +namespace Microsoft.Windows.Controls.Formatting +{ + /// + /// An adorner that can display one and only one UIElement. + /// That element can be a panel, which contains multiple other elements. + /// The element is added to the adorner's visual and logical trees, enabling it to + /// particpate in dependency property value inheritance, amongst other things. + /// + internal class UIElementAdorner + : Adorner + where TElement : UIElement + { + #region Fields + + TElement _child = null; + double _offsetLeft = 0; + double _offsetTop = 0; + + #endregion // Fields + + #region Constructor + + /// + /// Constructor. + /// + /// The element to which the adorner will be bound. + public UIElementAdorner(UIElement adornedElement) + : base(adornedElement) + { + } + + #endregion // Constructor + + #region Public Interface + + #region Child + + /// + /// Gets/sets the child element hosted in the adorner. + /// + public TElement Child + { + get { return _child; } + set + { + if (value == _child) + return; + + if (_child != null) + { + base.RemoveLogicalChild(_child); + base.RemoveVisualChild(_child); + } + + _child = value; + + if (_child != null) + { + base.AddLogicalChild(_child); + base.AddVisualChild(_child); + } + } + } + + #endregion // Child + + #region GetDesiredTransform + + /// + /// Override. + /// + /// + /// + public override GeneralTransform GetDesiredTransform(GeneralTransform transform) + { + GeneralTransformGroup result = new GeneralTransformGroup(); + result.Children.Add(base.GetDesiredTransform(transform)); + result.Children.Add(new TranslateTransform(_offsetLeft, _offsetTop)); + return result; + } + + #endregion // GetDesiredTransform + + #region OffsetLeft + + /// + /// Gets/sets the horizontal offset of the adorner. + /// + public double OffsetLeft + { + get { return _offsetLeft; } + set + { + _offsetLeft = value; + UpdateLocation(); + } + } + + #endregion // OffsetLeft + + #region SetOffsets + + /// + /// Updates the location of the adorner in one atomic operation. + /// + public void SetOffsets(double left, double top) + { + _offsetLeft = left; + _offsetTop = top; + this.UpdateLocation(); + } + + #endregion // SetOffsets + + #region OffsetTop + + /// + /// Gets/sets the vertical offset of the adorner. + /// + public double OffsetTop + { + get { return _offsetTop; } + set + { + _offsetTop = value; + UpdateLocation(); + } + } + + #endregion // OffsetTop + + #endregion // Public Interface + + #region Protected Overrides + + /// + /// Override. + /// + /// + /// + protected override Size MeasureOverride(Size constraint) + { + if (_child == null) + return base.MeasureOverride(constraint); + + _child.Measure(constraint); + return _child.DesiredSize; + } + + /// + /// Override. + /// + /// + /// + protected override Size ArrangeOverride(Size finalSize) + { + if (_child == null) + return base.ArrangeOverride(finalSize); + + _child.Arrange(new Rect(finalSize)); + return finalSize; + } + + /// + /// Override. + /// + protected override IEnumerator LogicalChildren + { + get + { + ArrayList list = new ArrayList(); + if (_child != null) + list.Add(_child); + return list.GetEnumerator(); + } + } + + /// + /// Override. + /// + /// + /// + protected override Visual GetVisualChild(int index) + { + return _child; + } + + /// + /// Override. + /// + protected override int VisualChildrenCount + { + get { return _child == null ? 0 : 1; } + } + + #endregion // Protected Overrides + + #region Private Helpers + + void UpdateLocation() + { + AdornerLayer adornerLayer = base.Parent as AdornerLayer; + if (adornerLayer != null) + adornerLayer.Update(base.AdornedElement); + } + + #endregion // Private Helpers + } +} diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/RichTextBox.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/RichTextBox.cs index c88f9e96..ff9c810d 100644 --- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/RichTextBox.cs +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/RichTextBox/RichTextBox.cs @@ -2,6 +2,7 @@ using System.Windows; using System.Windows.Data; using System.Windows.Threading; +using Microsoft.Windows.Controls.Formatting; namespace Microsoft.Windows.Controls { @@ -10,7 +11,8 @@ namespace Microsoft.Windows.Controls #region Private Members private bool _textHasLoaded; - bool isInvokePending; + private bool isInvokePending; + private FormatToolbarManager _manager; #endregion //Private Members @@ -31,26 +33,25 @@ namespace Microsoft.Windows.Controls #region Properties - private ITextFormatter _textFormatter; - /// - /// The ITextFormatter the is used to format the text of the RichTextBox. - /// Deafult formatter is the RtfFormatter - /// - public ITextFormatter TextFormatter + #region AllowFormatting + + public static readonly DependencyProperty AllowFormatingProperty = DependencyProperty.Register("AllowFormating", typeof(bool), typeof(RichTextBox), new PropertyMetadata(false, new PropertyChangedCallback(OnAllowFormatingPropertyChanged))); + public bool AllowFormating { - get - { - if (_textFormatter == null) - _textFormatter = new RtfFormatter(); //default is rtf + get { return (bool)GetValue(AllowFormatingProperty); } + set { SetValue(AllowFormatingProperty, value); } + } - return _textFormatter; - } - set - { - _textFormatter = value; - } + private static void OnAllowFormatingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RichTextBox rtb = (RichTextBox)d; + + if ((bool)e.NewValue) + rtb._manager = new FormatToolbarManager(rtb); } + #endregion //AllowFormatting + #region Text public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(RichTextBox), new FrameworkPropertyMetadata(String.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnTextPropertyChanged), new CoerceValueCallback(CoerceTextProperty), true, System.Windows.Data.UpdateSourceTrigger.LostFocus)); @@ -78,6 +79,30 @@ namespace Microsoft.Windows.Controls #endregion //Text + #region TextFormatter + + private ITextFormatter _textFormatter; + /// + /// The ITextFormatter the is used to format the text of the RichTextBox. + /// Deafult formatter is the RtfFormatter + /// + public ITextFormatter TextFormatter + { + get + { + if (_textFormatter == null) + _textFormatter = new RtfFormatter(); //default is rtf + + return _textFormatter; + } + set + { + _textFormatter = value; + } + } + + #endregion //TextFormatter + #endregion //Properties #region Methods diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj index 888b65ba..ab297bae 100644 --- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj @@ -53,6 +53,10 @@ + + MSBuild:Compile + Designer + MSBuild:Compile Designer @@ -82,6 +86,11 @@ True + + + FormatToolbar.xaml + + @@ -105,6 +114,15 @@ + + + + + + + + +