c# - Richtextbox 中的文本在选择之外应用文本装饰
问题描述
我有一个使用 MVVM 架构的 WPF 桌面应用程序。我是按照 Tim Buchalka 在 Udemy 上的“Windows Presentation Foundation Masterclass”开始制作的(付费课程,仅在有人已经知道它的内容时才提及),特别是制作简单的 Evernote 克隆的部分。
我有一个RichTextBox
和几个按钮,允许用户更改当前选定文本的格式 - 粗体、斜体、下划线和删除线。粗体和斜体可以很好地工作,问题在于下划线和删除线。我可以很好地应用任何一个属性,只要它在整个选择中都是相同的(全部带下划线,全部删除线,两者都或两者都没有)。但是,当我尝试将样式应用于选择时,其中一个部分是 udnerlined/删除线而另一部分不是,格式会应用于与选择本身相比偏移的文本范围(更不用说样式不是' t 应用了我想要的方式,但偏移量目前是更大的问题)。我会尝试添加图片以更好地说明我的意思。
选择均匀时的造型正常工作:
选中文本的非同质部分,即将点击下划线按钮:
单击按钮后如何应用样式:
我试图弄清楚使用断点发生了什么,但该方法似乎只按预期迭代选择。我将不胜感激任何建议和/或建设性的批评。无论如何,这是我的代码:
粗体按钮的方法:
private void btnUnderline_Click(object sender, RoutedEventArgs e)
{
bool isButtonChecked = (sender as ToggleButton).IsChecked ?? false;
TextDecorationCollection textDecorations = new TextDecorationCollection();
try
{
textDecorations.Add( contentRichTextBox.Selection.GetPropertyValue(
Inline.TextDecorationsProperty) as TextDecorationCollection);
if (isButtonChecked)
{ textDecorations.Add(TextDecorations.Underline); }
else { textDecorations.TryRemove(TextDecorations.Underline, out textDecorations);}
contentRichTextBox.Selection.ApplyPropertyValue(Inline.TextDecorationsProperty,
textDecorations);
}
catch (Exception)
{
textDecorations = new TextDecorationCollection();
if (!contentRichTextBox.Selection.IsEmpty)
{
var tpFirst = contentRichTextBox.Selection.Start;
var tpLast = contentRichTextBox.Selection.End;
var textRange = new TextRange(tpFirst, tpFirst.GetPositionAtOffset(1));
var underlined = (textRange.GetPropertyValue(Inline.TextDecorationsProperty)
as TextDecorationCollection).Contains(TextDecorations.Underline[0]);
for (TextPointer t = tpFirst;
t.CompareTo(tpLast) <= 0;
t = t.GetPositionAtOffset(1))
{
textDecorations.Clear();
textRange = new TextRange(t, t.GetPositionAtOffset(1));
textDecorations = textRange.GetPropertyValue(
Inline.TextDecorationsProperty) as TextDecorationCollection;
if (!underlined)
{ textDecorations.Add(TextDecorations.Underline[0]); }
else { textDecorations.TryRemove(TextDecorations.Underline,
out textDecorations); }
textRange.ApplyPropertyValue(Inline.TextDecorationsProperty,
textDecorations);
}
}
else
{
NewParagraphWhenSelectionIsEmpty();
}
}
}
删除线按钮的方法:
private void btnStrikethrough_Click(object sender, RoutedEventArgs e)
{
bool isButtonChecked = (sender as ToggleButton).IsChecked ?? false;
TextDecorationCollection textDecorations = new TextDecorationCollection();
try
{
textDecorations.Add(contentRichTextBox.Selection.GetPropertyValue(
Inline.TextDecorationsProperty) as TextDecorationCollection);
if (isButtonChecked)
{ textDecorations.Add(TextDecorations.Strikethrough); }
else { textDecorations.TryRemove(TextDecorations.Strikethrough,
out textDecorations); }
contentRichTextBox.Selection.ApplyPropertyValue(Inline.TextDecorationsProperty,
textDecorations);
}
catch (Exception)
{
textDecorations = new TextDecorationCollection();
if (!contentRichTextBox.Selection.IsEmpty)
{
var tpFirst = contentRichTextBox.Selection.Start;
var tpLast = contentRichTextBox.Selection.End;
var textRange = new TextRange(tpFirst, tpFirst.GetPositionAtOffset(1));
var strikedthrough = (textRange.GetPropertyValue(
Inline.TextDecorationsProperty) as
TextDecorationCollection).Contains(TextDecorations.Strikethrough[0]);
for (TextPointer t = tpFirst;
t.CompareTo(tpLast) <= 0;
t = t.GetPositionAtOffset(1))
{
textDecorations.Clear();
textRange = new TextRange(t, t.GetPositionAtOffset(1));
textDecorations = textRange.GetPropertyValue(
Inline.TextDecorationsProperty) as TextDecorationCollection;
if (!strikedthrough)
{ textDecorations.Add(TextDecorations.Strikethrough[0]); }
else { textDecorations.TryRemove(TextDecorations.Strikethrough,
out textDecorations); }
textRange.ApplyPropertyValue(Inline.TextDecorationsProperty,
textDecorations);
}
}
else
{
NewParagraphWhenSelectionIsEmpty();
}
}
}
富文本框中的选择发生变化时使用的方法:
private void contentRichTextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
var selectedWeight = contentRichTextBox.Selection.GetPropertyValue(
FontWeightProperty);
btnBold.IsChecked = (selectedWeight != DependencyProperty.UnsetValue)
&& (selectedWeight.Equals(FontWeights.Bold));
var selectedStyle = contentRichTextBox.Selection.GetPropertyValue(FontStyleProperty);
btnItalics.IsChecked = (selectedStyle != DependencyProperty.UnsetValue)
&& (selectedStyle.Equals(FontStyles.Italic));
var selectedDecoration = contentRichTextBox.Selection.GetPropertyValue(
Inline.TextDecorationsProperty) as TextDecorationCollection;
btnUnderline.IsChecked = (selectedDecoration != null)
&& (selectedDecoration != DependencyProperty.UnsetValue)
&& selectedDecoration.Contains(TextDecorations.Underline[0]);
btnStrikethrough.IsChecked = (selectedDecoration != null)
&& (selectedDecoration != DependencyProperty.UnsetValue)
&& selectedDecoration.Contains(TextDecorations.Strikethrough[0]);
cbEditorFontFamily.SelectedItem = contentRichTextBox.Selection.GetPropertyValue(
Inline.FontFamilyProperty);
if (contentRichTextBox.Selection.GetPropertyValue(Inline.FontSizeProperty)
!= DependencyProperty.UnsetValue)
{ cbEditorFontSize.Text = (contentRichTextBox.Selection.GetPropertyValue(
Inline.FontSizeProperty)).ToString(); }
}
插入新段落时使用的方法:
private void NewParagraphWhenSelectionIsEmpty()
{
var fontFamily = new FontFamily(cbEditorFontFamily.SelectedItem.ToString());
var fontSize = Convert.ToDouble(cbEditorFontSize.Text);
var fontStyle = (btnItalics.IsChecked ?? false) ? FontStyles.Italic :
FontStyles.Normal;
var fontWeight = (btnItalics.IsChecked ?? false) ? FontWeights.Bold :
FontWeights.Normal;
var textDecorations = new TextDecorationCollection();
if (btnUnderline.IsChecked ?? false)
{ textDecorations.Add(TextDecorations.Underline[0]); }
if (btnStrikethrough.IsChecked ?? false)
{ textDecorations.Add(TextDecorations.Strikethrough[0]); }
// Check to see if we are at the start of the textbox and nothing has been added yet
if (contentRichTextBox.Selection.Start.Paragraph == null)
{
// Add a new paragraph object to the richtextbox with the fontsize
Paragraph p = new Paragraph();
p.FontFamily = fontFamily;
p.FontSize = fontSize;
p.FontStyle = fontStyle;
p.FontWeight = fontWeight;
p.TextDecorations = textDecorations;
contentRichTextBox.Document.Blocks.Add(p);
}
else
{
// Get current position of cursor
TextPointer curCaret = contentRichTextBox.CaretPosition;
// Get the current block object that the cursor is in
Block curBlock = contentRichTextBox.Document.Blocks.Where
(x => x.ContentStart.CompareTo(curCaret) == -1
&& x.ContentEnd.CompareTo(curCaret) == 1).FirstOrDefault();
if (curBlock != null)
{
Paragraph curParagraph = curBlock as Paragraph;
// Create a new run object with the fontsize, and add it to the current block
Run newRun = new Run();
newRun.FontFamily = fontFamily;
newRun.FontSize = fontSize;
newRun.FontStyle = fontStyle;
newRun.FontWeight = fontWeight;
newRun.Foreground = new
SolidColorBrush((Color)wpfcpEditorFontColour.SelectedColor);
newRun.TextDecorations = textDecorations;
curParagraph.Inlines.Add(newRun);
// Reset the cursor into the new block.
// If we don't do this, the font size will default again when you start
// typing.
contentRichTextBox.CaretPosition = newRun.ElementStart;
}
}
}
解决方案
推荐阅读
- flow - 使用流将数据插入 kusto 表
- php - Woocommerce:如果项目具有特定产品标签,则在购物车页面中为产品添加不同的徽章
- kubernetes - 如何将 TLS 证书放入 pod 以与 Prometheus helm chart 一起使用?
- php - Mysql 准备的 SELECT 语句没有正确回显
- laravel - Laravel 多对多关系字段名称
- eclipse - 0 我试图在 Eclipse 中通过 Tomcat 运行一个项目,但是当我点击它时,它显示一个错误:
- plot - Julia Plotting:删除和修改现有线
- c# - 如何从 HttpClient 获取有关连接的信息(IP 地址)
- python - 在 Python 中计算随机搜索的概率
- ruby-on-rails - Activeadmin 中的 Formtastic:有没有一种方法可以使用多个选择输入创建多个连接记录:关联模型的 true?