How to bind a TextBlock to a resource containing formatted text?

前端 未结 7 935
被撕碎了的回忆
被撕碎了的回忆 2020-11-28 08:57

I have a TextBlock in my WPF window.

 
     Some formatted text.
 

When it is r

7条回答
  •  时光取名叫无心
    2020-11-28 09:18

    EDIT:

    This line,

    is a bad approach to accesing the Project.Properties.Resources namespace. It causes awkward glitches when recompiling.

    Much better to use x:Static to do somthing like this,

    Text="{x:Static props:Resources.SomeText}"

    in your binding. Thx to Ben


    Okay, this is how I did it. It's not perfect but it works.

    Remember, there is a project resource called FormattedText.

    cs:

    // TextBlock with a bindable InlineCollection property.
    
    // Type is List(Inline) not InlineCollection becuase
    // InlineCollection makes the IDE xaml parser complain
    // presumably this is caused by an inherited attribute.
    
    public class BindableTextBlock : TextBlock
    {
        public static readonly DependencyProperty InlineCollectionProperty =
            DependencyProperty.Register(
                "InlineCollection",
                typeof(List),
                typeof(BindableTextBlock),
                new UIPropertyMetadata(OnInlineCollectionChanged));
    
        private static void OnInlineCollectionChanged(DependencyObject sender,
            DependencyPropertyChangedEventArgs e)
        {
            BinableTextBlock instance = sender as BindableTextBlock;
    
            if (instance != null)
            {
                List newText = e.NewValue as List;
                if (newText != null)
                {
                    // Clear the underlying Inlines property
                    instance.Inlines.Clear();
                    // Add the passed List to the real Inlines
                    instance.Inlines.AddRange(newText.ToList());
                }
            }
        }
    
        public List InlineCollection
        {
            get
            {
                return (List)GetValue(InlineCollectionProperty);
            }
            set
            {
                SetValue(InlineCollectionProperty, value);
            }
        }
    }
    
    // Convertor between a string of xaml with implied run elements
    // and a generic list of inlines
    
    [ValueConversion(typeof(string), typeof(List))]
    public class StringInlineCollectionConvertor : IValueConverter
    {
        public object Convert(object value, 
            Type targetType, 
            object parameter, 
            System.Globalization.CultureInfo culture)
        {
            string text = value as String;
    
            // a surrogate TextBlock to host an InlineCollection
            TextBlock results = new TextBlock();
    
            if (!String.IsNullOrEmpty(text))
            {
                //Arbritary literal acting as a replace token, 
                //must not exist in the empty xaml definition.
                const string Replace = "xxx";
    
                // add a dummy run element and replace it with the text
                results.Inlines.Add(new Run(Replace));
                string resultsXaml = XamlWriter.Save(results);
                string resultsXamlWithText = resultsXaml.Replace(Replace, text);
    
                // deserialise the xaml back into our TextBlock
                results = XamlReader.Parse(resultsXamlWithText) as TextBlock;
            }
            return results.Inlines.ToList();
        }
    
        // Not clear when this will be called but included for completeness
    
        public object ConvertBack(
            object value, 
            Type targetType, 
            object parameter, 
            System.Globalization.CultureInfo culture)
        {
            String results = String.Empty;
    
            InlineCollection inlines = value as InlineCollection;
            if (inlines != null)
            {
                //read the xaml as xml and return the "content"
                var reader = 
                    XElement.Parse(XamlWriter.Save(inlines)).CreateReader();
                reader.MoveToContent();
                results = reader.ReadInnerXml();
            }
            return results;
        }
    }
    

    xaml:

    
        
            
            
        
        
    
    

    I made 2 classes. A sub-classed TextBlock with a "bindable" InlineCollection and an IValueConverter to convert the collection from and to a String.

    Using InlineCollection directly as the type of the property made VS2010 complain, although the code still ran fine. I changed to a generic list of Inlines. I assume that there is an inherited attribute telling VS that the InlineCollection has no constructor.

    I tryed making the InlineCollection property the BindableTextBlock's ContentProperty but ran into issues and out of time. Please feel free to take the next step and tell me about it.

    I apologise for any errata but this code had to be transcribed and sanitised.

    If there is a better way of doing this, surely there must be, please tell me that too. Wouldn't it be nice if this functionality was built in or, have I missed something?

提交回复
热议问题