gpt4 book ai didi

c# - WPF RichTextBox - 多 block 选择

转载 作者:太空宇宙 更新时间:2023-11-03 13:18:39 25 4
gpt4 key购买 nike

您好,我目前正在处理涉及使用 RichTextBox 的 WPF 项目(使用/C#)。我目前已经完成了大部分工作,但需要帮助通过鼠标选择获得多个 block ,例如设置 TextAlignment 或 Margin。供您引用,以下将通过为 RichTextBox 插入符号设置 TextPointer 来选择 one block ,然后遍历 Document.Blocks 以获得一个个人 block 。但我想要一种能让我获得的不仅仅是一个区 block 的方法。有人可以更深入地了解如何选择多个 block (每个 block 在不同的行上)吗?

// XAML

<RichTextBox Name="rtb" HorizontalAlignment="Left">
<FlowDocument>
<Paragraph TextAlignment="Left" Margin="0.0">
<Run Text="Hello"/>
</Paragraph>
<Paragraph TextAlignment="Right" Margin="0.0">
<Run Text="World"/>
</Paragraph>
<Paragraph TextAlignment="Left" Margin="1.0">
<Run Text="Hello"/>
</Paragraph>
<Paragraph TextAlignment="Right" Margin="1.0">
<Run Text="World"/>
</Paragraph>
</FlowDocument>
</RichTextBox>


// Code Behind

var curCaret = rtb.CaretPosition;
Block curBlock = rtb.Document.Blocks.Where(x => x.ContentStart.CompareTo(curCaret) == -1 && x.ContentEnd.CompareTo(curCaret) == 1).FirstOrDefault();

最佳答案

如果你只想找到顶级Blocks与当前 selection range 重叠,你可以这样做:

public static class FlowDocumentHelper
{
public static bool Overlaps(this TextElement element, TextPointer start, TextPointer end)
{
return element.ContentEnd.CompareTo(start) > 0 && element.ContentStart.CompareTo(end) < 0;
}
}

然后

var blocks = richTextBox.Document.Blocks.Where(block => block.Overlaps(richTextBox.Selection.Start, richTextBox.Selection.End));

但是,如果您正在寻找 paragraphs与当前选择范围重叠这将不起作用,因为它们可能深埋在文本元素层次结构中,例如在 figure 中在 list 里面在 table 里面在 section 里面.要真正发现与当前选择范围重叠的段落,您必须递归遍历 block 的层次结构,而 WPF 没有提供直接的方法来执行此操作(尽管它们内部肯定有此信息!)。

因此,可以为所有可能的 TextElement 手工制作递归迭代器。可能包含 child 的类,或遍历所有 TextPointer文档中的对象并使用它们来发现层次结构。下面使用后一种策略:

public static class FlowDocumentHelper
{
public static IEnumerable<TTextElement> WalkTextRange<TTextElement>(this FlowDocument doc, TextPointer start, TextPointer end) where TTextElement : TextElement
{
var lastVisited = new Dictionary<int, TTextElement>();
foreach (var stack in doc.WalkTextHierarchy())
{
var element = stack.Peek() as TTextElement;
if (element != null)
{
TTextElement previous;
if (!lastVisited.TryGetValue(stack.Count - 1, out previous) || previous != element)
{
if (element.Overlaps(start, end))
yield return element;
lastVisited[stack.Count - 1] = element;
}
}
}
}

public static bool Overlaps(this TextElement element, TextPointer start, TextPointer end)
{
return element.ContentEnd.CompareTo(start) > 0 && element.ContentStart.CompareTo(end) < 0;
}

public static IEnumerable<Stack<DependencyObject>> WalkTextHierarchy(this FlowDocument doc)
{
if (doc == null)
throw new ArgumentNullException();

var stack = new Stack<DependencyObject>();

// Keep a TextPointer for FlowDocument.ContentEnd handy, so we know when we're done.
TextPointer docEnd = doc.ContentEnd;

// Keep going until the TextPointer is equal to or greater than ContentEnd.
for (var iterator = doc.ContentStart;
(iterator != null) && (iterator.CompareTo(docEnd) < 0);
iterator = iterator.GetNextContextPosition(LogicalDirection.Forward))
{
var parent = iterator.Parent;

// Identify the type of content immediately adjacent to the text pointer.
TextPointerContext context = iterator.GetPointerContext(LogicalDirection.Forward);

switch (context)
{
case TextPointerContext.ElementStart:
case TextPointerContext.EmbeddedElement:
case TextPointerContext.Text:
PushElement(stack, parent);
yield return stack;
break;

case TextPointerContext.ElementEnd:
break;

default:
throw new System.Exception("Unhandled TextPointerContext " + context.ToString());
}

switch (context)
{
case TextPointerContext.ElementEnd:
case TextPointerContext.EmbeddedElement:
case TextPointerContext.Text:
PopToElement(stack, parent);
break;

case TextPointerContext.ElementStart:
break;

default:
throw new System.Exception("Unhandled TextPointerContext " + context.ToString());
}
}
}

static int IndexOf<T>(Stack<T> source, T value)
{
int index = 0;
var comparer = EqualityComparer<T>.Default;
foreach (T item in source)
{
if (comparer.Equals(item, value))
return index;
index++;
}
return -1;
}

static void PopToElement<T>(Stack<T> stack, T item)
{
for (int index = IndexOf(stack, item); index >= 0; index--)
stack.Pop();
}

static void PushElement<T>(Stack<T> stack, T item)
{
PopToElement(stack, item);
stack.Push(item);
}
}

然后

var paragraphs = richTextBox.Document.WalkTextRange<Paragraph>(richTextBox.Selection.Start, richTextBox.Selection.End);

(注意 - 经过适度测试的非生产代码。)

最后,如果您想让您的用户使用 Ctrl-Select 多个不相邻的文本范围,可以在 Word 中完成,如下所述:Select items that aren't next to each other ,那我觉得你运气不好。 RichTextBox 似乎不支持此用户交互。

关于c# - WPF RichTextBox - 多 block 选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25149593/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com