My question is similar to this: WPF Generate TextBlock Inlines but I don't have enough reputation to comment. Here is the attached property class:
public class Attached
{
public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
"FormattedText",
typeof(string),
typeof(TextBlock),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure));
public static void SetFormattedText(DependencyObject textBlock, string value)
{
textBlock.SetValue(FormattedTextProperty, value);
}
public static string GetFormattedText(DependencyObject textBlock)
{
return (string)textBlock.GetValue(FormattedTextProperty);
}
private static void FormattedTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBlock = d as TextBlock;
if (textBlock == null)
{
return;
}
var formattedText = (string)e.NewValue ?? string.Empty;
formattedText = string.Format("<Span xml:space=\"preserve\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">{0}</Span>", formattedText);
textBlock.Inlines.Clear();
using (var xmlReader = XmlReader.Create(new StringReader(formattedText)))
{
var result = (Span)XamlReader.Load(xmlReader);
textBlock.Inlines.Add(result);
}
}
}
I'm using this attached property class and trying to apply it to a textblock to make the text recognize inline values like bold, underline, etc from a string in my view model class. I have the following XAML in my textblock:
<TextBlock Grid.Row="1" Grid.Column="1" TextWrapping="Wrap" my:Attached.FormattedText="test" />
However I get nothing at all in the textblock when I start the program. I also would like to bind the text to a property on my view model eventually but wanted to get something to show up first...
Sorry this is probably a newbie question but I can't figure out why it's not working. It doesn't give me any error here, just doesn't show up. If I try to bind, it gives me the error:
{"A 'Binding' cannot be set on the 'SetFormattedText' property of type 'TextBlock'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject."}
First, the type of property needs to be a class name, not the type TextBlock
:
public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
"FormattedText",
typeof(string),
typeof(TextBlock), <----- Here
Second, the handler is not called, it must be registered here:
new FrameworkPropertyMetadata(string.Empty,
FrameworkPropertyMetadataOptions.AffectsMeasure,
YOUR_PropertyChanged_HANDLER)
Thirdly, an example to work, you need to specify the input string like this:
<Bold>My little text</Bold>
Working example is below:
XAML
<Window x:Class="InlineTextBlockHelp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:this="clr-namespace:InlineTextBlockHelp"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBlock Name="TestText"
this:AttachedPropertyTest.FormattedText="TestString"
Width="200"
Height="100"
TextWrapping="Wrap" />
<Button Name="TestButton"
Width="100"
Height="30"
VerticalAlignment="Top"
Content="TestClick"
Click="Button_Click" />
</Grid>
</Window>
Code-behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
string inlineExpression = "<Bold>Once I saw a little bird, go hop, hop, hop.</Bold>";
AttachedPropertyTest.SetFormattedText(TestText, inlineExpression);
}
}
public class AttachedPropertyTest
{
public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
"FormattedText",
typeof(string),
typeof(AttachedPropertyTest),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure, FormattedTextPropertyChanged));
public static void SetFormattedText(DependencyObject textBlock, string value)
{
textBlock.SetValue(FormattedTextProperty, value);
}
public static string GetFormattedText(DependencyObject textBlock)
{
return (string)textBlock.GetValue(FormattedTextProperty);
}
private static void FormattedTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBlock = d as TextBlock;
if (textBlock == null)
{
return;
}
var formattedText = (string)e.NewValue ?? string.Empty;
formattedText = string.Format("<Span xml:space=\"preserve\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">{0}</Span>", formattedText);
textBlock.Inlines.Clear();
using (var xmlReader = XmlReader.Create(new StringReader(formattedText)))
{
var result = (Span)XamlReader.Load(xmlReader);
textBlock.Inlines.Add(result);
}
}
}
Initially be plain text, after clicking on the Button
will be assigned to inline text.
Example for MVVM version
To use this example in MVVM style, you need to create the appropriate property in the Model/ViewModel
and associate it with the attached dependency property like this:
<TextBlock Name="TestText"
PropertiesExtension:TextBlockExt.FormattedText="{Binding Path=InlineText,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Width="200"
Height="100"
TextWrapping="Wrap" />
Property in Model/ViewModel
must support method NotifyPropertyChanged
.
Here is a full sample:
AttachedProperty
public class TextBlockExt
{
public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
"FormattedText",
typeof(string),
typeof(TextBlockExt),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure, FormattedTextPropertyChanged));
public static void SetFormattedText(DependencyObject textBlock, string value)
{
textBlock.SetValue(FormattedTextProperty, value);
}
public static string GetFormattedText(DependencyObject textBlock)
{
return (string)textBlock.GetValue(FormattedTextProperty);
}
private static void FormattedTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBlock = d as TextBlock;
if (textBlock == null)
{
return;
}
var formattedText = (string)e.NewValue ?? string.Empty;
formattedText = string.Format("<Span xml:space=\"preserve\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">{0}</Span>", formattedText);
textBlock.Inlines.Clear();
using (var xmlReader = XmlReader.Create(new StringReader(formattedText)))
{
var result = (Span)XamlReader.Load(xmlReader);
textBlock.Inlines.Add(result);
}
}
}
MainViewModel
public class MainViewModel : NotificationObject
{
private string _inlineText = "";
public string InlineText
{
get
{
return _inlineText;
}
set
{
_inlineText = value;
NotifyPropertyChanged("InlineText");
}
}
}
MainWindow.xaml
<Window x:Class="InlineTextBlockHelp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:InlineTextBlockHelp.ViewModels"
xmlns:PropertiesExtension="clr-namespace:InlineTextBlockHelp.PropertiesExtension"
Title="MainWindow" Height="350" Width="525"
ContentRendered="Window_ContentRendered">
<Window.DataContext>
<ViewModels:MainViewModel />
</Window.DataContext>
<Grid>
<TextBlock Name="TestText"
PropertiesExtension:TextBlockExt.FormattedText="{Binding Path=InlineText,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Width="200"
Height="100"
TextWrapping="Wrap" />
</Grid>
</Window>
Code-behind (just for test)
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_ContentRendered(object sender, EventArgs e)
{
MainViewModel mainViewModel = this.DataContext as MainViewModel;
mainViewModel.InlineText = "<Bold>Once I saw a little bird, go hop, hop, hop.</Bold>";
}
}
This example is available at this
link
.
来源:https://stackoverflow.com/questions/21894206/binding-text-to-attached-property