【问题标题】:Binding text to attached property将文本绑定到附加属性
【发布时间】:2014-02-19 22:42:28
【问题描述】:

我的问题与此类似:WPF Generate TextBlock Inlines 但我没有足够的声誉发表评论。这是附加的属性类:

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);
        }
    }
}

我正在使用这个附加的属性类并尝试将其应用到文本块,以使文本从我的视图模型类中的字符串中识别内联值,如粗体、下划线等。我的文本块中有以下 XAML:

<TextBlock Grid.Row="1" Grid.Column="1" TextWrapping="Wrap" my:Attached.FormattedText="test" />

但是,当我启动程序时,我在文本块中什么也没有。我也想最终将文本绑定到我的视图模型上的一个属性,但想先显示一些东西......

抱歉,这可能是一个新手问题,但我不知道为什么它不起作用。它在这里没有给我任何错误,只是没有出现。如果我尝试绑定,它会给我错误:

{“无法在“TextBlock”类型的“SetFormattedText”属性上设置“Binding”。“Binding”只能在 DependencyObject 的 DependencyProperty 上设置。”}

【问题讨论】:

  • 在 StackOverflow,您应该以问题的形式提出问题,而以 cmets 的形式提出,您最好提供另一个问题的链接。但是,您需要提供 all of the relevant code that is required to reproduce your problem,因为这是社区可能决定删除您的问题的原因之一。
  • 好的对不起,谢谢指点。我更新了包含我正在尝试使用的类的源代码以及我尝试使用它的更多细节......

标签: wpf xaml mvvm textblock attached-properties


【解决方案1】:

首先,属性的类型需要是类名,而不是TextBlock的类型:

public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
    "FormattedText",
    typeof(string),
    typeof(TextBlock), <----- Here

二、handler没有被调用,必须在这里注册:

new FrameworkPropertyMetadata(string.Empty, 
                              FrameworkPropertyMetadataOptions.AffectsMeasure,
                              YOUR_PropertyChanged_HANDLER)

第三,一个例子,你需要像这样指定输入字符串:

<Bold>My little text</Bold>

工作示例如下:

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);
        }
    }
}

最初是纯文本,点击后Button会被赋值为内嵌文本。

Example for MVVM version

要在 MVVM 样式中使用此示例,您需要在 Model/ViewModel 中创建适当的属性并将其与附加的依赖属性相关联,如下所示:

<TextBlock Name="TestText"
           PropertiesExtension:TextBlockExt.FormattedText="{Binding Path=InlineText, 
                                                                    Mode=TwoWay,
                                                                    UpdateSourceTrigger=PropertyChanged}"
           Width="200"
           Height="100" 
           TextWrapping="Wrap" />

Model/ViewModel 中的属性必须支持方法NotifyPropertyChanged

这是一个完整的示例:

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>";
    }
}

此示例可在此link 获得。

【讨论】:

  • 我很抱歉,我没有忽视你,只是被拉走去灭火,现在才回到这个话题。谢谢你,这对了解正在发生的事情非常有帮助。但是,这需要您按下按钮才能将文本设置为新值。我真正需要的是将该值绑定到我的视图模型上的一个属性,该属性可能会在界面打开时发生变化。我不明白我怎么能做到这一点......这可能吗?另外,我赞成一个非常有用的答案,但在我正在寻找绑定件时尚未将其标记为答案...
  • 太棒了,标记为答案!我下载了您的示例,唯一奇怪的是它不断给我两个自定义命名空间的“未定义 CLR 命名空间”错误。我已经检查了它们,它们似乎是正确的,所以我不确定为什么。它构建和运行良好,但不知道为什么设计师会显示这些错误......
  • 实际上我只是根据您发布的内容修改了我的项目,并且我的项目中没有出现设计器错误。不确定样本中的问题是什么,但没关系。这正是我所需要的。谢谢,希望我能再次投票!
  • 这是一个很好的答案,它解决了我的问题。我发现的唯一问题是TextBlockExt.FormattedTextPropertyChanged 中的formattedText 应该是按照stackoverflow.com/a/19498780/492 编码的XML ...但只是XML 中的纯文本,而不是标记。
猜你喜欢
  • 2011-10-03
  • 1970-01-01
  • 2011-02-07
  • 2011-11-01
  • 2011-02-14
  • 1970-01-01
  • 2013-07-27
  • 2020-12-22
  • 1970-01-01
相关资源
最近更新 更多