计算字段在 UI 中编辑之前不会更新
Calculated field not updating until edited in UI
作为新手程序员,我正在尝试使用 XAML 和 C# 测试数据绑定。我有两个绑定到属性的滑块,我想用滑块设置的两个属性值的总和来更新 TextBox。
我正在使用 INotifyPropertyChanged
并尝试更改我能找到的每个 属性 但我无法让文本框更新,直到我编辑文本框,此时,文本框更新为正确的值。使用 UpdateSourceTrigger=PropertyChanged
只会在我编辑文本框时更新文本框,而不是在我 select 另一个元素时更新文本框。我试过编写一个单独的事件处理程序,它不使用 [CallerNameMember]
并使用指定的 属性 但它似乎没有改变任何东西。
<Grid>
<Grid.RowDefinitions>
</Grid.RowDefinitions>
<TextBox Grid.Row="0"
Text="{Binding BoundNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
FontSize="20"
FontWeight="Bold"
AllowDrop="False" />
<Slider Grid.Row="1"
Value="{Binding BoundNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Maximum="100"
Minimum="10"
IsSnapToTickEnabled="True"
TickFrequency="10" />
<TextBox Grid.Row="2"
Text="{Binding BoundNumber2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
AllowDrop="False" />
<Slider Grid.Row="3"
Value="{Binding BoundNumber2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Maximum="100"
Minimum="10"
IsSnapToTickEnabled="True"
TickFrequency="10" />
<TextBox Grid.Row="4"
Name="MathBox"
Text="{Binding QuickMath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}">
</TextBox>
</Grid>
public partial class OrderScreen : INotifyPropertyChanged
{
public OrderScreen()
{
DataContext = this;
InitializeComponent();
}
private int quickMath;
public int QuickMath
{
get { return _boundNumber + _boundNumber2; }
set
{
if (value != quickMath)
{
quickMath = value;
OnPropertyChanged();
}
}
}
private int _boundNumber;
public int BoundNumber
{
get { return _boundNumber; }
set
{
if (_boundNumber != value)
{
_boundNumber = value;
// MathBox.Text = quickMath.ToString();
OnPropertyChanged();
}
}
}
private int _boundNumber2;
public int BoundNumber2
{
get { return _boundNumber2; }
set
{
if (_boundNumber2 != value)
{
_boundNumber2 = value;
MathBox.Text = quickMath.ToString();
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
我可以让它与注释掉的 MathBox.Text = quickMath.ToString();
一起工作,但我希望有更好的方法来使用数据绑定。感谢期待!
您尚未在任何地方初始化 PropertyChanged
事件,因此永远不会调用它。像这样声明和初始化它:
public event PropertyChangedEventHandler PropertyChanged = delegate { };
绑定机制订阅了DataSource
对象的PropertyChanged
事件,所以不需要"initialize"事件连同INPC
实现,但是你可能已经注意到,QuickMath
属性 的 PropertyChanged
事件确实不会在 BoundNumber
或 BoundNumber2
更改时触发。
您可以通过不同的方式修复它,例如为所有受影响的属性显式调用 OnPropertyChanged:
private int _boundNumber;
public int BoundNumber
{
get { return _boundNumber; }
set
{
if (_boundNumber != value)
{
_boundNumber = value;
OnPropertyChanged();
OnPropertyChanged(nameof(QuickMath));
}
}
}
请注意,这样您可以保留 QuickMath
属性 个 read-only。这种方法在其他情况下效果很好,例如使用 time-related 属性,假设您的数据源 属性 格式化字符串,如 "Edited 2 minutes ago" 基于记录时间戳和当前时间,你调用PropertyChanged
作为定时任务。
public int QuickMath => _boundNumber + _boundNumber2;
或者,您可以在修改 BoundNumber
和 BoundNumber2
的同时更新 QuickMath
以触发 QuickMath
setter 内部的 OnPropertyChanged()
调用:
private int _boundNumber2;
public int BoundNumber2
{
get { return _boundNumber2; }
set
{
if (_boundNumber2 != value)
{
_boundNumber2 = value;
OnPropertyChanged();
QuickMath = BoundNumber + BoundNumber2;
}
}
}
如果 QuickMath
中的逻辑不允许将其变为 read-only 属性,这就有意义了。在这种情况下,您必须相应地调整 getter 并在那里使用 private 或 protected setter 以避免数据不一致和意外行为。
private int _quickMath;
public int QuickMath
{
get { return _quickMath; }
private set
{
if (value != _quickMath)
{
_quickMath = value;
OnPropertyChanged();
}
}
}
在这两种情况下都不需要 two-way 绑定到 QuickMath
:
<TextBlock Grid.Row="4" Text="{Binding QuickMath, Mode=OneWay}"/>
在 side-note 上并查看其余代码,真正值得一提的是绑定机制有望将 UI 从数据中分离出来,其中 XAML 知道数据源对象属性(名称和类型)而不是关于它的内部实现,而数据源对象可能根本不知道 XAML。所以
- 不应从数据对象调用
FrameworkElement
,如 MathBox.Text
- 将数据对象 class 与页面或控件 class 完全分开被认为是一个很好的设计。
希望这对您有所帮助。
作为新手程序员,我正在尝试使用 XAML 和 C# 测试数据绑定。我有两个绑定到属性的滑块,我想用滑块设置的两个属性值的总和来更新 TextBox。
我正在使用 INotifyPropertyChanged
并尝试更改我能找到的每个 属性 但我无法让文本框更新,直到我编辑文本框,此时,文本框更新为正确的值。使用 UpdateSourceTrigger=PropertyChanged
只会在我编辑文本框时更新文本框,而不是在我 select 另一个元素时更新文本框。我试过编写一个单独的事件处理程序,它不使用 [CallerNameMember]
并使用指定的 属性 但它似乎没有改变任何东西。
<Grid>
<Grid.RowDefinitions>
</Grid.RowDefinitions>
<TextBox Grid.Row="0"
Text="{Binding BoundNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
FontSize="20"
FontWeight="Bold"
AllowDrop="False" />
<Slider Grid.Row="1"
Value="{Binding BoundNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Maximum="100"
Minimum="10"
IsSnapToTickEnabled="True"
TickFrequency="10" />
<TextBox Grid.Row="2"
Text="{Binding BoundNumber2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
AllowDrop="False" />
<Slider Grid.Row="3"
Value="{Binding BoundNumber2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Maximum="100"
Minimum="10"
IsSnapToTickEnabled="True"
TickFrequency="10" />
<TextBox Grid.Row="4"
Name="MathBox"
Text="{Binding QuickMath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}">
</TextBox>
</Grid>
public partial class OrderScreen : INotifyPropertyChanged
{
public OrderScreen()
{
DataContext = this;
InitializeComponent();
}
private int quickMath;
public int QuickMath
{
get { return _boundNumber + _boundNumber2; }
set
{
if (value != quickMath)
{
quickMath = value;
OnPropertyChanged();
}
}
}
private int _boundNumber;
public int BoundNumber
{
get { return _boundNumber; }
set
{
if (_boundNumber != value)
{
_boundNumber = value;
// MathBox.Text = quickMath.ToString();
OnPropertyChanged();
}
}
}
private int _boundNumber2;
public int BoundNumber2
{
get { return _boundNumber2; }
set
{
if (_boundNumber2 != value)
{
_boundNumber2 = value;
MathBox.Text = quickMath.ToString();
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
我可以让它与注释掉的 MathBox.Text = quickMath.ToString();
一起工作,但我希望有更好的方法来使用数据绑定。感谢期待!
您尚未在任何地方初始化 PropertyChanged
事件,因此永远不会调用它。像这样声明和初始化它:
public event PropertyChangedEventHandler PropertyChanged = delegate { };
绑定机制订阅了DataSource
对象的PropertyChanged
事件,所以不需要"initialize"事件连同INPC
实现,但是你可能已经注意到,QuickMath
属性 的 PropertyChanged
事件确实不会在 BoundNumber
或 BoundNumber2
更改时触发。
您可以通过不同的方式修复它,例如为所有受影响的属性显式调用 OnPropertyChanged:
private int _boundNumber;
public int BoundNumber
{
get { return _boundNumber; }
set
{
if (_boundNumber != value)
{
_boundNumber = value;
OnPropertyChanged();
OnPropertyChanged(nameof(QuickMath));
}
}
}
请注意,这样您可以保留 QuickMath
属性 个 read-only。这种方法在其他情况下效果很好,例如使用 time-related 属性,假设您的数据源 属性 格式化字符串,如 "Edited 2 minutes ago" 基于记录时间戳和当前时间,你调用PropertyChanged
作为定时任务。
public int QuickMath => _boundNumber + _boundNumber2;
或者,您可以在修改 BoundNumber
和 BoundNumber2
的同时更新 QuickMath
以触发 QuickMath
setter 内部的 OnPropertyChanged()
调用:
private int _boundNumber2;
public int BoundNumber2
{
get { return _boundNumber2; }
set
{
if (_boundNumber2 != value)
{
_boundNumber2 = value;
OnPropertyChanged();
QuickMath = BoundNumber + BoundNumber2;
}
}
}
如果 QuickMath
中的逻辑不允许将其变为 read-only 属性,这就有意义了。在这种情况下,您必须相应地调整 getter 并在那里使用 private 或 protected setter 以避免数据不一致和意外行为。
private int _quickMath;
public int QuickMath
{
get { return _quickMath; }
private set
{
if (value != _quickMath)
{
_quickMath = value;
OnPropertyChanged();
}
}
}
在这两种情况下都不需要 two-way 绑定到 QuickMath
:
<TextBlock Grid.Row="4" Text="{Binding QuickMath, Mode=OneWay}"/>
在 side-note 上并查看其余代码,真正值得一提的是绑定机制有望将 UI 从数据中分离出来,其中 XAML 知道数据源对象属性(名称和类型)而不是关于它的内部实现,而数据源对象可能根本不知道 XAML。所以
- 不应从数据对象调用
FrameworkElement
,如MathBox.Text
- 将数据对象 class 与页面或控件 class 完全分开被认为是一个很好的设计。
希望这对您有所帮助。