سؤال

للقيام ربط البيانات من الوثيقة في WPF RichtextBox, رأيت 2 حلول حتى الآن ، والتي هي مستمدة من RichtextBox و إضافة DependencyProperty و أيضا الحل مع "الوكيل".لا الأولى أو الثانية مرضية.لا أحد يعرف حل آخر, أو بدلا, تجاري RTF مراقبة وهي قادرة على ربط البيانات?العادي النص ist لا بديل منذ نحتاج النص صيغة.

أي فكرة ؟

هل كانت مفيدة؟

المحلول

أعرف أن هذا هو آخر العمر, ولكن تحقق من تمديد WPF أدوات.فقد RichTextBox التي تدعم ما كنت tryign القيام به.

نصائح أخرى

هناك طريقة أسهل بكثير!

يمكنك بسهولة إنشاء المرفقة DocumentXaml (أو DocumentRTF) المنشأة والتي سوف تسمح لك لربط RichTextBox وثيقة.ويستخدم مثل هذا ، حيث سيرته الذاتية هو سلسلة الممتلكات الخاصة بك في نموذج البيانات:

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

فويلا!تماما bindable RichTextBox البيانات!

تنفيذ هذا العقار هو بسيط جدا:عند تعيين الخاصية, تحميل XAML (أو RTF) إلى FlowDocument.عندما FlowDocument التغييرات تحديث قيمة الخاصية.

هذا الرمز ينبغي أن تفعل خدعة:

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

نفس القانون يمكن أن تستخدم TextFormats.RTF أو TextFormats.XamlPackage.بالنسبة XamlPackage سيكون لديك عقار من نوع byte[] بدلا من السلسلة.

على XamlPackage شكل لديها العديد من المزايا أكثر من عادي XAML ، وخاصة وتشمل القدرة على الموارد مثل الصور و هو أكثر مرونة وأسهل للعمل مع من RTF.

فمن الصعب أن نصدق هذا السؤال جلس لمدة 15 شهرا دون أي شخص مشيرا إلى وسيلة سهلة للقيام بذلك.

أستطيع أن أعطيك موافق الحل يمكنك الذهاب مع ذلك ، ولكن قبل أن أفعل أنا ذاهب إلى محاولة لشرح السبب في الوثيقة لا أ DependencyProperty لتبدأ.

خلال فترة من التحكم RichTextBox وثيقة الملكية عموما لا تغيير.RichTextBox تهيئة مع FlowDocument.أن الوثيقة المعروضة ، يمكن تحريرها و المهترئ في العديد من الطرق, ولكن القيمة الأساسية من وثيقة الملكية يبقى أن حالة واحدة من FlowDocument.لذلك هناك حقا أي سبب يجب أن تكون تبعية الملكية ، أي Bindable.إذا كان لديك مواقع متعددة أن المرجعية هذا FlowDocument ، ما عليك سوى الإشارة مرة واحدة.لأنه هو نفس سبيل المثال في كل مكان ، تغييرات سوف تكون في متناول الجميع.

لا أعتقد FlowDocument يدعم الوثيقة تغيير الإخطارات ، على الرغم من أنني لست متأكدا.

أن يقال, هنا هو الحل.قبل أن تبدأ منذ RichTextBox لا تنفذ INotifyPropertyChanged و الوثيقة ليست تبعية الملكية ، لدينا أي إخطارات عندما RichTextBox وثيقة الملكية التغييرات ، لذلك الربط يمكن إلا أن تسير في اتجاه واحد.

إنشاء فئة من شأنها أن توفر FlowDocument.ملزمة يتطلب وجود تبعية الملكية ، لذلك هذه الفئة يرث من 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); }
        }
    }

إنشاء نافذة "مربع نص منسق" في 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>

تعطي نافذة حقل من نوع HasDocument.

HasDocument hasDocument;

نافذة منشئ يجب أن تخلق ملزمة.

hasDocument = new HasDocument();

InitializeComponent();

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

إذا كنت تريد أن تكون قادرة على اعلان ملزمة في XAML, سيكون لديك لجعل الخاصة بك HasDocument الدرجة مستمدة من FrameworkElement بحيث يمكن إدراجها في منطقية شجرة.

الآن, إذا كنت تريد تغيير وثيقة الملكية على HasDocument الغنية مربع نص وثيقة سوف تتغير أيضا.

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;

يجب ضبطها القانون السابق قليلا.الأول من كل مجموعة.تغيرت لم يعمل بالنسبة لي.بعد أن غيرت مجموعة.تغيير richTextBox.TextChanged اتضح أن TextChanged معالج الحدث الاحتجاج SetDocumentXaml بشكل متكرر لذا وفرت حماية ضد ذلك.أود أيضا أن تستخدم XamlReader/XamlWriter بدلا من 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));
                    }
                };
            }
        )
    );
}

لماذا لا مجرد استخدام FlowDocumentScrollViewer ?

 <RichTextBox>
     <FlowDocument PageHeight="180">
         <Paragraph>
             <Run Text="{Binding Text, Mode=TwoWay}"/>
          </Paragraph>
     </FlowDocument>
 </RichTextBox>

يبدو أن هذا هو أسهل طريقة حتى الآن و لم يتم عرض أي من هذه الإجابات.

في نموذج عرض فقط Text متغير.

إنشاء UserControl التي RichTextBox..الآن إضافة ما يلي تبعية الملكية:

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

هذا الحل هو على الأرجح أن "الوكيل" الحل رأيت في مكان ما..ومع ذلك..RichTextBox ببساطة لا تملك الوثيقة DependencyProperty...لذلك عليك أن تفعل هذا بطريقة أخرى...

HTH

هنا VB.Net نسخة من لولو الجواب:

Public Class RichTextBoxHelper
Inherits DependencyObject

Private Shared _recursionProtection As New HashSet(Of System.Threading.Thread)()

Public Shared Function GetDocumentXaml(ByVal depObj As DependencyObject) As String
    Return DirectCast(depObj.GetValue(DocumentXamlProperty), String)
End Function

Public Shared Sub SetDocumentXaml(ByVal depObj As DependencyObject, ByVal value As String)
    _recursionProtection.Add(System.Threading.Thread.CurrentThread)
    depObj.SetValue(DocumentXamlProperty, value)
    _recursionProtection.Remove(System.Threading.Thread.CurrentThread)
End Sub

Public Shared ReadOnly DocumentXamlProperty As DependencyProperty = DependencyProperty.RegisterAttached("DocumentXaml", GetType(String), GetType(RichTextBoxHelper), New FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender Or FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, Sub(depObj, e)
                                                                                                                                                                                                                                                                                                                    RegisterIt(depObj, e)
                                                                                                                                                                                                                                                                                                                End Sub))

Private Shared Sub RegisterIt(ByVal depObj As System.Windows.DependencyObject, ByVal e As System.Windows.DependencyPropertyChangedEventArgs)
    If _recursionProtection.Contains(System.Threading.Thread.CurrentThread) Then
        Return
    End If
    Dim rtb As RichTextBox = DirectCast(depObj, RichTextBox)
    Try
        rtb.Document = Markup.XamlReader.Parse(GetDocumentXaml(rtb))
    Catch
        rtb.Document = New FlowDocument()
    End Try
    ' When the document changes update the source
    AddHandler rtb.TextChanged, AddressOf TextChanged
End Sub

Private Shared Sub TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
    Dim rtb As RichTextBox = TryCast(sender, RichTextBox)
    If rtb IsNot Nothing Then
        SetDocumentXaml(sender, Markup.XamlWriter.Save(rtb.Document))
    End If
End Sub

End Class

هذا VB.Net الإصدار يعمل على وضعي.أزلت موضوع جمع إشارة ، بدلا من استخدام RemoveHandler و AddHandler.أيضا, منذ FlowDocument يمكن إلا أن يكون منضما إلى أحد RichTextBox في وقت وضعت في التحقق من أن RichTextBox هو IsLoaded=True.دعونا نبدأ مع ما كنت الطبقة في MVVM التطبيق الذي يستخدم ResourceDictionary بدلا من النافذة.

    ' Loaded and Unloaded events seems to be the only way to initialize a control created from a Resource Dictionary
' Loading document here because Loaded is the last available event to create a document
Private Sub Rtb_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
    ' only good place to initialize RichTextBox.Document with DependencyProperty
    Dim rtb As RichTextBox = DirectCast(sender, RichTextBox)
    Try
        rtb.Document = RichTextBoxHelper.GetDocumentXaml(rtb)
    Catch ex As Exception
        Debug.WriteLine("Rtb_Loaded: Message:" & ex.Message)
    End Try
End Sub

' Loaded and Unloaded events seems to be the only way to initialize a control created from a Resource Dictionary
' Free document being held by RichTextBox.Document by assigning New FlowDocument to RichTextBox.Document. Otherwise we'll see an of "Document belongs to another RichTextBox"
Private Sub Rtb_Unloaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
    Dim rtb As RichTextBox = DirectCast(sender, RichTextBox)
    Dim fd As New FlowDocument
    RichTextBoxHelper.SetDocumentXaml(rtb, fd)
    Try
        rtb.Document = fd
    Catch ex As Exception
        Debug.WriteLine("PoemDocument.PoemDocumentView.PoemRtb_Unloaded: Message:" & ex.Message)
    End Try
End Sub

Public Class RichTextBoxHelper
    Inherits DependencyObject

    Public Shared Function GetDocumentXaml(ByVal depObj As DependencyObject) As FlowDocument
        Return depObj.GetValue(DocumentXamlProperty)
    End Function

    Public Shared Sub SetDocumentXaml(ByVal depObj As DependencyObject, ByVal value As FlowDocument)
        depObj.SetValue(DocumentXamlProperty, value)
    End Sub

    Public Shared ReadOnly DocumentXamlProperty As DependencyProperty = DependencyProperty.RegisterAttached("DocumentXaml", GetType(FlowDocument), GetType(RichTextBoxHelper), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender Or FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, Sub(depObj, e)
                                                                                                                                                                                                                                                                                                                                   RegisterIt(depObj, e)
                                                                                                                                                                                                                                                                                                                               End Sub))


    Private Shared Sub RegisterIt(ByVal depObj As System.Windows.DependencyObject, ByVal e As System.Windows.DependencyPropertyChangedEventArgs)
        Dim rtb As RichTextBox = DirectCast(depObj, RichTextBox)
        If rtb.IsLoaded Then
            RemoveHandler rtb.TextChanged, AddressOf TextChanged
            Try
                rtb.Document = GetDocumentXaml(rtb)
            Catch ex As Exception
                Debug.WriteLine("RichTextBoxHelper.RegisterIt: ex:" & ex.Message)
                rtb.Document = New FlowDocument()
            End Try
            AddHandler rtb.TextChanged, AddressOf TextChanged
        Else
            Debug.WriteLine("RichTextBoxHelper: Unloaded control ignored:" & rtb.Name)
        End If
    End Sub

    ' When a RichTextBox Document changes, update the DependencyProperty so they're in sync.
    Private Shared Sub TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
        Dim rtb As RichTextBox = TryCast(sender, RichTextBox)
        If rtb IsNot Nothing Then
            SetDocumentXaml(sender, rtb.Document)
        End If
    End Sub

End Class

معظم احتياجاتي كانت راضية عن هذا الجواب https://stackoverflow.com/a/2989277/3001007 قبل كرزيستوف.ولكن مسألة واحدة مع هذا الرمز (التي واجهتها) ، ملزم لن تعمل مع عناصر تحكم متعددة.حتى لقد غيرت _recursionProtection مع Guid القائمة على التنفيذ.حتى انها تعمل عناصر تحكم متعددة في نفس النافذة أيضا.

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

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

        public static void SetDocumentXaml(DependencyObject obj, string value)
        {
            var fw1 = (FrameworkElement)obj;
            if (fw1.Tag == null || (Guid)fw1.Tag == Guid.Empty)
                fw1.Tag = Guid.NewGuid();
            _recursionProtection.Add((Guid)fw1.Tag);
            obj.SetValue(DocumentXamlProperty, value);
            _recursionProtection.Remove((Guid)fw1.Tag);
        }

        public static readonly DependencyProperty DocumentXamlProperty = DependencyProperty.RegisterAttached(
            "DocumentXaml",
            typeof(string),
            typeof(RichTextBoxHelper),
            new FrameworkPropertyMetadata(
                "",
                FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                (obj, e) =>
                {
                    var richTextBox = (RichTextBox)obj;
                    if (richTextBox.Tag != null && _recursionProtection.Contains((Guid)richTextBox.Tag))
                        return;


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

                    try
                    {
                        string docXaml = GetDocumentXaml(richTextBox);
                        var stream = new MemoryStream(Encoding.UTF8.GetBytes(docXaml));
                        FlowDocument doc;
                        if (!string.IsNullOrEmpty(docXaml))
                        {
                            doc = (FlowDocument)XamlReader.Load(stream);
                        }
                        else
                        {
                            doc = new FlowDocument();
                        }

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

لأجل اكتمال ، اسمحوا لي أن أضيف بعض خطوط من الرد الأصلي https://stackoverflow.com/a/2641774/3001007 قبل راي الحروق.هذا هو كيفية استخدام البرنامج المساعد.

<RichTextBox local:RichTextBoxHelper.DocumentXaml="{Binding Autobiography}" />

يا شباب لماذا عناء مع كل faff.هذا يعمل تماما.أي رمز المطلوبة

<RichTextBox>
    <FlowDocument>
        <Paragraph>
            <Run Text="{Binding Mytextbinding}"/>
        </Paragraph>
    </FlowDocument>
</RichTextBox>
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top