1

リスト ビューにログ エントリのリストを表示する特殊なログ ビューアを開発しています。

アプリケーションは Window で構成され (実際にはCatelを使用しているため、DataWindow です)、その中に UI があります。私はMVVMを使用しているので、対応するVMも作成しました。私のモデルは、LogEntry のコレクションを持つログです。ログは、ユーザーの操作時に VM にロードされます。

各 LogEntry には、XAML に解析し、その一部をハイパーリンクに変換するテキスト (文字列プロパティ) である Message プロパティがあります。ユーザーがハイパーリンクをクリックすると、メイン VM で定義されているコマンドを実行したいと思います (VM に属するいくつかのプロパティを使用するため、そこにある必要があります)。

最初に、RichTextBox を使用しようとしました。WPF はバインドをサポートしていないため、Extended WPF Toolbox (ここ) の RTB を使用することにしました。

ITextFormatterテキストを読み取ってビルドするカスタムを作成しました((ドキュメント パラメータ) が渡されるFlowDocumentことに注意してください)。で:ITextFormatterFlowDocumentSetText

foreach (var line in text.Split('\n'))
{
    //some manipulations
    Paragraph para = new Paragraph();
    para.Inlines.Add(new Run(manipulatedText1));
    para.Inlines.Add(CreateHyperLink(manipulatedText2));
    document.Blocks.Add(para);
}

CreateHyperLink関数は をビルドし、そのコマンドHyperlinkとパラメーターを設定する必要があります。

private Hyperlink CreateHyperlink(string text)
{
    var hLink = new Hyperlink(new Bold(new Run(text)));
    hLink.TargetName = text;
    //Attach a command and set arguments (target etc)
    hLink.Command = ???
    hLink.TargetName = text;
    //Do some formatting
    return hLink;
}

これにより、書式設定されたコンテンツが RTB に表示されるようになりましたListViewが、下線が引かれているだけで、通常のテキストとして動作し、アクションはありません。(ここに質問を投稿しましたが、まだ回答がありません)。

次に、解決策を見つけようとしたときに、FlowDocumentScrollViewer. IValueConverterテキスト (メッセージ) を解析し、ハイパーリンクを含むドキュメントを作成する を作成しました。これは、少しシンプルでクリーンなソリューションのようです。このアプローチを使用すると、フォーマットされたメッセージ表示が得られ、ハイパーリンクはそのように認識されます (青色で「全体」ユニットとして表示されます) が、コマンドを起動するのにまだ運がありません。

したがって、私は2つの質問があります:

  1. どちらのコントロールを選択するのが良いですか、またはそれぞれを使用することの長所と短所は何ですか? FlowDocumentScrollViewerは本質的に読み取り専用であり、より適切な書式設定をサポートしている可能性があります (?) がListView、マウスを使用したスクロールでいくつかの問題が発生します ( の上にある場合FlowDocumentScrollViewer、リストをスクロールしません。修正できる可能性があります)。

  2. VM からハイパーリンクにコマンドを渡して実行するにはどうすればよいですか? 何らかのバインディングを行う必要があると思いますが、方法/場所がわかりません。FlowDocument をレンダリングするときに との両方で作成してその値を使用しようとしましたITextFormatterが、(インスタンスが静的リソースとして作成されるため) 正しくないか、正しくバインドしていません。IValueConverterICommand DependancyProperty

私は(で)試しました:

<local:TextToFlowDocumentConverter 
         x:Key="textToFlowDocumentConverter" 
         HyperlinkCommand="{Binding NavigateDnHyperlinkCommand, 
         RelativeSource={RelativeSource FindAncestor, 
         AncestorType={x:Type catel:DataWindow}}, Path=DataContext}"/>

VMでフォーマッター/コンバーターをインスタンス化できると思いますが、それは適切なMVVMではありません...

ところで、解析時にリンクを「ハードコード」しようとしました(CreateHyperLink上記)

hLink.RequestNavigate += new System.Windows.Navigation.RequestNavigateEventHandler(hLink_RequestNavigate);

これは両方のコントロールでは機能しませんでした

さらに、XAML でHyperlink.Clickand Hyperlink.RequestNavigate(添付プロパティ(?)) を設定し、それらをウィンドウのコード ビハインドに配置します。これは機能します (注: RTB の場合は、and を設定する必要がIsDocumentEnabled="True"ありIsReadOnly="True"ます)。

ありがとう、

トマー

4

1 に答える 1

0

上で書いたように、添付の動作でこれを実行しようとしました。残念ながら、私はそれを機能させることができませんでしたが、それは私が見逃しているいくつかのニュアンスであり、誰かがそれを思い付くことができると思います-また、おそらくこの答えは役に立つかもしれません.

だから、私がやろうとしたことは、の Hyperlink.CommandAttachedPropertyを更新するために使用できる新しいクラスを作成することです:FlowDocument

/// <summary>
/// RichTextBox helper class to allow bind command to hyperlinks in RichTextBox.Document
/// </summary>
public class RichTextBoxHyperlinkHelper
{
    /// <summary>
    /// Get the Command associated with DependencyObject
    /// </summary>
    /// <param name="obj">The DependencyObject</param>
    /// <returns>ICommand associated with this DependencyObject</returns>
    public static ICommand GetHyperlinkCommand(DependencyObject obj)
    {
        return (ICommand)obj.GetValue(HyperlinkCommandProperty);
    }

    /// <summary>
    /// Set the ICommand associated with this DependencyObject (RichTextBox)
    /// </summary>
    /// <param name="obj">The DependencyObject (RichTextBox)</param>
    /// <param name="value">The new ICommand value</param>
    public static void SetHyperlinkCommand(DependencyObject obj, ICommand value)
    {
        obj.SetValue(HyperlinkCommandProperty, value);
    }

    // Using a DependencyProperty as the backing store for HyperlinkCommand.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty HyperlinkCommandProperty =
        DependencyProperty.RegisterAttached("HyperlinkCommand", typeof(ICommand), typeof(RichTextBoxHyperlinkHelper),
        new FrameworkPropertyMetadata((RichTextBox)null, new PropertyChangedCallback(OnHyperlinkCommandSet)));

    /// <summary>
    /// A method to run when command is set initialy
    /// </summary>
    /// <param name="d"></param>
    /// <param name="e"></param>
    private static void OnHyperlinkCommandSet(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var rtb = d as RichTextBox;

        if (rtb == null)
        {
            //TODO: Throw?
            return;
        }

        FixHyperLinks(rtb);
    }

    private static void FixHyperLinks(RichTextBox rtb)
    {
        //Get the command attached to this RichTextBox
        var command = GetHyperlinkCommand(rtb);

        if (command != null && rtb.Document != null)
        {
            //Add event handler for data context changed - in which case the .Document may change as well
            rtb.DataContextChanged += new DependencyPropertyChangedEventHandler(rtb_DataContextChanged);

            //Traverse the document, find hyperlinks and set their command
            Queue<Block> blocks = new Queue<Block>();   //Use queue instead of recursion...
            rtb.Document.Blocks.ToList().ForEach(b => blocks.Enqueue(b));   //Add top level blocks

            while (blocks.Count > 0)    //While still blocks to process
            {
                var currentBlock = blocks.Dequeue();    //Get block

                //If paragraph - check inlines for Hyperlinks
                if (currentBlock is Paragraph)
                {
                    foreach (var item in (currentBlock as Paragraph).Inlines)
                    {
                        //If an Hyperlink - set its command
                        if (item is Hyperlink)
                        {
                            (item as Hyperlink).Command = command;
                        }
                        //TODO: process child inlines etc
                    }                       
                }

                //TODO: Process other types of blocks/child blocks
            }

            //Make sure document is enabled and read only so Hyperlinks work
            rtb.IsDocumentEnabled = true;
            rtb.IsReadOnly = true;
        }
    }

    //On case of data context change - rehook the command (calling FixHyperLinks)
    static void rtb_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var rtb = sender as RichTextBox;

        if (rtb != null)
            FixHyperLinks(rtb);
    }
}

次に、おそらく、次のように使用されます。

<ext:RichTextBox Text="{Binding Message}" IsDocumentEnabled="True" IsReadOnly="True"
                 local:RichTextBoxHyperlinkHelper.HyperlinkCommand="{Binding RelativeSource=
                 {RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}},
                 Path=DataContext.NavigateDnHyperlinkCommand, NotifyOnSourceUpdated=True}">
    <ext:RichTextBox.TextFormatter>
        <local:TextDnRtfFormatter />
    </ext:RichTextBox.TextFormatter>
    <ext:RichTextBox.Resources>
        <Style TargetType="{x:Type Paragraph}">
            <Setter Property="Margin" Value="2"/>
        </Style>                                    
    </ext:RichTextBox.Resources>
</ext:RichTextBox>

コードをステップ実行すると、ハイパーリンクのコマンドが VM からバインドされたコマンドに正しく設定されていることがわかりますが、ハイパーリンクをクリックしても何も起こりません。

TextDnRtfFormatter内部でコマンドを内部で定義されたコマンドに設定すると、古いコマンドが起動されることは何の価値もありませんRichTextBoxHyperlinkHelper.

さらに、私はこれを解決するために動作を使用しようとします ( )。最終的には、VM からメッセージを送信して VM でキャッチすることで ( Catel を使用して) EventToCommand、MVVM とはまったく異なる方法で解決しました。TextDnRtfFormatterIMessageMediator

于 2012-05-04T16:49:03.343 に答える