I have a RichTextBox bound to a string.
Using C# I generate a string that writes to it.
But if I want to manually change the text by clicking into the RichTextBox and deleting it with the backspace key, or pressing Enter to make a new line, the binding becomes broken and I can no longer programmatically write to it with the string a second time.
XAML
<RichTextBox x:Name="rtbScriptView"
Margin="11,71,280,56"
Padding="10,10,10,48"
FontSize="14"
Grid.ColumnSpan="1"
VerticalScrollBarVisibility="Auto"
RenderOptions.ClearTypeHint="Enabled"
Style="{DynamicResource RichTextBoxStyle}">
<FlowDocument>
<Paragraph>
<Run Text="{Binding ScriptView_Text,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
</Paragraph>
</FlowDocument>
</RichTextBox>
View Model
private string _ScriptView_Text;
public string ScriptView_Text
{
get { return _ScriptView_Text; }
set
{
if (_ScriptView_Text == value)
{
return;
}
_ScriptView_Text = value;
OnPropertyChanged("ScriptView_Text");
}
}
C#
ViewModel vm = new ViewModel();
DataContext = vm;
// Display a string in the RichTextBox
vm.ScriptView_Text = "This is a test."; // <-- This won't work if text is manually modified
When you edit the RichTextBox, you alter the elements inside of the FlowDocument element. The element you have a binding on, is probably removed at some point during this editing.
Have a look at RichtTextBox.Document.Groups to see what's happening when you edit the RichTextBox.
The default RichTextBox does not really support MVVM/Binding very well. You'd want to have a binding on the Document property, but this is not supported for the default RichTextBox.
You could have a look here.
Or extend it yourself, something like this?:
BindableRichTextBox class
public class BindableRichTextBox : RichTextBox
{
public static readonly DependencyProperty DocumentProperty = DependencyProperty.Register(nameof(Document), typeof(FlowDocument), typeof(BindableRichTextBox), new FrameworkPropertyMetadata(null, OnDocumentChanged));
public new FlowDocument Document
{
get => (FlowDocument)GetValue(DocumentProperty);
set => SetValue(DocumentProperty, value);
}
public static void OnDocumentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var rtb = (RichTextBox)obj;
rtb.Document = args.NewValue != null ? (FlowDocument)args.NewValue : new FlowDocument();
}
}
XAML
<controls:BindableRichTextBox Document="{Binding YourFlowDocumentObject, Mode=OneWay}"/>
Then you can get the string from the FlowDocument.
Why you have to write this line. Please remove line after check.
if (_ScriptView_Text == value)
{
return;
}
Related
I have looked at various solutions on how to have a simple URL on a textfield, a rich text field in a WPF implementation using C#.
Tried to implement solutions on Clicking HyperLinks in a RichTextBox without holding down CTRL - WPF and Add clickable hyperlinks to a RichTextBox without new paragraph. It just looks overly complicated for what i think should be a simple task.
<RichTextBox IsReadOnly="True" IsDocumentEnabled="True">
<FlowDocument>
<Paragraph FontSize="12"> See www.google.com</Paragraph>
</FlowDocument>
</RichTextBox>
I also tried the implementation on the link
Example using Hyperlink in WPF
Here, the error I get is this.
MainWindow does not contain a definition of Hyperlink_RequestNAvigate and no accessible extension method hyperlink_RequestNavigate accepting a first argument of type MainWindow could be found, are you missing a using directive of an assembly reference.
<TextBlock>
<Hyperlink NavigateUri="http://www.google.com" RequestNavigate="Hyperlink_RequestNavigate">
Click here
</Hyperlink>
</TextBlock>
code behind
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
// for .NET Core you need to add UseShellExecute = true
// see https://learn.microsoft.com/dotnet/api/system.diagnostics.processstartinfo.useshellexecute#property-value
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
e.Handled = true;
}
I am looking to have a text saying for more information click here and if the user clicks on click here, they navigate to a predefined url.
The way I've implemented hyperlinks in the past is to create a attached property, which you can add onto the Hyperlink, which will handle the RequestNavigate itself.
For example:
namespace WpfDemo.Extensions
{
public static class HyperlinkExtensions
{
public static bool GetIsExternal(DependencyObject obj)
{
return (bool)obj.GetValue(IsExternalProperty);
}
public static void SetIsExternal(DependencyObject obj, bool value)
{
obj.SetValue(IsExternalProperty, value);
}
public static readonly DependencyProperty IsExternalProperty =
DependencyProperty.RegisterAttached("IsExternal", typeof(bool), typeof(HyperlinkExtensions), new UIPropertyMetadata(false, OnIsExternalChanged));
private static void OnIsExternalChanged(object sender, DependencyPropertyChangedEventArgs args)
{
var hyperlink = sender as Hyperlink;
if ((bool)args.NewValue)
hyperlink.RequestNavigate += Hyperlink_RequestNavigate;
else
hyperlink.RequestNavigate -= Hyperlink_RequestNavigate;
}
private static void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
{
ProcessStartInfo psi = new()
{
FileName = e.Uri.AbsoluteUri,
UseShellExecute = true,
};
Process.Start(psi);
e.Handled = true;
}
}
}
The code defines an attached property that when you add to a Hyperlink, will handle the logic for navigating to the links for you.
You can use the following code the following way:
<Window
x:Class="WpfDemo.MainWindow"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:helper="clr-namespace:WpfDemo.Extensions"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Grid>
<TextBlock>
<Hyperlink helper:HyperlinkExtensions.IsExternal="true" NavigateUri="https://google.com">
Google Link
</Hyperlink>
</TextBlock>
</Grid>
</Window>
I want to show in my C#-WPF application a text containing links. The texts are static and known during compile time.
The following is doing want i want when working directly on the XAML file:
<TextBlock Name="TextBlockWithHyperlink">
Some text
<Hyperlink
NavigateUri="http://somesite.com"
RequestNavigate="Hyperlink_RequestNavigate">
some site
</Hyperlink>
some more text
</TextBlock>
Since using MVVM i want to bind the Textblock to a newly constructed Textblock object, through a dependency property. The XAML then looks like this:
<StackPanel Grid.Row="1" Margin="5 0 0 0">
<TextBlock Height="16" FontWeight="Bold" Text="Generic Text with link"/>
<TextBlock Text="{Binding Path=TextWithLink, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
In my ViewModel i place
private void someMethod(){
...
TextWithLink = CreateText();
...
}
private TextBlock(){
TextBlock tb = new TextBlock();
Run run1 = new Run("Text preceeding the hyperlink.");
Run run2 = new Run("Text following the hyperlink.");
Run run3 = new Run("Link Text.");
Hyperlink hyperl = new Hyperlink(run3);
hyperl.NavigateUri = new Uri("http://search.msn.com");
tb.Inlines.Add(run1);
tb.Inlines.Add(hyperl);
tb.Inlines.Add(run2);
return tb;
}
private TextBlock _textWithLink;
public TextBlock TextWithLink {
get => _textWithLink;
set{
_textWithLink = value;
OnPropertyChanged();
}
}
The dependency property setup is working i see a new TextBlock getting assigned to the XAML control, however there is no content shown, just the displayed text reads
System.Windows.Controls.TextBlock
rather than the content. I cannot get my head around what i have to change to show the desired mixed text. Happy for an help.
Instead of using a TextBlock instance in a view model, you should instead use a collection of Inline elements with a UI element that accept it as the source of a Binding.
Since the Inlines property of a TextBlock is not bindable, you may create a deribed TextBlock with a bindable property like this:
public class MyTextBlock : TextBlock
{
public static readonly DependencyProperty BindableInlinesProperty =
DependencyProperty.Register(
nameof(BindableInlines),
typeof(IEnumerable<Inline>),
typeof(MyTextBlock),
new PropertyMetadata(null, BindableInlinesPropertyChanged));
public IEnumerable<Inline> BindableInlines
{
get { return (IEnumerable<Inline>)GetValue(BindableInlinesProperty); }
set { SetValue(BindingGroupProperty, value); }
}
private static void BindableInlinesPropertyChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var textblock = (MyTextBlock)o;
var inlines = (IEnumerable<Inline>)e.NewValue;
textblock.Inlines.Clear();
if (inlines != null)
{
textblock.Inlines.AddRange(inlines);
}
}
}
Now you may use it like
<local:MyTextBlock BindableInlines="{Binding SomeInlines}"/>
with a view model property like this:
public IEnumerable<Inline> SomeInlines { get; set; }
...
var link = new Hyperlink(new Run("Search"));
link.NavigateUri = new Uri("http://search.msn.com");
link.RequestNavigate += (s, e) => Process.Start(e.Uri.ToString());
SomeInlines = new List<Inline>
{
new Run("Some text "),
link,
new Run(" and more text")
};
I need to set the font family for the next text to be written in a RichTextBox.
I tried setting that with...
<RichTextBox x:Name="RichTextEditor" MaxWidth="1000" SpellCheck.IsEnabled="True"
FontFamily="{Binding ElementName=TextFontComboBox, Path=SelectedItem}"
FontSize="{Binding ElementName=TextSizeComboBox, Path=SelectedValue}"
Width="Auto" Height="Auto" HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto" />
...but it changed the whole text. I suppose that with the Selection property I can restrict the change to be applied just to the selected area. But how for the next -not yet typed- text?
In order to set the FontFamily based on the cursor position you need to define a custom control with a dependency property that helps insert a new Run section by overriding the OnTextInput method.
I included most of the code, you'll need to modify the namespaces to fit your development environment.
The code uses a ViewModel to manage the available fonts and manage if the font changed.
This code is only a prototype and does not deal with focusing issues between the two controls.
To use this code:
1- Type some text in the RichTectBox.
2- Change the font in the ComboBox.
3- Tab back to the RichTextBox.
4- Type some more text.
Here is the custom RichTextBox control:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace RichTextboxFont.Views
{
public class RichTextBoxCustom : RichTextBox
{
public static readonly DependencyProperty CurrentFontFamilyProperty =
DependencyProperty.Register("CurrentFontFamily",
typeof(FontFamily), typeof
(RichTextBoxCustom),
new FrameworkPropertyMetadata(new FontFamily("Tahoma"),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnCurrentFontChanged)));
public FontFamily CurrentFontFamily
{
get
{
return (FontFamily)GetValue(CurrentFontFamilyProperty);
}
set
{
SetValue(CurrentFontFamilyProperty, value);
}
}
private static void OnCurrentFontChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{}
protected override void OnTextInput(TextCompositionEventArgs e)
{
ViewModels.MainViewModel mwvm = this.DataContext as ViewModels.MainViewModel;
if ((mwvm != null) && (mwvm.FontChanged))
{
TextPointer textPointer = this.CaretPosition.GetInsertionPosition(LogicalDirection.Forward);
Run run = new Run(e.Text, textPointer);
run.FontFamily = this.CurrentFontFamily;
this.CaretPosition = run.ElementEnd;
mwvm.FontChanged = false;
}
else
{
base.OnTextInput(e);
}
}
}
}
Here is the XAML:
<Window x:Class="RichTextboxFont.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RichTextboxFont.Views"
xmlns:ViewModels="clr-namespace:RichTextboxFont.ViewModels"
Title="Main Window"
Height="400" Width="800">
<DockPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ComboBox ItemsSource="{Binding Path=Fonts}"
SelectedItem="{Binding Path=SelectedFont, Mode=TwoWay}"/>
<local:RichTextBoxCustom Grid.Row="1"
CurrentFontFamily="{Binding Path=SelectedFont, Mode=TwoWay}"
FontSize="30"/>
</Grid>
</DockPanel>
</Window>
Here is the ViewModel:
If you do not use view models, let me know and I'll add the base class code too; otherwise, google/stackoverflow can help you too.
using System.Collections.ObjectModel;
using System.Windows.Media;
namespace RichTextboxFont.ViewModels
{
public class MainViewModel : ViewModelBase
{
#region Constructor
public MainViewModel()
{
FontFamily f1 = new FontFamily("Georgia");
_fonts.Add(f1);
FontFamily f2 = new FontFamily("Tahoma");
_fonts.Add(f2);
}
private ObservableCollection<FontFamily> _fonts = new ObservableCollection<FontFamily>();
public ObservableCollection<FontFamily> Fonts
{
get
{
return _fonts;
}
set
{
_fonts = value;
OnPropertyChanged("Fonts");
}
}
private FontFamily _selectedFont = new FontFamily("Tahoma");
public FontFamily SelectedFont
{
get
{
return _selectedFont;
}
set
{
_selectedFont = value;
FontChanged = true;
OnPropertyChanged("SelectedFont");
}
}
private bool _fontChanged = false;
public bool FontChanged
{
get
{
return _fontChanged;
}
set
{
_fontChanged = value;
OnPropertyChanged("FontChanged");
}
}
#endregion
}
}
Here is the Window code-behind where I initialise the ViewModel:
using System.Windows;
namespace RichTextboxFont.Views
{
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
this.DataContext = new ViewModels.MainViewModel();
}
}
}
There's a much easier way to do this: Implement a toolbar for your RichTextBox.
Unlike WinForms, the RichTextBox in WPF doesn't come with a toolbar by default, but it's really easy to create one yourself. The RichTextBox automatically handles many EditingCommands, so it's just a matter of creating a toolbar and some buttons. Microsoft has provided sample code for this at the bottom of the RichTextBox Overview on MSDN.
Unfortunately, those editing commands don't include setting the FontFace property of the selection, though you can create a ComboBox on the toolbar that can trigger the change with an event handler in the codebehind file.
That's the approach taken in this CodePlex article by Gregor Pross: WPF RichTextEditor
The project is commented in German, but the source itself is very clearly written. The codebehind used for his font selector ComboBox looks like this:
private void Fonttype_DropDownClosed(object sender, EventArgs e)
{
string fontName = (string)Fonttype.SelectedItem;
if (fontName != null)
{
RichTextControl.Selection.ApplyPropertyValue(System.Windows.Controls.RichTextBox.FontFamilyProperty, fontName);
RichTextControl.Focus();
}
}
The main reason that people struggle with the FontFace selection is that after the font selection has been made, you must return focus to the RichTextBox. If the user must manually press tab or click into the RichTextBox, a new text selection gets created and you lose the formatting options you've chosen.
One of the answers to this StackOverflow question discusses that problem.
WPF Richtextbox FontFace/FontSize
This isn't exactly a trivial answer.
To do inline text formatting in a Rich TextBox like you want you will have to modify the Document property of the RichTextBox. Very simply, something like this will work
<RichTextBox >
<RichTextBox.Document>
<FlowDocument>
<Paragraph>
<Run>Something</Run>
<Run FontWeight="Bold">Something Else</Run>
</Paragraph>
</FlowDocument>
</RichTextBox.Document>
</RichTextBox>
I think you could create a custom Control that creates a new block element and sets the font properties you need based on the user input.
For example, If the user types something then presses bold. You would want to wrap the previous text in a run and create a new run element setting the FontWeight to bold then the subsequent text will be wrapped in the bolded run.
Again, not a trivial solution but I can't think of any other way to accomplish what you are after.
This is for a Windows 10 Universal App.
XAML:
<RelativePanel Padding="4" Margin="4,12,0,0">
<TextBlock x:Name="Label" Text="Class Name" Margin="12,0,0,4"/>
<ListView x:Name="ClassTextBoxes"
ItemsSource="{Binding TextBoxList}"
SelectionMode="None" RelativePanel.Below="Label">
<ListView.ItemTemplate>
<DataTemplate >
<RelativePanel>
<TextBox x:Name="tbox"
PlaceholderText="{Binding PlaceHolder}"
Text="{Binding BoxText,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Padding="4" Width="200" MaxLength="25"/>
<TextBlock x:Name="errorLabel"
RelativePanel.Below="tbox"
Text="{Binding Error, Mode=TwoWay}"
Padding="0,0,0,4"
FontSize="10"
Foreground="Red"/>
<Button Content="Delete" Margin="12,0,0,0" RelativePanel.RightOf="tbox"/>
</RelativePanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</RelativePanel>
Model:
public class TextBoxStrings : BaseModel
{
private string _placeholder;
public string PlaceHolder
{
get { return _placeholder; }
set
{
if (_placeholder != value)
{
_placeholder = value;
NotifyPropertyChanged();
}
}
}
private string _boxText;
public string BoxText
{
get { return _boxText; }
set
{
if (_boxText != value)
{
_boxText = CheckBoxText(value);
NotifyPropertyChanged();
}
}
}
public string CheckBoxText(string val)
{
var r = new Regex("[^a-zA-Z0-9]+");
return r.Replace(val, "");
}
}
ViewModel:
private TrulyObservableCollection<TextBoxStrings> _textBoxList;
public TrulyObservableCollection<TextBoxStrings> TextBoxList
{
get { return _textBoxList; }
set
{
if (_textBoxList != value)
{
_textBoxList = value;
RaisePropertyChanged();
}
}
}
and I add new TextBoxString objects to my TextBoxList collection from within my view-model.
I want to make it that users can't type in certain characters (or rather, they get deleted whenever they
are typed in.
This works...in the model. Setting breakpoints and looking at the values, everything in the Model is working: value goes into the setter and gets changed, _boxText holds the new value that is set from CheckBoxText();
But the problem is, in my View, the textbox doesn't reflect changes to the underlying text that I make in the model.
So if I type in "abc*()" into "tbox", the value in the model will be "abc". The value of the textbox, however, will still be "abc*()".
I have a feeling it has something to do with the fact that I'm editing items that are inside of a collection and I don't have anything implemented to handle changing items within a collection. I was under the impression that using INotifyPropertyChanged and ObservableCollection<T> would take care of that for me.
Does anyone have any suggestions?
Thank you!
Edit: So, now I'm trying to use TrulyObservableCollection because I thought this was the problem, but it hasn't helped. Here it is: https://gist.github.com/itajaja/7507120
But the problem is, in my View, the textbox doesn't reflect changes to the underlying text that I make in the model.
As you've seen, the TextBox do reflect changes to your model. When you type in "abc*()" in the TextBox, the value in the model will be changed to "abc". The problem here is that the binding system in UWP is "intelligent". For TwoWay bindings, changes to the target will automatically propagate to the source and in this scenario, binding system assumes that the PropertyChanged event will fire for corresponding property in source and it ignores these events. So even you have RaisePropertyChanged or NotifyPropertyChanged in you source, the TextBox still won't update.
In WPF, we can call BindingExpression.UpdateTarget Method to force the update. But this method is not available in UWP.
As a workaround, you should be able to use TextBox.TextChanged event to check the input like following:
private void tbox_TextChanged(object sender, TextChangedEventArgs e)
{
var tb = sender as TextBox;
if (tb != null)
{
var originalText = tb.Text;
var r = new Regex("[^a-zA-Z0-9]+");
if (originalText != r.Replace(originalText, ""))
{
var index = (tb.SelectionStart - 1) < 0 ? 0 : (tb.SelectionStart - 1);
tb.Text = r.Replace(originalText, "");
tb.SelectionStart = index;
}
}
}
However it may break your MVVM model, you can use data validation to avoid this and here is a blog: Let’s Code! Handling validation in your Windows Store app (WinRT-XAML) you can refer to. And for my personal opinion, data validation is a better direction for this scenario.
if (_boxText != value)
{
_boxText = CheckBoxText(value);
NotifyPropertyChanged();
}
Try changing this to:
var tmp = CheckBoxText(value);
if (_boxText != tmp)
{
_boxText = tmp;
NotifyPropertyChanged();
}
I hope, in your XAML, the binding to property BoxText is two-way, right?
You should edit BoxText and then send checked value to UI. Just send value to CheckBoxText and already edited should be assigned to _boxText. And then you should send BoxText to UI by calling RaisePropertyChanged("BoxTest"). Please, see the following code snippet:
private string _boxText;
public string BoxText
{
get { return _boxText; }
set
{
if (_boxText != value)
{
_boxText=CheckBoxText(value);
RaisePropertyChanged("BoxText");
}
}
}
There is no difference where you use INotifyPropertyChanged for one property of for properties placed in collection. The complete example with collections and ListView can be seen here
I need to change the display order of the text in my UWP app but unfortunately I don't find any straight solution to do so.
The textblock in WinRT does not support this property, at least I can't found any information about this feature from MSDN. I found a solution that I need create a "New" textblock control which supports the text display in vertical order but the solution is for silverlight so I'm working on it to see whether it works or not.
This is how textblock works normally:
This is how textblock that I want it to work:
I know there is a way that just setting up the Width and text wraping something but it only works for a certain screen size & resolution, which means under other screen the text will not display properly
Any tips would be appreciated.
To get a "real" vertical text in UWP try the following:
<TextBlock Text="Rotated Text"
FontSize="18"
Foreground="Black">
<TextBlock.RenderTransform>
<RotateTransform Angle="-90" />
</TextBlock.RenderTransform>
</TextBlock>
Edit - UWP verison with user control
VerticalTextBlock - code behind
public partial class VerticalTextBlock : UserControl
{
public VerticalTextBlock()
{
InitializeComponent();
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text",
typeof(string),
typeof(VerticalTextBlock),
new PropertyMetadata(string.Empty, textChangeHandler));
private static void textChangeHandler(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var prop = d as VerticalTextBlock;
var textBlock = prop.TheTextBlock;
var str = (e.NewValue as string);
textBlock.Inlines.Clear();
for (int i = 0; i < str.Length-1; i++)
{
textBlock.Inlines.Add(new Run() { Text = str[i] + Environment.NewLine });
}
textBlock.Inlines.Add(new Run() { Text = str[str.Length-1].ToString()});
}
}
VerticalTextBlock - XAML
<UserControl
...
>
<TextBlock x:Name="TheTextBlock"/>
</UserControl>
Usage and test - XAML
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Name="a" Text="ASD"></TextBlock>
<local:VerticalTextBlock x:Name="b" Text="{Binding ElementName=a, Path=Text}" />
<local:VerticalTextBlock x:Name="c" Text="{Binding ElementName=b, Path=Text}" />
<TextBlock x:Name="d" Text="{Binding ElementName=c, Path=Text}"></TextBlock>
<TextBlock TextAlignment="Center" HorizontalAlignment="Left">
<Run Text="A"/>
<LineBreak/>
<Run Text="S"/>
<LineBreak/>
<Run Text="D"/>
<LineBreak/>
<Run Text="A"/>
<LineBreak/>
<Run Text="S"/>
<LineBreak/>
<Run Text="D"/>
</TextBlock>
</StackPanel>
Original Answer - didn't notice it's UWP not WPF
You got me interested as I've only done this in Android, so there are a few solutions that will work but I decided to try custom control extending TextBlock
public partial class VerticalTextBlock : TextBlock
{
public VerticalTextBlock()
{
InitializeComponent();
}
new public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
new public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text",
typeof(string),
typeof(VerticalTextBlock),
new PropertyMetadata(string.Empty, textChangeHandler));
private static void textChangeHandler(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var prop = d as VerticalTextBlock;
var str = (e.NewValue as string);
var inlines = str.Select(x => new Run(x + Environment.NewLine));
prop.Inlines.Clear();
prop.Inlines.AddRange(inlines);
}
}
Usage in XAML
<local:VerticalTextBlock Text="AABBCCDDEEFF" />