2

在我学习的过程中……我创建了一个简单的数据绑定项目,可以很好地处理数据,例如 firstName。但是,当我尝试使用 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>
4

3 回答 3

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。因为stat2是使用绑定TextBox绑定其值的,这会导致绑定机制将您设置的值发送回视图模型。这会导致另一个 PropertyChanged 事件被触发,这会改变 的值,从而导致另一个 PropertyChanged 事件被触发......这个无休止的循环不会停止,这就是你得到堆栈溢出错误的原因。LastNameTwoWaystat2

你不会得到任何这样的堆栈溢出,FirstName因为stat1它的属性TextBlock没有绑定。Text

于 2012-07-27T21:42:43.787 回答
1

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

你知道这是怎么回事吗?

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

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

于 2012-07-27T19:20:34.487 回答
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。

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

在 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>
于 2012-07-27T19:31:10.713 回答