如何使超链接在从RTF文件创建的FlowDocument中工作

问题描述 投票:1回答:1

我目前正在使用下面的代码来加载文档并订阅文档中的超链接。但是,GetVisuals从不返回任何内容。我需要更改什么才能使其正常工作?

由于某些字符不能很好地翻译到所有数据库,因此我们将rtf文档存储在数据库的base64中。

我已经尝试将订阅超链接链接到FlowDocumentScrollViewer的IsVisibleChanged,LayoutChanged。可以在此处的文本文件中找到样本文档-https://1drv.ms/t/s!AlTc1v3Zi2qjitQ_acN-cG3mtG-8ug?e=HRpV8n

Protected Overrides Sub OnPropertyChanged(e As DependencyPropertyChangedEventArgs)
        MyBase.OnPropertyChanged(e)
        If e.Property Is Base64DocumentProperty Then
            If String.IsNullOrWhiteSpace(Base64Document) Then Return
            Dim range As New TextRange(flowDocument.Document.ContentStart, flowDocument.Document.ContentEnd)
            If ComponentModel.DesignerProperties.GetIsInDesignMode(Me) Then
                range.Text = "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Qua igitur re ab deo vincitur, si aeternitate non vincitur? Quicquid enim a sapientia proficiscitur, id continuo debet expletum esse omnibus suis partibus; Te ipsum, dignissimum maioribus tuis, voluptasne induxit, ut adolescentulus eriperes P. Sed quid attinet de rebus tam apertis plura requirere? Atqui reperies, inquit, in hoc quidem pertinacem; Hoc dixerit potius Ennius: Nimium boni est, cui nihil est mali. Sin autem est in ea, quod quidam volunt, nihil impedit hanc nostram comprehensionem summi boni. Duo Reges: constructio interrete. </p>

<p>An me, inquis, tam amentem putas, ut apud imperitos isto modo loquar? Re mihi non aeque satisfacit, et quidem locis pluribus. Nullus est igitur cuiusquam dies natalis. Deinde dolorem quem maximum? De vacuitate doloris eadem sententia erit. Quid turpius quam sapientis vitam ex insipientium sermone pendere? Et quidem, Cato, hanc totam copiam iam Lucullo nostro notam esse oportebit; At ille pellit, qui permulcet sensum voluptate. Praeclare enim Plato: Beatum, cui etiam in senectute contigerit, ut sapientiam verasque opiniones assequi possit. Cum autem in quo sapienter dicimus, id a primo rectissime dicitur. <a href=""http://loripsum.net/"" target=""_blank"">Age sane, inquam.</a> Quippe: habes enim a rhetoribus; </p>

<p><b>Conferam avum tuum Drusum cum C.</b> <a href=""http://loripsum.net/"" target=""_blank"">Paria sunt igitur.</a> <b>Sed quid sentiat, non videtis.</b> Multa sunt dicta ab antiquis de contemnendis ac despiciendis rebus humanis; Est enim effectrix multarum et magnarum voluptatum. <mark>Igitur ne dolorem quidem.</mark> </p>

"
                Return
            End If

            Using ms = New MemoryStream(Convert.FromBase64String(Base64Document))
                range.Load(ms, DataFormats.Rtf)
            End Using
            SubscribeToHyperlinks()
        End If
    End Sub

    Private Sub SubscribeToHyperlinks()
        Dim hyperlinks = GetVisuals(flowDocument.Document).OfType(Of Hyperlink)
        For Each hyperlink In hyperlinks
            hyperlink.Command = OpenLinkCommand
            hyperlink.CommandParameter = hyperlink.NavigateUri
        Next
    End Sub

    Private Iterator Function GetVisuals(ByVal root As DependencyObject) As IEnumerable(Of DependencyObject)
        For Each child In LogicalTreeHelper.GetChildren(root).OfType(Of DependencyObject)()
            Yield child

            For Each descendants In GetVisuals(child)
                Yield descendants
            Next
        Next
    End Function

完整的用户控件。

<UserControl x:Class="InformationViewerButton"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:msl="clr-namespace:MediaSystems"
             mc:Ignorable="d" Height="30" Width="30"
             >
    <Grid Margin="0">
        <ToggleButton Margin="0" Padding="0" x:Name="toggleButton"  DockPanel.Dock="Left" Background="Transparent" BorderThickness="0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
            <Path Data="M13,9H11V7H13M13,17H11V11H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" Fill="Blue" Stroke="White" Stretch="Uniform"/>
        </ToggleButton>

        <Popup IsOpen="{Binding IsChecked, Source={x:Reference toggleButton}}" PlacementTarget="{Binding ElementName=toggleButton, Mode=OneWay}" Placement="Right" Height="400" Width="800" StaysOpen="False" VerticalOffset="-50" AllowsTransparency="True">
            <Border BorderThickness="1" CornerRadius="5" Padding="3" BorderBrush="Black" Background="White" >
                <FlowDocumentScrollViewer x:Name="flowDocument"  VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Hidden" BorderThickness="0" >
                    <FlowDocument/>
                </FlowDocumentScrollViewer>
            </Border>
        </Popup>
    </Grid>
</UserControl>

后面的代码

Imports System.ComponentModel
Imports System.IO

Public Class InformationViewerButton
    Protected Overrides Sub OnPropertyChanged(e As DependencyPropertyChangedEventArgs)
        MyBase.OnPropertyChanged(e)
        If e.Property Is Base64DocumentProperty Then
            If String.IsNullOrWhiteSpace(Base64Document) Then Return
            Dim range As New TextRange(flowDocument.Document.ContentStart, flowDocument.Document.ContentEnd)
            If ComponentModel.DesignerProperties.GetIsInDesignMode(Me) Then
                range.Text = "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Qua igitur re ab deo vincitur, si aeternitate non vincitur? Quicquid enim a sapientia proficiscitur, id continuo debet expletum esse omnibus suis partibus; Te ipsum, dignissimum maioribus tuis, voluptasne induxit, ut adolescentulus eriperes P. Sed quid attinet de rebus tam apertis plura requirere? Atqui reperies, inquit, in hoc quidem pertinacem; Hoc dixerit potius Ennius: Nimium boni est, cui nihil est mali. Sin autem est in ea, quod quidam volunt, nihil impedit hanc nostram comprehensionem summi boni. Duo Reges: constructio interrete. </p>

<p>An me, inquis, tam amentem putas, ut apud imperitos isto modo loquar? Re mihi non aeque satisfacit, et quidem locis pluribus. Nullus est igitur cuiusquam dies natalis. Deinde dolorem quem maximum? De vacuitate doloris eadem sententia erit. Quid turpius quam sapientis vitam ex insipientium sermone pendere? Et quidem, Cato, hanc totam copiam iam Lucullo nostro notam esse oportebit; At ille pellit, qui permulcet sensum voluptate. Praeclare enim Plato: Beatum, cui etiam in senectute contigerit, ut sapientiam verasque opiniones assequi possit. Cum autem in quo sapienter dicimus, id a primo rectissime dicitur. <a href=""http://loripsum.net/"" target=""_blank"">Age sane, inquam.</a> Quippe: habes enim a rhetoribus; </p>

<p><b>Conferam avum tuum Drusum cum C.</b> <a href=""http://loripsum.net/"" target=""_blank"">Paria sunt igitur.</a> <b>Sed quid sentiat, non videtis.</b> Multa sunt dicta ab antiquis de contemnendis ac despiciendis rebus humanis; Est enim effectrix multarum et magnarum voluptatum. <mark>Igitur ne dolorem quidem.</mark> </p>

"
                Return
            End If

            Using ms = New MemoryStream(Convert.FromBase64String(Base64Document))
                range.Load(ms, DataFormats.Rtf)
            End Using
            SubscribeToHyperlinks()
        End If
    End Sub

    Private Sub SubscribeToHyperlinks()
        Dim hyperlinks = GetVisuals(flowDocument.Document).OfType(Of Hyperlink)
        For Each hyperlink In hyperlinks
            hyperlink.Command = OpenLinkCommand
            hyperlink.CommandParameter = hyperlink.NavigateUri
        Next
    End Sub

    Private Iterator Function GetVisuals(ByVal root As DependencyObject) As IEnumerable(Of DependencyObject)
        For Each child In LogicalTreeHelper.GetChildren(root).OfType(Of DependencyObject)()
            Yield child

            For Each descendants In GetVisuals(child)
                Yield descendants
            Next
        Next
    End Function
#Region "OpenLinkCommand"

    Dim _OpenLinkCommand As DelegateCommand

    Protected Sub OpenLink(link As Object)
        Dim uri = CType(link, Uri)
        Process.Start(New ProcessStartInfo(uri.AbsoluteUri))
    End Sub

    Protected Function CanOpenLink(link As Object) As Boolean

        Return True
    End Function

    <DebuggerBrowsable(DebuggerBrowsableState.Never)>
    Public ReadOnly Property OpenLinkCommand As DelegateCommand
        Get
            If _OpenLinkCommand Is Nothing Then
                Dim newAction As New Action(Of Object)(AddressOf OpenLink)
                _OpenLinkCommand = New DelegateCommand(newAction, AddressOf CanOpenLink)
            End If
            Return _OpenLinkCommand
        End Get
    End Property

    Public Sub DoSomething()
        Task.Run(Sub()
                     BackgroundProcess()
                 End Sub)
    End Sub

    Private Sub BackgroundProcess()
        Dim ControlValue As Object
        Application.Current.Dispatcher.Invoke(Sub()
                                                  ControlValue = flowDocument.ToString
                                              End Sub)

        Application.Current.Dispatcher.Invoke(Sub()
                                                  ControlValue = flowDocument.ToString
                                              End Sub)
    End Sub
#End Region

    Private Sub flowDocument_IsVisibleChanged(sender As Object, e As DependencyPropertyChangedEventArgs) Handles flowDocument.IsVisibleChanged
        If flowDocument.IsVisible Then SubscribeToHyperlinks()
    End Sub

    Public Property Base64Document As String
        Get
            Return GetValue(Base64DocumentProperty)
        End Get

        Set(ByVal value As String)
            SetValue(Base64DocumentProperty, value)
        End Set
    End Property

    Public Shared ReadOnly Base64DocumentProperty As DependencyProperty =
                           DependencyProperty.Register("Base64Document",
                           GetType(String), GetType(InformationViewerButton),
                           New PropertyMetadata(Nothing))

End Class

Public Class MVVMExampleViewModel
    Implements ComponentModel.INotifyPropertyChanged

    Private _MajorChange As String
    Public Property MajorChange As String
        Get
            Return _MajorChange
        End Get
        Set(value As String)
            _MajorChange = value
            DoPropertyChanged("MajorChange")
        End Set
    End Property

    Private Sub DoPropertyChanged(propertyname As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyname))
    End Sub

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
End Class

委托命令

Imports System.Windows.Input
Imports System.Windows.Threading
Imports System.Windows
Imports System.Threading

Public Class DelegateCommand
    Implements ICommand
    Implements IDisposable

    Dim _dispatcher As Dispatcher

    Private ReadOnly Property MyDispatcher As Dispatcher
        Get
            Return _dispatcher
        End Get
    End Property

    ' Public Event CanExecuteChanged(sender As Object, e As EventArgs) Implements ICommand.CanExecuteChanged

    Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
        AddHandler(ByVal value As EventHandler)
            AddHandler CommandManager.RequerySuggested, value
        End AddHandler
        RemoveHandler(ByVal value As EventHandler)
            AddHandler CommandManager.RequerySuggested, value
        End RemoveHandler
        RaiseEvent()
            'RaiseEvent CanExecuteChanged(Me, New EventArgs)
        End RaiseEvent
    End Event

    ''' <summary>
    ''' Raises the <see cref="CanExecuteChanged"/> event.
    ''' </summary>
    Protected Overridable Sub OnCanExecuteChanged()
        If Not _dispatcher.CheckAccess() Then
            _dispatcher.Invoke(DirectCast(AddressOf OnCanExecuteChanged, ThreadStart), DispatcherPriority.Normal)
        Else
            CommandManager.InvalidateRequerySuggested()
        End If
    End Sub

    Protected Overrides Sub Finalize()
        MyBase.Finalize()
    End Sub

    Private _canExecute As Predicate(Of Object)
    Private _execute As Action(Of Object)

    ''' <summary>
    ''' Constructs an instance of <c>DelegateCommand</c>.
    ''' </summary>
    ''' <remarks>
    ''' This constructor creates the command without a delegate for determining whether the command can execute. Therefore, the
    ''' command will always be eligible for execution.
    ''' </remarks>
    ''' <param name="execute">
    ''' The delegate to invoke when the command is executed.
    ''' </param>
    Public Sub New(execute As Action(Of Object))
        Me.New(execute, Nothing)
    End Sub

    ''' <summary>
    ''' Constructs an instance of <c>DelegateCommand</c>.
    ''' </summary>
    ''' <param name="execute">
    ''' The delegate to invoke when the command is executed.
    ''' </param>
    ''' <param name="canExecute">
    ''' The delegate to invoke to determine whether the command can execute.
    ''' </param>
    Public Sub New(execute As Action(Of Object), canExecute As Predicate(Of Object))
        'execute.AssertNotNull("execute")
        _execute = execute
        _canExecute = canExecute
        If Application.Current IsNot Nothing Then
            _dispatcher = Application.Current.Dispatcher
        Else
            _dispatcher = Dispatcher.CurrentDispatcher
        End If
    End Sub

    Private Sub OnDispose()
        _canExecute = Nothing
        _execute = Nothing
    End Sub

    ''' <summary>
    ''' Determines whether this command can execute.
    ''' </summary>
    ''' <remarks>
    ''' If there is no delegate to determine whether the command can execute, this method will return <see langword="true"/>. If a delegate was provided, this
    ''' method will invoke that delegate.
    ''' </remarks>
    ''' <param name="parameter">
    ''' The command parameter.
    ''' </param>
    ''' <returns>
    ''' <see langword="true"/> if the command can execute, otherwise <see langword="false"/>.
    ''' </returns>
    Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
        If disposedValue Then Return False
        If _canExecute Is Nothing Then
            Return True
        End If
        If TypeOf _canExecute.Target Is ViewModelBase AndAlso DirectCast(_canExecute.Target, ViewModelBase).IsDisposed Then
            Dispose()
            Return False
        End If

        Return _canExecute(parameter)
    End Function

    ''' <summary>
    ''' Executes this command.
    ''' </summary>
    ''' <remarks>
    ''' This method invokes the provided delegate to execute the command.
    ''' </remarks>
    ''' <param name="parameter">
    ''' The command parameter.
    ''' </param>
    Public Sub Execute(parameter As Object) Implements ICommand.Execute
        If disposedValue Then Return
        _execute(parameter)
    End Sub
    ''' <summary>
    ''' Returns the name of this command
    ''' </summary>
    ''' <value>The name of the command</value>
    ''' <returns>The name of the command</returns>
    ''' <remarks>This has been added for public commands that may be shared between components.</remarks>
    Public Property CommandName As String

#Region "IDisposable Support"
    Private disposedValue As Boolean ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not disposedValue Then
            If disposing Then
                ' TODO: dispose managed state (managed objects).
            End If

            ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
            ' TODO: set large fields to null.
        End If
        disposedValue = True
    End Sub

    ' TODO: override Finalize() only if Dispose(disposing As Boolean) above has code to free unmanaged resources.
    'Protected Overrides Sub Finalize()
    '    ' Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
    '    Dispose(False)
    '    MyBase.Finalize()
    'End Sub

    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
        Dispose(True)
        ' TODO: uncomment the following line if Finalize() is overridden above.
        ' GC.SuppressFinalize(Me)
    End Sub
#End Region

End Class
.net wpf hyperlink
1个回答
1
投票

经过更多测试后,发现问题出在我用来配置文档的Telerik文档控件格式化链接而不是控件本身的方式上。

谢谢那些花时间去看这个的人,即使您没有回答。可以在github上找到带有好和坏rtf文件的工作示例。

https://github.com/PhoenixStoneham/InformationButtonTester

© www.soinside.com 2019 - 2024. All rights reserved.