如何解决InlineUIContainer删除时出现System.StackOverflowException
因此,我有一个RichTextBox,它可以包含一个或多个包含一个小的UserControl的InlineUIContainer。 当其中之一被删除时,将直接引发StackOverflowException(在Backspace按下键和异常之间没有我的代码运行)。
内部代码中引发了异常,由于WinDbg,我设法恢复了StackTrace,这是结果的重要部分:
...
00bce210 51048111 System.Windows.Markup.Primitives.MarkupWriter.RecordNamespaces(Scope,System.Windows.Markup.Primitives.MarkupObject,System.Windows.Markup.IValueSerializerContext,Boolean)
00bce25c 51048111 System.Windows.Markup.Primitives.MarkupWriter.RecordNamespaces(Scope,Boolean)
00bce2a8 51048111 System.Windows.Markup.Primitives.MarkupWriter.RecordNamespaces(Scope,Boolean)
00bce2f4 51048111 System.Windows.Markup.Primitives.MarkupWriter.RecordNamespaces(Scope,Boolean)
00bce340 51048111 System.Windows.Markup.Primitives.MarkupWriter.RecordNamespaces(Scope,Boolean)
00bce38c 51048111 System.Windows.Markup.Primitives.MarkupWriter.RecordNamespaces(Scope,Boolean)
00bce3d8 5104829a System.Windows.Markup.Primitives.MarkupWriter.WriteItem(System.Windows.Markup.Primitives.MarkupObject)
00bce3f0 51047ebd System.Windows.Markup.Primitives.MarkupWriter.SaveAsXml(System.Xml.XmlWriter,System.Windows.Markup.Primitives.MarkupObject)
00bce41c 51047e15 System.Windows.Markup.Primitives.MarkupWriter.SaveAsXml(System.Xml.XmlWriter,System.Object)
00bce428 51024246 System.Windows.Markup.XamlWriter.Save(System.Object,System.IO.TextWriter)
00bce43c 510241c6 System.Windows.Markup.XamlWriter.Save(System.Object)
00bce46c 51132783 System.Windows.Documents.TextTreeDeleteContentUndoUnit.copyObjectNode(System.Windows.Documents.TextTreeObjectNode,ContentContainer ByRef)
00bce484 511325c8 System.Windows.Documents.TextTreeDeleteContentUndoUnit.copyContent(System.Windows.Documents.TextTreeNode,System.Windows.Documents.TextTreeNode)
00bce4ac 5113290e System.Windows.Documents.TextTreeDeleteContentUndoUnit.copyElementNode(System.Windows.Documents.TextTreeTextElementNode,ContentContainer ByRef)
00bce4dc 51132632 System.Windows.Documents.TextTreeDeleteContentUndoUnit.copyContent(System.Windows.Documents.TextTreeNode,System.Windows.Documents.TextTreeNode)
00bce504 511323a0 System.Windows.Documents.TextTreeDeleteContentUndoUnit..ctor(System.Windows.Documents.TextContainer,System.Windows.Documents.TextPointer,System.Windows.Documents.TextPointer)
00bce52c 51406de0 System.Windows.Documents.TextTreeUndo.CreateDeleteContentUndoUnit(System.Windows.Documents.TextContainer,System.Windows.Documents.TextPointer)
00bce548 50729cc7 System.Windows.Documents.TextContainer.DeleteContentInternal(System.Windows.Documents.TextPointer,System.Windows.Documents.TextPointer)
00bce57c 50728c51 System.Windows.Documents.TextRangeEdit.DeleteContentBetweenPositions(System.Windows.Documents.TextPointer,System.Windows.Documents.TextPointer)
00bce590 50728bdc System.Windows.Documents.TextRangeEdit.DeleteEquiScopedContent(System.Windows.Documents.TextPointer,System.Windows.Documents.TextPointer)
00bce5c0 50728ae7 System.Windows.Documents.TextRangeEdit.DeleteParagraphContent(System.Windows.Documents.ITextPointer,System.Windows.Documents.ITextPointer)
00bce5dc 50fe4172 System.Windows.Documents.TextRangeEditTables.DeleteContent(System.Windows.Documents.TextPointer,System.Windows.Documents.TextPointer)
00bce620 50fa7fa1 System.Windows.Documents.TextPointer.System.Windows.Documents.ITextPointer.DeleteContentToPosition(System.Windows.Documents.ITextPointer)
00bce644 5103afb6 System.Windows.Documents.TextRangeBase.SetText(System.Windows.Documents.ITextRange,System.String)
00bce68c 50fdf012 System.Windows.Documents.TextSelection.System.Windows.Documents.ITextRange.set_Text(System.String)
00bce6b0 50fdb214 System.Windows.Documents.TextEditorTyping.OnBackspace(System.Object,System.Windows.Input.ExecutedRoutedEventArgs)
然后对MarkupWriter.RecordNamespaces的调用将增加13000次,直到发生异常。我做了一些研究,当删除InlineUIContainer时,该框架尝试将其序列化为XAML,以便能够执行Undo命令。但是在这种情况下,序列化正在MarkupWriter.RecordNamespaces调用上循环。
因为它是内部代码,所以我不知道如何防止此循环。我想到的是完全阻止对此InlineUIContainer进行序列化,但是我还没有找到一种方法。
有一个吗?
编辑:
根据要求,这是RichTextBox和UserControl的定义。
CurveFormulaTextBox.xaml
<UserControl x:Class="StruCAT.Views.CurveFormulaTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prop="clr-namespace:StruCAT.Properties"
xmlns:viewmodels="clr-namespace:StruCAT.viewmodels"
x:Name="ThisCurveFormulaTextBox"
d:DataContext="{x:Type viewmodels:Parameterviewmodel}"
DataContextChanged="UserControl_DataContextChanged"
mc:Ignorable="d">
<DockPanel>
<RichTextBox x:Name="RichTextBox"
MinWidth="250"
Padding="1"
VerticalContentAlignment="Center"
IsDocumentEnabled="True"
LostFocus="RichTextBox_LostFocus"
PreviewKeyDown="RichTextBox_PreviewKeyDown"
SelectionChanged="RichTextBox_SelectionChanged">
<RichTextBox.Document>
<FlowDocument LineHeight="24" PageWidth="10000">
<Paragraph x:Name="Paragraph" Padding="0,0" />
</FlowDocument>
</RichTextBox.Document>
</RichTextBox>
<Popup x:Name="InsertionPopup"
AllowsTransparency="True"
IsOpen="{Binding IsFocused,ElementName=RichTextBox,Mode=OneWay}"
PlacementTarget="{Binding ElementName=RichTextBox}">
<StackPanel>
<StackPanel.Effect>
<DropShadowEffect BlurRadius="2"
Opacity="0.5"
ShadowDepth="0" />
</StackPanel.Effect>
<polygon HorizontalAlignment="Center"
Fill="White"
Points="4,0 0,8,8" />
<Border Margin="1,1,1"
Padding="1"
Background="White"
BorderThickness="0"
CornerRadius="3">
<StackPanel Orientation="Horizontal">
<Button Margin="0"
Padding="1,0"
Content="{StaticResource CurveIcon}"
PreviewMouseLeftButtonDown="InsertCurve"
Style="{StaticResource CustomButton}"
ToolTip="{x:Static prop:Resources.AddCurve}" />
<Button Margin="0"
Content="{StaticResource VariableIcon}"
PreviewMouseLeftButtonDown="Insertvariable"
Style="{StaticResource CustomButton}"
ToolTip="{x:Static prop:Resources.AddVariable}" />
</StackPanel>
</Border>
</StackPanel>
</Popup>
</DockPanel>
</UserControl>
CurveFormulaTextBox.xaml.cs
using StruCAT.viewmodels;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
namespace StruCAT.Views
{
/// <summary>
/// Logique d'interaction pour CurveFormulaTextBox.xaml
/// </summary>
public partial class CurveFormulaTextBox : UserControl
{
public CurveFormulaTextBox()
{
InitializeComponent();
}
public bool Working { get; set; } = false;
public string Formula
{
get { return (string)GetValue(FormulaProperty); }
set { SetValue(FormulaProperty,value); }
}
public static readonly DependencyProperty FormulaProperty = DependencyProperty.Register("Formula",typeof(string),typeof(CurveFormulaTextBox),new PropertyMetadata("",FormulaChanged));
private static void FormulaChanged(object sender,DependencyPropertyChangedEventArgs e)
{
var cftb = (CurveFormulaTextBox)sender;
if (e.NewValue != null && !cftb.Working)
cftb.FillParagraph();
}
private void UpdateFormula()
{
Working = true;
var formula = "";
foreach (var inline in Paragraph.Inlines)
{
if (inline.GetType() == typeof(Run))
formula += ((Run)inline).Text;
else if (inline.GetType() == typeof(InlineUIContainer) && ((InlineUIContainer)inline).Child.GetType() == typeof(KeywordComboBox))
{
var kw = ((KeywordComboBox)((InlineUIContainer)inline).Child).SelectedKeyword;
if (kw != null)
formula += "{" + kw.Model.Type + ";" + kw.Model.ID + "}";
}
}
Formula = formula;
Working = false;
}
public void FillParagraph()
{
Working = true;
foreach (var match in Regex.Matches(Formula,"{.*?}|[^{}]+"))
{
if (match.ToString().StartsWith("{"))
{
var targettype = match.ToString().Replace("{","").Replace("}","").Trim().Split(';')[0];
var keywordID = match.ToString().Replace("{","").Trim().Split(';')[1];
var cbo = CreateKCB(targettype);
cbo.SelectedKeyword = (DataContext as Parameterviewmodel).Parent.Parent.Parent.Keywords.FirstOrDefault(x => x.Model.ID == keywordID);
Paragraph.Inlines.Add(cbo);
}
else
{
Paragraph.Inlines.Add(new Run(match.ToString()));
}
}
Working = false;
}
private KeywordComboBox CreateKCB(string type)
{
var kcb = new KeywordComboBox() { Type = type,DataContext = this.DataContext };
kcb.SelectionChanged += () => UpdateFormula();
return kcb;
}
private void UserControl_DataContextChanged(object sender,DependencyPropertyChangedEventArgs e) {
FillParagraph();
}
private void RichTextBox_LostFocus(object sender,RoutedEventArgs e)
{
UpdateFormula();
}
private void RichTextBox_PreviewKeyDown(object sender,KeyEventArgs e)
{
if (e.Key == Key.Enter) e.Handled = true;
}
private void RichTextBox_SelectionChanged(object sender,RoutedEventArgs e)
{
Rect positionRect = RichTextBox.CaretPosition.GetCharacterRect(LogicalDirection.Backward);
InsertionPopup.HorizontalOffset = positionRect.Left - ((FrameworkElement)InsertionPopup.Child).ActualWidth / 2.0;
UpdateFormula();
}
private void InsertCurve(object sender,MouseButtonEventArgs e)
{
new InlineUIContainer(CreateKCB("Curve"),RichTextBox.CaretPosition);
}
private void Insertvariable(object sender,MouseButtonEventArgs e)
{
new InlineUIContainer(CreateKCB("Float"),RichTextBox.CaretPosition);
}
}
}
KeywordComboBox.xaml
<UserControl x:Class="StruCAT.Views.KeywordComboBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodels="clr-namespace:StruCAT.viewmodels"
x:Name="ThisKeywordComboBox"
Margin="1,-6"
d:DataContext="{x:Type viewmodels:Parameterviewmodel}"
mc:Ignorable="d">
<DockPanel Height="22">
<Border Background="WhiteSmoke"
BorderBrush="Silver"
BorderThickness="1,1"
CornerRadius="2,2"
UseLayoutRounding="True">
<ContentPresenter DockPanel.Dock="Left">
<ContentPresenter.Style>
<Style targettype="{x:Type ContentPresenter}">
<Setter Property="Content" Value="{StaticResource VariableIcon}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Type,ElementName=ThisKeywordComboBox}" Value="Curve">
<Setter Property="Content" Value="{StaticResource CurveIcon}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
</Border>
<ComboBox Padding="3,3,3"
displayMemberPath="Name"
IsEnabled="{Binding Path=ItemsSource,RelativeSource={RelativeSource Self},Converter={StaticResource EmptyToFalseConverter}}"
IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedKeyword,ElementName=ThisKeywordComboBox,UpdateSourceTrigger=PropertyChanged}"
SelectionChanged="ComboBox_SelectionChanged">
<ComboBox.ItemsSource>
<MultiBinding Converter="{StaticResource KeywordsFilter}">
<Binding Path="Parent.Parent.Parent.Keywords" />
<Binding ElementName="ThisKeywordComboBox" Path="Type" />
<Binding Path="Parent.Parent.Keywordviewmodel" />
<Binding Path="Parent.Keywords" />
<!-- USED ONLY TO TRIGGER UPDATE -->
<Binding Path="Parent.Parent.Parent.Keywords.Count" />
<Binding Path="Parent.Keywords.Count" />
</MultiBinding>
</ComboBox.ItemsSource>
</ComboBox>
</DockPanel>
</UserControl>
KeywordComboBox.xaml.cs
using StruCAT.viewmodels;
using System.Windows;
using System.Windows.Controls;
namespace StruCAT.Views
{
/// <summary>
/// Logique d'interaction pour KeywordComboBox.xaml
/// </summary>
public partial class KeywordComboBox : UserControl
{
public KeywordComboBox()
{
InitializeComponent();
}
public Keywordviewmodel SelectedKeyword
{
get { return (Keywordviewmodel)GetValue(SelectedKeywordProperty); }
set { SetValue(SelectedKeywordProperty,value); }
}
public static readonly DependencyProperty SelectedKeywordProperty = DependencyProperty.Register("SelectedKeyword",typeof(Keywordviewmodel),typeof(KeywordComboBox),new PropertyMetadata(null));
public string Type
{
get { return (string)GetValue(TypeProperty); }
set { SetValue(TypeProperty,value); }
}
public static readonly DependencyProperty TypeProperty = DependencyProperty.Register("Type",new PropertyMetadata("Float"));
private void ComboBox_SelectionChanged(object sender,SelectionChangedEventArgs e)
{
SelectionChanged();
}
public delegate void SelectionChange();
public event SelectionChange SelectionChanged;
}
}
在注释中提供的第一个解决方案从(?)开始消失,该解决方案是通过将RichTextBox的UndoLimit设置为0来完全阻止Undo命令。它有效地防止了删除InlineUIContainer时的异常。如果没有其他解决方案,我将使用此解决方案。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。