source

리치 텍스트 상자 wpf 바인딩

factcode 2023. 4. 9. 22:25
반응형

리치 텍스트 상자 wpf 바인딩

"DataBinding"의 Document pf inRichtextBox, ★★★★★★★★★★★★★★★★★★,RichtextBox 해서 '아예'를 붙입니다.DependencyProperty 「이 도 사용할 수 있습니다.

첫 번째도 두 번째도 만족스럽지 못하다.Data Binding이 가능한 상용 RTF 컨트롤 또는 다른 솔루션을 알고 있는 사람이 있습니까?통상적인TextBox텍스트 서식이 필요하기 때문에 다른 방법이 없습니다.

감 잡히는 게 없어요?

훨씬 더 쉬운 방법이 있어요!

을 쉽게 수 .DocumentXaml (오류)DocumentRTF하여 " " " 를 바인딩할 수 .RichTextBox의 문서입니다.요.Autobiography입니다.

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

가능 Voila!RichTextBox 데이터!

. RTF 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 텍스트 형식.에는 Xaml Package 속성이 있습니다.byte[]string.

Xaml Package 포맷은 일반 XAML에 비해 몇 가지 장점이 있습니다.특히 이미지 등의 리소스를 포함할 수 있으며 RTF보다 유연하고 사용하기 쉽습니다.

이 질문이 15개월 동안 아무도 이것을 쉽게 할 수 있는 방법을 지적하지 않고 있었다는 것을 믿기 어렵다.

오래된 투고인 것은 알지만, Extended WPF Toolkit을 확인해 주세요.여기에는 사용자가 수행할 작업을 지원하는 RichTextBox가 있습니다.

적절한 솔루션을 제공할 수 있습니다.그러기 전에 먼저 Document가 왜 discuse가 아닌지에 대해 설명하겠습니다.DependencyProperty우선은요.

의 중RichTextBox 제어,Document이치노RichTextBox is is is is is is is is is is로 됩니다.FlowDocument이되며 여러 가지 및 수 , 은 " " " " " " " " " 입니다. " 입니다.Document의 1개의 인스턴스가 남아 있다.FlowDocument 때문에 러, ., 것, 것, 것, 것, ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., . .,DependencyProperty아, 아, 아, 아, 아, 아. 를 개 FlowDocument1번으로 하다어디에서나 동일한 인스턴스이므로 모든 사용자가 변경 사항에 액세스할 수 있습니다.

는 그렇게 하지 않는다.FlowDocument는 문서 변경 알림을 지원합니다.다만, 확실히는 문서 변경 통지를 서포트하고 있습니다.

그렇다면, 여기 해결책이 있습니다.시작하기 전에RichTextBox구현되지 않음INotifyPropertyChanged또한 문서는DependencyProperty, 이 경우 알림이 없습니다.RichTextBox의 Document 속성이 변경되므로 바인딩은 단방향으로만 할 수 있습니다.

다음을 제공하는 클래스를 만듭니다.FlowDocument. 바인딩을 위해서는 다음 항목이 필요합니다.DependencyProperty이 클래스는 다음에서 상속됩니다.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); }
    }
}

작성하다WindowXAML의 리치 텍스트박스가 있어요

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

를 제공하다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논리 트리에 삽입할 수 있도록 합니다.

, 이렇게 요.Documentproperty の 。HasDocument의 , 「 」Document화할것것 것것것다다

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 이벤트 핸들러는 SetDocumentXaml을 재귀적으로 호출할 수 있으므로 이에 대한 보호를 제공했습니다.TextRange 대신 XamlReader/XamlWriter도 사용했습니다.

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));
                    }
                };
            }
        )
    );
}
 <RichTextBox>
     <FlowDocument PageHeight="180">
         <Paragraph>
             <Run Text="{Binding Text, Mode=TwoWay}"/>
          </Paragraph>
     </FlowDocument>
 </RichTextBox>

이것이 지금까지 가장 쉬운 방법이라고 생각되며, 이러한 답변에는 표시되지 않습니다.

에는 ★★★★★★★★★★★★★★★★★★★★★★★」Text★★★★★★ 。

RTB라는 이름의 RichTextBox가 있는 UserControl을 만듭니다.이제 다음 종속성 속성을 추가합니다.

    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;
        FlowDocument document = e.NewValue as FlowDocument;
        if (document  == null)
        {
            control.RTB.Document = new FlowDocument(); //Document is not amused by null :)
        }
        else
        {
            control.RTB.Document = document;
        }
    }

이 솔루션은 아마도 어디서 본 '대리' 솔루션일 것입니다.하지만..RichTextBox에는 DependencyProperty로 문서가 없습니다...그러니 다른 방법으로 해야 해

HTH

제 요구의 대부분은 krzysztof의 https://stackoverflow.com/a/2989277/3001007에 대한 답변으로 충족되었습니다.그러나 이 코드의 한 가지 문제(제가 직면한 문제)는 바인딩이 여러 컨트롤에서는 작동하지 않는다는 것입니다.그래서 나는 변했다._recursionProtection a 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}" />

다음은 Ray Burns의 Data Binding 답변과 XAML 문자열의 RichTextBox-Document 변환에 기초한 솔루션입니다.

뷰 모델

    TestText = @"<FlowDocument xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""><Paragraph><Bold>Hello World!</Bold></Paragraph></FlowDocument>";

보다

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

리치 텍스트 박스 헬퍼

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;
                var    xaml        = GetDocumentXaml(richTextBox);
                Stream sm          = new MemoryStream(Encoding.UTF8.GetBytes(xaml));
                richTextBox.Document = (FlowDocument) XamlReader.Load(sm);
                sm.Close();
            }
        }
    );
}

여기 VB가 있습니다.Lolo 답변의 넷 버전:

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

종료 클래스

이 VB.넷 버전은 상황에 맞게 작동합니다.스레드 컬렉션 세마포어를 삭제하고 대신 RemoveHandler와 AddHandler를 사용했습니다.또한 FlowDocument는 한 번에 하나의 RichTextBox에만 바인딩할 수 있기 때문에 RichTextBox의 IsLoaded=True가 있는지 확인합니다.먼저 Windows 대신 ResourceDictionary를 사용하는 MVVM 앱에서 클래스를 사용한 방법부터 살펴보겠습니다.

    ' 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

얘들아, 왜 쓸데없는 소리만 해?이건 완벽하게 작동한다.코드는 필요 없습니다.

<RichTextBox>
    <FlowDocument>
        <Paragraph>
            <Run Text="{Binding Mytextbinding}"/>
        </Paragraph>
    </FlowDocument>
</RichTextBox>

언급URL : https://stackoverflow.com/questions/343468/richtextbox-wpf-binding

반응형