C# 无法计算表达式,因为当前线程处于堆栈溢出状态

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11694309/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 18:52:15  来源:igfitidea点击:

Cannot evaluate expression because the current thread is in a stack overflow state

c#wpfxaml

提问by amit kohan

As I'm learning ... I have created a simple data binding project which works fine with on piece of data e.g. firstName. However, when I'm trying to use lastName compilers throws a runtime error as

在我学习的过程中……我创建了一个简单的数据绑定项目,它可以很好地处理数据,例如 firstName。但是,当我尝试使用 lastName 编译器时会引发运行时错误

** Cannot evaluate expression because the current thread is in a stack overflow state.**

** 无法计算表达式,因为当前线程处于堆栈溢出状态。**

this is the code. As you see 2nd field (last name) is commented out since it is causing stack overflow. any comment is appreciated.

这是代码。如您所见,第二个字段(姓氏)已被注释掉,因为它会导致堆栈溢出。任何评论表示赞赏。

public partial class MainWindow : Window
{
    Person p;

    public MainWindow()
    {
        InitializeComponent();

        p = new Person();
        p.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(p_PropertyChanged);

        this.DataContext = p;

        p.FirstName = p.OriginalFirstName;
        p.LastName = p.OriginalLastName;
    }

    void p_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        stat1.Text = (p.OriginalFirstName == p.FirstName) ? "Original" : "Modified";
        //stat2.Text = (p.OriginalLastName == p.LastName) ? "Original" : "Modifined";
    }

}

EDIT:

编辑:

 class Person : INotifyPropertyChanged 
    {

        public string OriginalFirstName = "Jim";
        public string OriginalLastName = "Smith";

        private string _firstName;

        #region FirstName
        public string FirstName
        {
            get { return _firstName; }
            set
            {
                if (value != null)
                {
                    _firstName = value;
                    NotifyTheOtherGuy(FirstName);
                }
            }
        }
        #endregion FirstName

        private string _lastName;

        #region LastName
        public string LastName
        {
            get { return _lastName; }
            set
            {
                if (value != null)
                {
                    _lastName = value;
                    NotifyTheOtherGuy(LastName);
                }
            }
        }
        #endregion LastName

        public Person()
        {

        }

        public event PropertyChangedEventHandler PropertyChanged;
        void NotifyTheOtherGuy(string msg)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(msg));
            }

        }

    }

XAML:

XAML:

<Window x:Class="FullNameDataBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>

        <Label Grid.Column="0" Grid.Row="0" Content="First Name:"/>
        <Label Grid.Row="1" Content="Last Name:"/>
        <TextBox  Grid.Column="1" Grid.Row="0" Background="Yellow" Margin="5" FontWeight="Bold" Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        <TextBlock x:Name="stat1" Grid.Column="2" />
        <TextBox x:Name="stat2" Grid.Column="1" Grid.Row="1" Background="Yellow" Margin="5" FontWeight="Bold" Text="{Binding Path=LastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Grid.Column="2" Grid.Row="1" />
    </Grid>
</Window>

采纳答案by Luke Woodward

I think the error in this chunk of your XAML:

我认为这块 XAML 中的错误:

    <TextBox  Grid.Column="1" Grid.Row="0" Background="Yellow" Margin="5" FontWeight="Bold" Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    <TextBlock x:Name="stat1" Grid.Column="2" />
    <TextBox x:Name="stat2" Grid.Column="1" Grid.Row="1" Background="Yellow" Margin="5" FontWeight="Bold" Text="{Binding Path=LastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    <TextBlock Grid.Column="2" Grid.Row="1" />

I think you want the last TextBlockto have x:Name="stat2", not the TextBoxbefore it.

我想你想的最后TextBlockx:Name="stat2",而不是TextBox之前。

When you change the LastName, your PropertyChanged event handler is called, which changes the text value of stat2. Because stat2is the TextBoxwhose value is bound to LastNameusing a TwoWaybinding, this causes the binding mechanism to send the value you set back to the view-model. This causes another PropertyChanged event to fire, which changes the value of stat2, which causes another PropertyChanged event to fire.... This endless cycle doesn't stop, which is why you get the stack-overflow error.

当您更改 时LastName,将调用您的 PropertyChanged 事件处理程序,这会更改 的文本值stat2。因为stat2TextBox其价值必然要LastName使用TwoWay绑定,这将导致绑定机制来送你重新设置为视图模型的价值。这会导致另一个 PropertyChanged 事件触发,这会更改 的值stat2,从而导致另一个 PropertyChanged 事件触发...。这个无休止的循环不会停止,这就是您收到堆栈溢出错误的原因。

You don't get any such stack overflow with FirstNamebecause stat1is a TextBlockwith no binding on its Textproperty.

你没有得到任何这样的堆栈溢出带FirstName,因为stat1TextBlock在其没有约束力的Text财产。

回答by Servy

Every time a property is changed, you change a property (the text value) which fires off another property changed event, which changes the text property, which fires off the ....

每次更改属性时,您都会更改一个属性(文本值),该属性会触发另一个属性更改事件,该事件会更改文本属性,从而触发 ....

Do you see where this is going?

你明白这是怎么回事吗?

You either need to disable the firing of the event when you change the text property, or not change it in the context of the property changed event handler.

您需要在更改文本属性时禁用事件触发,或者在属性更改事件处理程序的上下文中不更改它。

Since we don't have the details of your Personclass we don't know if it already support some mechanism for disabling event firing, or for changing a value without firing off events. If one doesn't exist, you may need to create your own. How you do that will depend on the implementation of that class.

由于我们没有您的Person类的详细信息,我们不知道它是否已经支持某种机制来禁用事件触发,或者在不触发事件的情况下更改值。如果不存在,您可能需要创建自己的。你如何做到这一点将取决于该类的实现。

回答by Alessandro Rossi

I suppose p_PropertyChanged method should be static and the following changes to your method could work without problems.

我想 p_PropertyChanged 方法应该是静态的,对您的方法进行以下更改可以毫无问题地工作。

static void p_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    MainWindow w = (MainWindow) sender;
    w.stat1.Text = (w.p.OriginalFirstName == w.p.FirstName) ? "Original" : "Modified";
    //stat2.Text = (p.OriginalLastName == p.LastName) ? "Original" : "Modifined";
}

but it would be better if you post part of the XAML code because you probably could get the same result in a cleaner way.

但是如果您发布部分 XAML 代码会更好,因为您可能可以以更清晰的方式获得相同的结果。

When working with WPF you should nearly forget some winform programming code practices. I suppose you should write your code using mainly Bindings and DependenciesProperty.

使用 WPF 时,您几乎应该忘记一些 winform 编程代码实践。我想您应该主要使用 Bindings 和 DependenciesProperty 来编写代码。

EDIT

编辑

  1. As someone else said you probably assigned the stat1 name to the wrong object and this started the infinite recursion that thrown the StackOverflowException.
  2. In the Person class, PropertyChanged must be called with the name of the property not with its value.
  1. 正如其他人所说,您可能将 stat1 名称分配给错误的对象,这开始了引发 StackOverflowException 的无限递归。
  2. 在 Person 类中,必须使用属性的名称而不是其值来调用 PropertyChanged。

Here follows a working sample that doesn't stop with that problem, I also wanted to show you how the WPF platform allows you to pull all the main elaboration tasks out of the View class and allowing you to move that phase in the Model class following MVVMparadigm

下面是一个不会因该问题而停止的工作示例,我还想向您展示 WPF 平台如何允许您从 View 类中提取所有主要细化任务,并允许您在 Model 类中移动该阶段MVVM范式

In the person class there's the addition of the Check properties: it evaluates the condition of your original propertychanged method that was firing the exception. Every time in the class there is a change on FirstName or LastName property if fires changed event for the change of CheckFirstName or CheckLastName. In this way you don't need to handle change events in the View class for this purpose because the evaluation of the condition is done already by this model class and the result is available and ready for a bound object.

在 person 类中添加了 Check 属性:它评估触发异常的原始 propertychanged 方法的条件。如果为 CheckFirstName 或 CheckLastName 的更改触发更改事件,则每次在类中都会更改 FirstName 或 LastName 属性。通过这种方式,您不需要为此目的处理 View 类中的更改事件,因为条件的评估已经由该模型类完成,并且结果可用并准备好用于绑定对象。

public class Person : INotifyPropertyChanged 
{

    public string OriginalFirstName = "Jim";
    public string OriginalLastName = "Smith";

    private string _firstName;

    #region FirstName
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            if (value != null)
            {
                _firstName = value;
                NotifyTheOtherGuy("CheckFirstName");
            }
        }
    }
    #endregion FirstName

    private string _lastName;

    #region LastName
    public string LastName
    {
        get { return _lastName; }
        set
        {
            if (value != null)
            {
                _lastName = value;
                NotifyTheOtherGuy("CheckLastName");
            }
        }
    }
    #endregion LastName


    public string CheckFirstName
    {
        get
        {
            return (FirstName==OriginalFirstName) ? "Original": "Modified";
        }
    }
    public string CheckLastName
    {
        get
        {
            return (LastName==OriginalLastName) ? "Original": "Modified";
        }
    }

    public Person()
    {

    }

    public event PropertyChangedEventHandler PropertyChanged;
    void NotifyTheOtherGuy(string msg)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(msg));
        }

    }

}

The MainWindow class: all the elaboration tasks are removed from this class and there's only the definition of a DependecyProperty for the Person object.

MainWindow 类:从该类中删除了所有细化任务,并且只有 Person 对象的 DependecyProperty 定义。

public partial class MainWindow : Window
{
    public static readonly DependencyProperty MyPersonProperty;
    static MainWindow()
    {
        MyPersonProperty = DependencyProperty.Register("MyPerson", typeof(Person), typeof(MainWindow));
    }
    Person MyPerson
    {
        set
        {
            SetValue(MyPersonProperty,value);
        }
        get
        {
            return GetValue(MyPersonProperty) as Person;
        }
    }
    public MainWindow()
    {
        MyPerson = new Person();

        InitializeComponent();
    }
}

The MainWindow XAML: each component is bound the the Person DependencyProperty in the right way. The TextBoxes are bound to update the Person properties values and the TextBlocks are bound to fetch the result of the Check properties that (as said before) notify its changes after the other properties of the Person class are changed.

MainWindow XAML:每个组件都以正确的方式绑定了 Person DependencyProperty。TextBoxes 绑定到更新 Person 属性值,TextBlocks 绑定获取 Check 属性的结果(如前所述)在 Person 类的其他属性更改后通知其更改。

<?xml version="1.0" encoding="utf-8"?>
<Window
    x:Class="TryPrj.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prj="clr-namespace:TryPrj"
    Title="TryPrj"
    Height="300"
    Width="300"
    x:Name="myWindow">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition
                Height="Auto" />
            <RowDefinition
                Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition
                Width="100" />
            <ColumnDefinition
                Width="100" />
            <ColumnDefinition
                Width="100" />
        </Grid.ColumnDefinitions>
        <Label
            Grid.Column="0"
            Grid.Row="0"
            Content="First Name:" />
        <Label
            Grid.Row="1"
            Content="Last Name:" />
        <TextBox
            Grid.Column="1"
            Grid.Row="0"
            Background="Yellow"
            Margin="5"
            FontWeight="Bold"
            Text="{Binding Path=MyPerson.FirstName, Mode=OneWayToSource, ElementName=myWindow, UpdateSourceTrigger=PropertyChanged}" />
        <TextBlock
            Grid.Column="2"
            Text="{Binding Path=MyPerson.CheckFirstName, Mode=OneWay, ElementName=myWindow}"
        />
        <TextBox
            Grid.Column="1"
            Grid.Row="1"
            Background="Yellow"
            Margin="5"
            FontWeight="Bold"
            Text="{Binding Path=MyPerson.LastName, Mode=OneWayToSource, ElementName=myWindow, UpdateSourceTrigger=PropertyChanged}" />
        <TextBlock
            Grid.Column="2"
            Grid.Row="1"
            Text="{Binding Path=MyPerson.CheckLastName, Mode=OneWay, ElementName=myWindow}" />
    </Grid>
</Window>