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:
- 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.
#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:
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:
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 #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:
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 {
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
<ToggleButton x:Name="text_boldButton"
Style="{StaticResource toolBarToggleButton}"
Command="EditingCommands.ToggleBold"
IsChecked="{Binding FontWeight_ToggleNormal_Bold}"
Width="23"
Content="{DynamicResource text_boldImage}" />
E.g.:
void setToggleButton()
{
this.text_boldButton.IsChecked = true;
}
Will not have the effect, if you attempt to assign the value function object.SetValue
:
private void insertImageButton_Click(object sender, RoutedEventArgs e)
{
this.text_boldButton.SetValue(IsChecked, true);
}
or
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
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.
#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;
}
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)
{
throw new NotImplementedException();
}
public event FotoStyleFlippinEventHandler FotoStyleFlippinChanged;
public bool FlippinFotoAtChangeDataInStyle { get; set; }
private bool wantReflect = false;
#endregion
In the ViewModel, you can apply a link with our adaptor:
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;
}
};
}
void b_FotoStyleFlippinChanged(object sender, FotoStyleFlippinEventArgs e)
{
var fotoStyle = e.FotoStyleFlippin;
}
#endregion
}
}
Because:
<toolbar datacontext="{Binding ElementName=behDemoRTB}" />
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.
<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
public IEnumerable<FontFamily> FontItemsSource { get { return Fonts.SystemFontFamilies; } }
Font size Generator
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
private static IEnumerable<Rectangle> ColorsItemsForFontForeground()
{
List<Rectangle> m_colorForSetColor = new List<Rectangle>();
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:
<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