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 (将#修改为@)