正如我正在学习的那样......我已经创建了一个简单的数据绑定项目,该项目可以很好地处理数据,例如:名字。但是,当我尝试使用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>
答案 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" />
我认为您希望最后TextBlock
有x:Name="stat2"
,而不是之前的TextBox
。
更改LastName
时,会调用PropertyChanged事件处理程序,这会更改stat2
的文本值。因为stat2
是TextBox
,其值使用LastName
绑定绑定到TwoWay
,这会导致绑定机制将您设置的值发送回视图模型。这会导致另一个PropertyChanged事件触发,这会更改stat2
的值,这会导致另一个PropertyChanged事件触发....这个无限循环不会停止,这就是为什么会出现堆栈溢出错误。< / p>
FirstName
没有任何此类堆栈溢出,因为stat1
是TextBlock
,其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编写代码。
修改
下面是一个不会解决该问题的工作示例,我还想向您展示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>