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

时间:2012-07-27 19:18:11

标签: c# wpf xaml

正如我正在学习的那样......我已经创建了一个简单的数据绑定项目,该项目可以很好地处理数据,例如:名字。但是,当我尝试使用lastName编译器时会抛出运行时错误

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

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

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";
    }

}

编辑:

 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:

<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>

3 个答案:

答案 0 :(得分:4)

我认为你的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" />

我认为您希望最后TextBlockx:Name="stat2",而不是之前的TextBox

更改LastName时,会调用PropertyChanged事件处理程序,这会更改stat2的文本值。因为stat2TextBox,其值使用LastName绑定绑定到TwoWay,这会导致绑定机制将您设置的值发送回视图模型。这会导致另一个PropertyChanged事件触发,这会更改stat2的值,这会导致另一个PropertyChanged事件触发....这个无限循环不会停止,这就是为什么会出现堆栈溢出错误。< / p>

FirstName没有任何此类堆栈溢出,因为stat1TextBlock,其Text属性没有绑定。

答案 1 :(得分:1)

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

你知道这是怎么回事吗?

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

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

答案 2 :(得分:0)

我认为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";
}

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

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

修改

  1. 正如其他人所说,您可能已将stat1名称分配给错误的对象,这会启动抛出StackOverflowException的无限递归。
  2. 在Person类中,必须使用属性的名称调用PropertyChanged,而不是使用其值。
  3. 下面是一个不会解决该问题的工作示例,我还想向您展示WPF平台如何允许您从View类中提取所有主要的精化任务并允许您移动该阶段在MVVM范例

    之后的Model类中

    在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));
            }
    
        }
    
    }
    

    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();
        }
    }
    

    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>