/************************************************************************ Extended WPF Toolkit Copyright (C) 2010-2012 Xceed Software Inc. This program is provided to you under the terms of the Microsoft Public License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license This program can be provided to you by Xceed Software Inc. under a proprietary commercial license agreement for use in non-Open Source projects. The commercial version of Extended WPF Toolkit also includes priority technical support, commercial updates, and many additional useful WPF controls if you license Xceed Business Suite for WPF. Visit http://xceed.com and follow @datagrid on Twitter. **********************************************************************/ using System; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using Xceed.Wpf.Toolkit.Core; namespace Xceed.Wpf.Toolkit { public class RichTextBoxFormatBarManager : DependencyObject { #region Members private global::System.Windows.Controls.RichTextBox _richTextBox; private UIElementAdorner _adorner; private IRichTextBoxFormatBar _toolbar; #endregion //Members #region Properties #region FormatBar public static readonly DependencyProperty FormatBarProperty = DependencyProperty.RegisterAttached( "FormatBar", typeof( IRichTextBoxFormatBar ), typeof( RichTextBox ), new PropertyMetadata( null, OnFormatBarPropertyChanged ) ); public static void SetFormatBar( UIElement element, IRichTextBoxFormatBar value ) { element.SetValue( FormatBarProperty, value ); } public static IRichTextBoxFormatBar GetFormatBar( UIElement element ) { return ( IRichTextBoxFormatBar )element.GetValue( FormatBarProperty ); } private static void OnFormatBarPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) { global::System.Windows.Controls.RichTextBox rtb = d as global::System.Windows.Controls.RichTextBox; if( rtb == null ) throw new Exception( "A FormatBar can only be applied to a RichTextBox." ); RichTextBoxFormatBarManager manager = new RichTextBoxFormatBarManager(); manager.AttachFormatBarToRichtextBox( rtb, e.NewValue as IRichTextBoxFormatBar ); } #endregion //FormatBar public bool IsAdornerVisible { get { return _adorner.Visibility == Visibility.Visible; } } #endregion //Properties #region Event Handlers void RichTextBox_MouseButtonUp( object sender, MouseButtonEventArgs e ) { if( e.ChangedButton == MouseButton.Left && e.LeftButton == MouseButtonState.Released ) { TextRange selectedText = new TextRange( _richTextBox.Selection.Start, _richTextBox.Selection.End ); #if !VS2008 if( selectedText.Text.Length > 0 && !String.IsNullOrWhiteSpace( selectedText.Text ) ) ShowAdorner(); #else if( selectedText.Text.Length > 0 && !String.IsNullOrEmpty( selectedText.Text ) ) ShowAdorner(); #endif else HideAdorner(); e.Handled = true; } else HideAdorner(); } void RichTextBox_PreviewMouseMove( object sender, MouseEventArgs e ) { //if the mouse moves outside the richtextbox bounds hide the adorner //though this deosn't always work, especially if the user moves the mouse very quickly. //need to find a better solution, but this will work for now. Point p = e.GetPosition( _richTextBox ); if( p.X <= 5.0 || p.X >= _richTextBox.ActualWidth - 5 || p.Y <= 3.0 || p.Y >= _richTextBox.ActualHeight - 3 ) HideAdorner(); } void RichTextBox_TextChanged( object sender, TextChangedEventArgs e ) { //this fixes the bug when applying text transformations the text would lose it's highlight. That was because the RichTextBox was losing focus //so we just give it focus again and it seems to do the trick of re-highlighting it. if( !_richTextBox.IsFocused && !_richTextBox.Selection.IsEmpty ) _richTextBox.Focus(); } #endregion //Event Handlers #region Methods /// /// Attaches a FormatBar to a RichtextBox /// /// The RichtextBox to attach to. /// The Formatbar to attach. private void AttachFormatBarToRichtextBox( global::System.Windows.Controls.RichTextBox richTextBox, IRichTextBoxFormatBar formatBar ) { _richTextBox = richTextBox; _richTextBox.PreviewMouseMove += RichTextBox_PreviewMouseMove; //we cannot use the PreviewMouseLeftButtonUp event because of selection bugs. //we cannot use the MouseLeftButtonUp event because it is handled by the RichTextBox and does not bubble up to here, so we must //add a hander to the MouseUpEvent using the Addhandler syntax, and specify to listen for handled events too. _richTextBox.AddHandler( Mouse.MouseUpEvent, new MouseButtonEventHandler( RichTextBox_MouseButtonUp ), true ); _richTextBox.TextChanged += RichTextBox_TextChanged; _adorner = new UIElementAdorner( _richTextBox ); formatBar.Target = _richTextBox; _toolbar = formatBar; } /// /// Shows the FormatBar /// void ShowAdorner() { if( _adorner.Visibility == Visibility.Visible ) return; VerifyAdornerLayer(); _toolbar.Update(); Control adorningEditor = _toolbar as Control; if( _adorner.Child == null ) _adorner.Child = adorningEditor; _adorner.Visibility = Visibility.Visible; PositionFormatBar( adorningEditor ); } /// /// Positions the FormatBar so that is does not go outside the bounds of the RichTextBox or covers the selected text /// /// private void PositionFormatBar( Control adorningEditor ) { Point mousePosition = Mouse.GetPosition( _richTextBox ); double left = mousePosition.X; double top = ( mousePosition.Y - 15 ) - adorningEditor.ActualHeight; //top if( top < 0 ) { top = mousePosition.Y + 10; } //right boundary if( left + adorningEditor.ActualWidth > _richTextBox.ActualWidth - 20 ) { left = left - ( adorningEditor.ActualWidth - ( _richTextBox.ActualWidth - left ) ); } _adorner.SetOffsets( left, top ); } /// /// Ensures that the IRichTextFormatBar is in the adorner layer. /// /// True if the IRichTextFormatBar 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; } /// /// Hides the IRichTextFormatBar that is in the adornor layer. /// void HideAdorner() { if( IsAdornerVisible ) { _adorner.Visibility = Visibility.Collapsed; //_adorner.Child = null; } } #endregion //Methods } }