Click here to Skip to main content
15,878,852 members
Articles / Desktop Programming / XAML

Reflection RichTextBox display styles using the techniques of tracking behavior

Rate me:
Please Sign up or sign in to vote.
4.36/5 (4 votes)
28 Aug 2012CPOL5 min read 21K   390   5   3
The vision is now the problem of reflecting styles of RichTextBox display controls. Applies the technique commonly known as behavior.

Introduction

As promised in my previous article, I will now issue rebound RichTextBox control styles. Uses a technique commonly known as behavior

Motivation

Fundamental question why such involvement in such a simple task? The answer is also simple - to make your life easier. By the way, to solve other problems

What is at stake?

What with?

  • The purity code – all dependencies are consolidated in one place.
  • The readability of code, XAML.
  • Do not use background code (behind).
  • The addition of auxiliary methods.
    • Font style Generator
    • Font size Generator
    • Font color Generator
    • Font background color Generator
    • Generator class representing the atoms styles
    • Integrated event changes the style
    • The breakdown of complex atoms styles
  • Total separation from the host
  • Existence as an external reference added and removed by the recognition.
  • Flexibility and scalability.
  • Testable - once tested the behavior will make sure that there's certainly nothing wrong happens. Besides relieving the code gives the freedom to test the standard model and presenter rather than the state controls.

One word for the best practice.

Moreover solve behavior problems of cooperation in particular ToggleButtom controls and ComboBox. The problem occurs when we want to reflect the states through the control ToggleBox. This control does not change visually by using IsChecked changes in the code. We need to create a separate code background or additional property presenter code to bind controls via XAML code and show its condition. It is not comfortable. Demolishing the application of good practice, and (or) MVVM pattern. Why knowledge of the View Model which is currently the state of thick font. This knowledge is necessary to you. Analysis certainly should not be carried out in the presenter. For this we use a separate task.

It seems to me that the rate is competent. It is worth the fight.

This works

The basis is the same as that described in the article http://www.codeproject.com/Articles/437175/Autocompletion-with-RichTextBox-in-WPF-as-Behavior

Images sprites thanks: http://www.gentleface.com/free_icon_set.html.

We start from the identification of assumptions:

Image 1Image 2

  • Controls ToolBar should have the possibility of presenting the status of
  • Everything must be done with the omission of the background code.
  • Don't want to use the links ToolBar controls from the ViewModel.
  • ToolBar controls are only an internal presentation logic in a view without leaving out Xaml.
  • The presentation occurs immediately after a change in style.
  • The syntax should not restrict the applicable ToolBar controls to present status. This means that to show the current state of the atoms you can apply various controls both the built-in and those added by applying only the converters IValueConverter
  • The behavior may not be dependent on any other object than the host (the RichTextBox display)
  • The behavior will be able to fly after the occurrence of an event changes.
  • The behavior is to be able to make foto of the current state of which is located in the place of the indicator.

The assumptions made, the prototype is Visual, to code.

How I Did It

As previously suggested, the whole base on Behavior<DependencyObject> here is RichTextBox.

Turn the standard task:

Sign up for the event RichTextBox control.

C#
#region Attached and Detaching
protected override void OnAttached()
{
    base.OnAttached();
    this.AssociatedObject.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonUp);
    this.AssociatedObject.PreviewKeyUp += new KeyEventHandler(AssociatedObject_PreviewKeyUp);
    this.AssociatedObject.GotFocus += new RoutedEventHandler(AssociatedObject_GotFocus);
}
void AssociatedObject_GotFocus(object sender, RoutedEventArgs e)
{
    prologAdapter();
}
void AssociatedObject_PreviewKeyUp(object sender, KeyEventArgs e)
{
    prologAdapter();
}
void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    prologAdapter();
}
protected override void OnDetaching()
{
    base.OnDetaching();
    this.AssociatedObject.PreviewMouseLeftButtonUp -= new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonUp);
    this.AssociatedObject.PreviewKeyUp -= new KeyEventHandler(AssociatedObject_PreviewKeyUp);
    this.AssociatedObject.GotFocus-=new RoutedEventHandler(AssociatedObject_GotFocus);
}
#endregion

Prologue is just a preliminary check whether we can work on:

C#
// Test whether we starting
void prologAdapter()
{
     TextPointer tp = this.AssociatedObject.CaretPosition;
     if (_flagPointer != null && tp.CompareTo(_flagPointer) == 0)
         return;
     else
         _flagPointer = tp;
     startAdapter();
}

If so this is the start:

C#
// started Work
void startAdapter()
{
   coreReset();
   this.AssociatedObject.Dispatcher.BeginInvoke(new Action(() =>
   {
     #region Assets
     _font_background = this.AssociatedObject.Selection.GetPropertyValue(Run.BackgroundProperty) as SolidColorBrush;
     _font_foreground = this.AssociatedObject.Selection.GetPropertyValue(Run.ForegroundProperty) as SolidColorBrush;
     _fontFamily = this.AssociatedObject.Selection.GetPropertyValue(Run.FontFamilyProperty) as FontFamily;
     _fontSize = (double)this.AssociatedObject.Selection.GetPropertyValue(Run.FontSizeProperty);
     _fontStretch = (FontStretch)this.AssociatedObject.Selection.GetPropertyValue(Run.FontStretchProperty);
     _fontStyle = (FontStyle)this.AssociatedObject.Selection.GetPropertyValue(Run.FontStyleProperty);
     _fontWeight = (FontWeight)this.AssociatedObject.Selection.GetPropertyValue(Run.FontWeightProperty);
     _lineHeight = (double)this.AssociatedObject.Selection.GetPropertyValue(Paragraph.LineHeightProperty);
     _isHyphenationEnabled = (bool)this.AssociatedObject.Selection.GetPropertyValue(Paragraph.IsHyphenationEnabledProperty);
      _textDecorations = this.AssociatedObject.Selection.GetPropertyValue(Run.TextDecorationsProperty) as TextDecorationCollection;
      _textIndent = (double)this.AssociatedObject.Selection.GetPropertyValue(Paragraph.TextIndentProperty);
      _textAlign = (TextAlignment)this.AssociatedObject.Selection.GetPropertyValue(Paragraph.TextAlignmentProperty);
      #endregion // assets
      #region Color
      coreFontColor();
      #endregion
      #region TextAlignment
      coreTextAlignment();
      #endregion
      #region FontWeight
      coreFontWeight();
      #endregion
      #region FontStyle
      coreFontStyle();
      #endregion
      #region Font type
      OnChangeProperty("FontFamily");
      #endregion
      #region FontSize
      coreFontSize();
      #endregion
      #region TextDecorations
      coreTextDecorations();
      #endregion
      if (this.FlippinFotoAtChangeDataInStyle) OnFoto(null);
      }));
}

In this part of the code, we read what we want to know about the style. Style is taken to the next variable. I now turn to the exchanged and discussed. I note only that there many kinds of data and some may be admitted as null without any consequences. Assets by region is a sequence of functions that support our bus crash data and analyzing it to atoms. Do not show here all but only one primary particle reactor.

Interesting method that parses font weight:

C#
void coreFontWeight()
{
    this._fontWeight_toggleNormal_Bold = _fontWeight > FontWeights.SemiBold;
    if (_fontWeight < FontWeights.Bold)
    {
        if (_fontWeight < FontWeights.Normal)
        {
            if (FontWeight.Compare(_fontWeight, FontWeights.ExtraLight) == 0) this._fontWeight_200_ExtraLight = true;
            else if (FontWeight.Compare(_fontWeight, FontWeights.ExtraLight) < 0) this._fontWeight_100_Thin = true;
            else
                if (FontWeight.Compare(_fontWeight, FontWeights.UltraLight) <= 0)
                {
                    this._fontWeight_250_UltraLight = true;
                }
                else { this._fontWeight_300_Light = true; }
            }
        else
        {
            if (_fontWeight < FontWeights.Medium)
            {
                if (FontWeight.Compare(_fontWeight, FontWeights.Normal) == 0)
                {
                    this._fontWeight_350_Normal = true;
                }
            else
            { this._fontWeight_400_Regular = true; }
        }
        else
        {
            if (FontWeight.Compare(_fontWeight, FontWeights.Medium) < 0)
            {
                this._fontWeight_550_DemiBold = true;
            }
            else if (FontWeight.Compare(_fontWeight, FontWeights.Medium) == 0)
            {
                this._fontWeight_500_Medium = true;
            }
            else if (FontWeight.Compare(_fontWeight, FontWeights.Medium) > 0)
            {
                this._fontWeight_600_SemiBold = true;
            }
        }
      }
}
else
{
    if (_fontWeight < FontWeights.Black)
    {
        if (FontWeight.Compare(_fontWeight, FontWeights.ExtraBold) == 0)
        {
            this._fontWeight_750_ExtraBold = true;
        }
        else // (FontWeight.Compare(_fontWeight, FontWeights.ExtraBold) < 0)
        {
            this._fontWeight_700_Bold = true;
        }
    }
    else if (FontWeight.Compare(_fontWeight, FontWeights.Black) == 0)
    {
        this._fontWeight_850_Black = true;
    }
    else if (FontWeight.Compare(_fontWeight, FontWeights.ExtraBold) < 0)
    {
        this._fontWeight_800_UltraBold = true;
    }
    else
    {
        if (FontWeight.Compare(_fontWeight, FontWeights.Heavy) < 0)
        {
            this._fontWeight_900_ExtraBlack = true;
        }
        else if (FontWeight.Compare(_fontWeight, FontWeights.ExtraBlack) == 0)
        {
            this._fontWeight_900_Heavy = true;
        }
        else
        {
            this._fontWeight_950_UltraBlack = true;
        }
    }
}

OnChangeProperty("FontWeight_ToggleNormal_Bold");
OnChangeProperty("FontWeight_ExtraLight");
OnChangeProperty("FontWeight_Thin");
OnChangeProperty("FontWeight_UltraLight");
OnChangeProperty("FontWeight_Light");
OnChangeProperty("FontWeight_Normal");
OnChangeProperty("FontWeight_Regular");
OnChangeProperty("FontWeight_DemiBold");
OnChangeProperty("FontWeight_Medium");
OnChangeProperty("FontWeight_SemiBold");
OnChangeProperty("FontWeight_ExtraBold");
OnChangeProperty("FontWeight_Bold");
OnChangeProperty("FontWeight_Black");
OnChangeProperty("FontWeight_UltraBold");
OnChangeProperty("FontWeight_ExtraBlack");
OnChangeProperty("FontWeight_Heavy");
OnChangeProperty("FontWeight_UltraBlack");

Accepted 17 names thick letters. However, in reality there is only 9 of their actual values. The procedure sequentially selects the median value and compares it with the pattern until it finds the required size. Select the appropriate value from the specified by the fontWeight:

  • The first line sets the switch whether it is Bold or Normal what in most applications is saying enough and we also we will mainly use this without much on nuance.
  • Next look for proper labeling of thickness and denote them true. At this point it is worth mentioning that prior to each pass control data are reset.
  • Finally, we report on the events in sequence all properties.

And this is the principle of operation of the entire class as regards detection properties.

Change status visualization controls

A few lines earlier I wrote that the light does not reflect the status of the parties of the procedural code

XML
<ToggleButton x:Name="text_boldButton"
              Style="{StaticResource toolBarToggleButton}"
              Command="EditingCommands.ToggleBold"
              IsChecked="{Binding FontWeight_ToggleNormal_Bold}"
              Width="23"
              Content="{DynamicResource text_boldImage}" />

E.g.:

C#
void setToggleButton()
{
    this.text_boldButton.IsChecked = true;
}

Will not have the effect, if you attempt to assign the value function object.SetValue:

C#
private void insertImageButton_Click(object sender, RoutedEventArgs e)
{
    this.text_boldButton.SetValue(IsChecked, true);
}

or

C#
private void insertImageButton_Click(object sender, RoutedEventArgs e)
{
   this. text_boldButton.SetValue(IsCheckedProperty, true);
}

Generates:

Error

"The name 'IsChecked' does not exist in the current context" 

and

C#
this.text_boldButton.IsChecked.Value= true;

Error

Property or indexer 'System.Nullable<bool>.Value' cannot be assigned to -- it is read only

Remains to create one of the properties:

  • Attaches DependencyProperty
  • Independent DependencyProperty
  • Property in the code behind is adding the INotifyPropertyChanged interface
  • Property in presenter (ViewModel).

I, however, wish to apply such miracles in class that retrieves the behavior of the host class. It requires no rubbish in the code (name). xaml.cs or dirty MVVM or MVP pattern. Even if the logic of the. When the need of such knowledge in the View Model (presenter), I use another adapter functionality delivered by the referred to here.

C#
#region FotoCommand
private ICommand _fotoCommand;
public ICommand FotoCommand
{
 get { return _fotoCommand ?? (_fotoCommand = new BehaviorDelegateCommand(OnFoto, CanOnFoto)); }
 set { _fotoCommand = value; }
}
protected void OnFoto(object parametr)
{
 FotoStyleFlippinEventHandler handler = FotoStyleFlippinChanged;
 if (handler != null) handler(
     this,
     new FotoStyleFlippinEventArgs(GetStateAllStyleFactory())
     );
}
protected bool CanOnFoto(object parametr)
{
     return true;
}
/// <summary>
/// Factory for foto actual all data 
/// </summary>
/// <returns>AddBehavior.FotoStyleFlippin</returns>
public FotoStyleFlippin FotoStyleFlippin
{
 get
 {
     OnFoto(null);
     return GetStateAllStyleFactory();
 }
}
private FotoStyleFlippin GetStateAllStyleFactory()
{
 FotoStyleFlippin f = new FotoStyleFlippin
  {
      TextAlignment = this.TextAlignment,
      TextAlignment_Left = this.TextAlignment_Left,
      TextAlignment_Right = this.TextAlignment_Right,
      TextAlignment_Center = this.TextAlignment_Center,
      TextAlignment_Justify = this.TextAlignment_Justify,
      FontBackground = this.FontBackground,
      FontForeground = this.FontForeground,
      FontFamily = this.FontFamily,
      FontSize = this.FontSize,
      FontStretch = this.FontStretch,
      FontStretch_1_UltraCondensed = this.FontStretch_1_UltraCondensed,
      FontStretch_2_ExtraCondensed = this.FontStretch_2_ExtraCondensed,
      FontStretch_3_Condensed = this.FontStretch_3_Condensed,
      FontStretch_4_SemiCondensed = this.FontStretch_4_SemiCondensed,
      FontStretch_5_Medium = this.FontStretch_5_Medium,
      FontStretch_6_SemiExpanded = this.FontStretch_6_SemiExpanded,
      FontStretch_7_Expanded = this.FontStretch_7_Expanded,
      FontStretch_8_ExtraExpanded = this.FontStretch_8_ExtraExpanded,
      FontStretch_9_UltraExpanded = this.FontStretch_9_UltraExpanded,
      FontWeight = this.FontWeight,
      FontWeight_Thin = this.FontWeight_Thin,
      FontWeight_ExtraLight = this.FontWeight_ExtraLight,
      FontWeight_UltraLight = this.FontWeight_UltraLight,
      FontWeight_Light = this.FontWeight_Light,
      FontWeight_Normal = this.FontWeight_Normal,
      FontWeight_Regular = this.FontWeight_Regular,
      FontWeight_Medium = this.FontWeight_Medium,
      FontWeight_DemiBold = this.FontWeight_DemiBold,
      FontWeight_SemiBold = this.FontWeight_SemiBold,
      FontWeight_Bold = this.FontWeight_Bold,
      FontWeight_ExtraBold = this.FontWeight_ExtraBold,
      FontWeight_UltraBold = this.FontWeight_UltraBold,
      FontWeight_Black = this.FontWeight_Black,
      FontWeight_Heavy = this.FontWeight_Heavy,
      FontWeight_ExtraBlack = this.FontWeight_ExtraBlack,
      FontWeight_UltraBlack = this.FontWeight_UltraBlack,
      FontStyle = this.FontStyle,
      FontStyle_Normal = this.FontStyle_Normal,
      FontStyle_Italic = this.FontStyle_Italic,
      FontStyle_Oblique = this.FontStyle_Oblique,
      Typography = this.Typography,
      LineHeight = this.LineHeight,
      IsHyphenationEnabled = this.IsHyphenationEnabled,
      TextIndent = this.TextIndent
  };
 if (wantReflect)
     f.PropertyChanged += delegate { reflection(f); };
 return f;
}
private void reflection(FotoStyleFlippin f)
{
 // for implementation
 throw new NotImplementedException();
}
public event FotoStyleFlippinEventHandler FotoStyleFlippinChanged;
public bool FlippinFotoAtChangeDataInStyle { get; set; }
private bool wantReflect = false;
#endregion

Image 3

In the ViewModel, you can apply a link with our adaptor:

C#
namespace Wpf.ImpressDemo
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using AddBehavior;
    using System.Windows.Controls;
 
    public class DemoMV
    {
#region Ctor
        public DemoMV()
        {
            var x = App.Current.MainWindow as MainWindow;
            x.Loaded += delegate
            {
              var b = (App.Current.MainWindow.Content as Grid)
                .Children.OfType<ToolBarTray>()
                 .FirstOrDefault(
                  tbt => tbt.ToolBars.Where(
                   n => n.DataContext != null && (n.DataContext 
                                  as ImpressStyleOverCaretAdapter) != null) != null)
                    .ToolBars.FirstOrDefault(tb => 
                       (tb.DataContext as ImpressStyleOverCaretAdapter) != null)
              .DataContext as ImpressStyleOverCaretAdapter;
             if (b != null)
        }
          b.FotoStyleFlippinChanged += new FotoStyleFlippinEventHandler(b_FotoStyleFlippinChanged);
          b.FlippinFotoAtChangeDataInStyle = true;
       }
          // or
          // var fotoStyle = b.FotoStyleFlippin;

          };
       }
 
    void b_FotoStyleFlippinChanged(object sender, FotoStyleFlippinEventArgs e)
    {
        var fotoStyle = e.FotoStyleFlippin;
        // Processed
    }
    #endregion
    }
}

Because:

XML
<toolbar datacontext="{Binding ElementName=behDemoRTB}" />

Image 4

An instance of the class DemoVM is connected to the project in the MainView. A few other things I'll leave to the implementation and the thoughts, although the example works fine and meets raised before him the job. In this example, the colors are recognized only in the list generated by the adapter. It is worth noted that there are two points where the function is called responsible for PropertyChanged. The so-called small and large onPropertyChanged. This is due to separate the receive and send data changes in the properties.

XML
<RichTextBox x:Name="demoRTB"
             TabIndex="0">
    <i:Interaction.Behaviors>
        <beh:ImpressStyleOverCaretAdapter x:Name="behDemoRTB"></beh:ImpressStyleOverCaretAdapter>
    </i:Interaction.Behaviors>
    <FlowDocument>
        <Paragraph FontSize="24" TextAlignment="Center">
            <Run FontWeight="Bold">SKUTNIK</Run>
            <LineBreak />
            <Run FontSize="32" FontFamily="Jing Jing">Andrzej</Run>                        
        </Paragraph>
    </FlowDocument>
</RichTextBox>

Additional features

  • Font style Generator
  • C#
    public IEnumerable<FontFamily> FontItemsSource { get { return Fonts.SystemFontFamilies; } }
  • Font size Generator
  • C#
    public double[] FontsSize { get { return new double[] { 6, 7, 8, 9, 10, 
       10.5, 11, 11.5, 12, 13, 14, 16, 18, 20, 24, 28, 32, 40, 48, 64, 72, 128 }; } }
  • Font color Generator
  • C#
    private static IEnumerable<Rectangle> ColorsItemsForFontForeground()
    {
        List<Rectangle> m_colorForSetColor = new List<Rectangle>();
        // Generate the color swatch
        int scale = 75;
        for (int r = 0; r < 255; r += scale )
        {
            for (int g = 0; g < 255; g += scale )
            {
                for (int b = 0; b < 255; b += scale )
                {
                    m_colorForSetColor.Add(new Rectangle
                    {
                        Width = 16,
                        Height = 16,
                        Margin = new Thickness(2),
                        Stroke = new SolidColorBrush(Colors.Black),
                        StrokeThickness = 1,
                        Fill = new SolidColorBrush(Color.FromArgb(255, (byte)r, (byte)g, (byte)b))
                    });
                }
            }
        }
        return m_colorForSetColor;
    }

    In code XAML:

    XML
    <ToolBar DataContext="{Binding ElementName=behDemoRTB}">
    <ComboBox x:Name="fontTypeComboBox"
              Height="23"
              ItemsSource="{Binding FontItemsSource, AsyncState=true}"
              Width="80"
              Margin="5 5 1 5"
              SelectedIndex="0"
              SelectedValue="{Binding FontFamily}"
              VerticalContentAlignment="Center"
              Padding="10 0 0 0">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <TextBlock  Text="{Binding}"
                            FontFamily="{Binding Source}" />
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>
    <ComboBox x:Name="fontSizesComboBox"
              ItemsSource="{Binding FontsSize}"
              Height="23"
              Width="40"
              SelectedIndex="6"
              SelectedValue="{Binding FontSize}" />
    <ComboBox x:Name="fontColorsComboBox"
              Height="23"
              Width="40"
              SelectedValue="{Binding FontForegroundRect}"
              ItemsSource="{Binding GenerateColorForSetColorForeground}">
        <ComboBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Width="200" />
            </ItemsPanelTemplate>
        </ComboBox.ItemsPanel>
    </ComboBox></ToolBar>

Summary

The article is much shorter than the presented topic. Well I decided to not deliver in the elaboration of the full experimentation is the best way to adapter because it knowledge. So say the geniuses who are experimenting middle name.

Best regards Andrzej Skutnik

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
freelancer
Poland Poland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 4 Pin
Kanasz Robert19-Sep-12 4:52
professionalKanasz Robert19-Sep-12 4:52 
GeneralMy vote of 4 Pin
Christian Amado28-Aug-12 12:48
professionalChristian Amado28-Aug-12 12:48 
SuggestionComments on the code Pin
Pavel Evgenjevich Timoshenko28-Aug-12 10:03
Pavel Evgenjevich Timoshenko28-Aug-12 10:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.