86 votes

Richtextbox wpf liaison

Pour faire la liaison de données du Document dans un WPF RichtextBox, j'ai vu 2 Solutions jusqu'à présent, qui sont à tirer de la RichtextBox et ajouter une DependencyProperty, et aussi la solution avec un "proxy". Ni le premier ou le deuxième sont satisfaisants. Est-ce que qqn a une autre solution, ou plutôt, un commercial de contrôle au format RTF qui est capable de liaison de données? La zone de texte normale ist pas une alternative, puisque nous avons besoin de formatage de texte.

Une idée?

117voto

Ray Burns Points 38537

Il y a un moyen beaucoup plus facile!

Vous pouvez facilement créer un attaché DocumentXaml (ou DocumentRTF) des biens qui vous permettra de lier le RichTextBox du document. Il est utilisé comme cela, où l'Autobiographie est une chaîne de propriété dans votre modèle de données:

<TextBox Text="{Binding FirstName}" />
<TextBox Text="{Binding LastName}" />
<RichTextBox local:RichTextBoxHelper.DocumentXaml="{Binding Autobiography}" />

Alto! Entièrement bindable RichTextBox de données!

La mise en œuvre de cette propriété est très simple: Lorsque la propriété est définie, charger le XAML (ou RTF) dans une nouvelle FlowDocument. Lorsque le FlowDocument modifications, mise à jour de la propriété de la valeur.

Ce code devrait faire l'affaire:

using System.IO;  
using System.Text;  
using System.Windows;  
using System.Windows.Controls;  
using System.Windows.Documents;  
public class RichTextBoxHelper : DependencyObject
{
  public static string GetDocumentXaml(DependencyObject obj) 
  {
    return (string)obj.GetValue(DocumentXamlProperty); 
  }
  public static void SetDocumentXaml(DependencyObject obj, string value) 
  {
    obj.SetValue(DocumentXamlProperty, value); 
  }
  public static readonly DependencyProperty DocumentXamlProperty = 
    DependencyProperty.RegisterAttached(
      "DocumentXaml",
      typeof(string),
      typeof(RichTextBoxHelper),
      new FrameworkPropertyMetadata
      {
        BindsTwoWayByDefault = true,
        PropertyChangedCallback = (obj, e) =>
        {
          var richTextBox = (RichTextBox)obj;

          // Parse the XAML to a document (or use XamlReader.Parse())
          var xaml = GetDocumentXaml(richTextBox);
          var doc = new FlowDocument();
          var range = new TextRange(doc.ContentStart, doc.ContentEnd);

          range.Load(new MemoryStream(Encoding.UTF8.GetBytes(xaml)), 
            DataFormats.Xaml);

          // Set the document
          richTextBox.Document = doc;

          // When the document changes update the source
          range.Changed += (obj2, e2) =>
          {
            if(richTextBox.Document==doc)
            {
              MemoryStream buffer = new MemoryStream();
              range.Save(buffer, DataFormats.Xaml);
              SetDocumentXaml(richTextBox, 
                Encoding.UTF8.GetString(buffer.ToArray()));
            }
          };
       }});
     }

Le même code peut être utilisé pour TextFormats.RTF ou TextFormats.XamlPackage. Pour XamlPackage vous avez une propriété de type byte[] au lieu de string.

Le XamlPackage format a plusieurs avantages sur la plaine XAML, en particulier la possibilité d'inclure des ressources telles que des images, et il est plus souple et plus facile à travailler que le format RTF.

Il est difficile de croire que cette question a siégé pendant 15 mois sans que personne n'en soulignant le moyen facile de le faire.

27voto

Brian Lagunas Points 1473

Je sais que c'est un vieux post, mais de vérifier l' Étendue de WPF Toolkit. Il a un RichTextBox qui prend en charge ce que vous tryign à faire.

19voto

Szymon Rozga Points 11277

Je peux vous donner un ok de la solution, et vous pouvez aller avec elle, mais avant que je le fais, je vais essayer d'expliquer pourquoi le Document n'est pas une DependencyProperty pour commencer.

Au cours de la durée de vie d'un contrôle RichTextBox, le Document de propriété en général ne change pas. Le RichTextBox est initialisé avec un FlowDocument. Ce document est affiché, vous pouvez modifier et de déformation dans beaucoup de façons, mais la valeur sous-jacente de la propriété de Document reste qu'une instance de l'FlowDocument. Par conséquent, il n'y a vraiment aucune raison pour que cela devrait être une Propriété de Dépendance, c'est à dire, pouvant être liés. Si vous avez plusieurs sites de référence cette FlowDocument, vous avez seulement besoin de la référence une fois. Puisque c'est le même instance de partout, les modifications seront accessibles à tout le monde.

Je ne pense pas que FlowDocument document prend en charge les notifications de changement, même si je ne suis pas sûr.

Cela étant dit, voici une solution. Avant de commencer, depuis RichTextBox ne pas implémenter INotifyPropertyChanged et le Document n'est pas une propriété de dépendance, nous n'avons pas de notifications quand le RichTextBox du Document modifications de la propriété, de sorte que la liaison ne peut être Univoques.

Créer une classe qui va fournir la FlowDocument. Liaison nécessite l'existence d'une Dépendance de la Propriété, de sorte que cette classe hérite de DependencyObject.

class HasDocument : DependencyObject
    {
        public static readonly DependencyProperty DocumentProperty =
            DependencyProperty.Register("Document", 
                                        typeof(FlowDocument), 
                                        typeof(HasDocument), 
                                        new PropertyMetadata(new PropertyChangedCallback(DocumentChanged)));

        private static void DocumentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            Debug.WriteLine("Document has changed");
        }

        public FlowDocument Document
        {
            get { return GetValue(DocumentProperty) as FlowDocument; }
            set { SetValue(DocumentProperty, value); }
        }
    }

Créer une Fenêtre avec une zone de texte enrichi dans le code XAML.

<Window x:Class="samples.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Flow Document Binding" Height="300" Width="300"
    >
    <Grid>
      <RichTextBox Name="richTextBox" />
    </Grid>
</Window>

Donner la Fenêtre, un champ de type HasDocument.

HasDocument hasDocument;

Constructeur de fenêtre doit créer la liaison.

hasDocument = new HasDocument();

InitializeComponent();

Binding b = new Binding("Document");
b.Source = richTextBox;
b.Mode = BindingMode.OneWay;
BindingOperations.SetBinding(hasDocument, HasDocument.DocumentProperty, b);

Si vous voulez être en mesure de déclarer que la liaison en XAML, vous devez faire votre HasDocument classe de dérivés de FrameworkElement de sorte qu'il peut être inséré dans l'arborescence logique.

Maintenant, si vous modifiez la propriété de Document sur HasDocument, la riche zone de texte du Document va aussi changer.

FlowDocument d = new FlowDocument();
Paragraph g = new Paragraph();
Run a = new Run();
a.Text = "I showed this using a binding";
g.Inlines.Add(a);
d.Blocks.Add(g);

hasDocument.Document = d;

19voto

Lolo Points 8894

J'ai réglé code précédent un peu. Tout d'abord la gamme.Changé n'a pas de travail pour moi. Après j'ai changé de gamme.Changé à richTextBox.TextChanged il s'avère que l'événement TextChanged gestionnaire peut invoquer SetDocumentXaml de manière récursive, donc j'ai fourni une protection contre elle. J'ai aussi utilisé XamlReader/XamlWriter au lieu de TextRange.

public class RichTextBoxHelper : DependencyObject
{
    private static HashSet<Thread> _recursionProtection = new HashSet<Thread>();

    public static string GetDocumentXaml(DependencyObject obj)
    {
        return (string)obj.GetValue(DocumentXamlProperty);
    }

    public static void SetDocumentXaml(DependencyObject obj, string value)
    {
        _recursionProtection.Add(Thread.CurrentThread);
        obj.SetValue(DocumentXamlProperty, value);
        _recursionProtection.Remove(Thread.CurrentThread);
    }

    public static readonly DependencyProperty DocumentXamlProperty = DependencyProperty.RegisterAttached(
        "DocumentXaml", 
        typeof(string), 
        typeof(RichTextBoxHelper), 
        new FrameworkPropertyMetadata(
            "", 
            FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
            (obj, e) => {
                if (_recursionProtection.Contains(Thread.CurrentThread))
                    return;

                var richTextBox = (RichTextBox)obj;

                // Parse the XAML to a document (or use XamlReader.Parse())

                try
                {
                    var stream = new MemoryStream(Encoding.UTF8.GetBytes(GetDocumentXaml(richTextBox)));
                    var doc = (FlowDocument)XamlReader.Load(stream);

                    // Set the document
                    richTextBox.Document = doc;
                }
                catch (Exception)
                {
                    richTextBox.Document = new FlowDocument();
                }

                // When the document changes update the source
                richTextBox.TextChanged += (obj2, e2) =>
                {
                    RichTextBox richTextBox2 = obj2 as RichTextBox;
                    if (richTextBox2 != null)
                    {
                        SetDocumentXaml(richTextBox, XamlWriter.Save(richTextBox2.Document));
                    }
                };
            }
        )
    );
}

10voto

Arcturus Points 14366

Créer un UserControl qui a un RichTextBox.. Maintenant, ajoutez les éléments suivants de la propriété de dépendance:

    public FlowDocument Document
    {
        get { return (FlowDocument)GetValue(DocumentProperty); }
        set { SetValue(DocumentProperty, value); }
    }

    public static readonly DependencyProperty DocumentProperty =
        DependencyProperty.Register("Document", typeof(FlowDocument), typeof(RichTextBoxControl), new PropertyMetadata(OnDocumentChanged));

    private static void OnDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        RichTextBoxControl control = (RichTextBoxControl) d;
        if (e.NewValue == null)
            control.RTB.Document = new FlowDocument(); //Document is not amused by null :)

        control.RTB.Document = document;
    }

Cette solution est probablement que "proxy" de la solution que vous avez vu quelque part.. Cependant.. RichTextBox simplement ne pas avoir le Document en tant que DependencyProperty... de Sorte que vous avez à faire cela d'une autre façon...

HTH

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X