首先(完全披露),我是C#的相对初学者,所以这可能是一个明显的哎呀。我已经在stackoverflow上回顾了几十个类似的问题和示例,我仍然无法将我的发现者放在我做错的事情上。我开始使用VS 2017 WPFapp项目。更改了OnStartup,因此我可以手动加载我的窗口。创建一个带有计时器的类,该计时器递增一个值,然后使用INotifyPropertyChanged更新MainWindow上的TextBox。
WPF窗口加载正常。 TextBlock以值5开头,因此绑定至少指向我的类和值。您将在输出中注意该值正在使用timer事件进行更新,并且NotifyPropertyChanged正在触发,但TextBlock永远不会更改。
我唯一的想法是TextBlock链接到我的UpdateWPF类的错误实例,但我不知道它是怎么回事。
提前感谢您的帮助!
这是我的代码......
MainWindow.xaml:(真的只是丢弃了一个文本块并设置了绑定)
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBlock HorizontalAlignment="Left" Height="50" Margin="139,123,0,0" TextWrapping="Wrap" Text="{Binding Path=GetValue,Mode=OneTime,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="215"/>
</Grid>
</Window>
MainWindow.xaml.cs (根本没有更改此代码)
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
App.xaml (刚刚注释掉了StartupUri)
<Application x:Class="WpfApp1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1">
<!--StartupUri="MainWindow.xaml"-->
<Application.Resources>
</Application.Resources>
</Application>
App.xaml.cs (肉和土豆)
namespace WpfApp1
{
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
UpdateWPF uWPF = new UpdateWPF();
MainWindow w = new MainWindow();
w.DataContext = uWPF;
w.Show();
}
public class UpdateWPF : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
Console.WriteLine("NotifyPropertyChanged Fired.");
}
private int value = 5;
public UpdateWPF()
{
Timer aTimer = new System.Timers.Timer();
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Interval = 1000;
aTimer.Enabled = true;
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
value++;
Console.WriteLine("Timer Event Fired. New Value is " + value.ToString());
NotifyPropertyChanged(nameof(GetValue));
}
public string GetValue => (value.ToString());
}
}
}
和输出
Timer Event Fired. New Value is 6
NotifyPropertyChanged Fired.
Timer Event Fired. New Value is 7
NotifyPropertyChanged Fired.
Timer Event Fired. New Value is 8
NotifyPropertyChanged Fired.
Timer Event Fired. New Value is 9
NotifyPropertyChanged Fired.
Timer Event Fired. New Value is 10
NotifyPropertyChanged Fired.
Timer Event Fired. New Value is 11
NotifyPropertyChanged Fired.
答案 0 :(得分:0)
正如上面的评论中所说,您必须正确设置UpdateSourceTrigger
。我也不建议您实施属性的方式。他们应该是这样的:
private int _value;
public int Value
{
get => _value;
set
{
_value = value;
OnPropertyChanged(nameof(Value));
}
}
现在您可以说Value = 0;
而不是手动提升PropertyChanged
事件。
如果您将NotifyPropertyChanged
方法更改为:
private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgw(propertyName));
Console.WriteLine("NotifyPropertyChanged Fired.");
}
您不必将属性名称传递给NotifyPropertyChanged
。由于CallerMemberNameAttribute
。
问候
洛里斯
修改强>
我的属性示例只是一个属性的示例,而不是您的。但你只需改变类型就可以了。
这应该适合你:
public class UpdateWPF : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
Console.WriteLine("NotifyPropertyChanged Fired.");
}
private int _value = 5;
public UpdateWPF()
{
Timer aTimer = new System.Timers.Timer();
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Interval = 1000;
aTimer.Enabled = true;
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
var i = Convert.ToInt32(value);
Value = ++i;
}
public string Value
{
get => _value;
set
{
_value = value;
NotifyPropertyChanged();
}
}
}
答案 1 :(得分:0)
ASh发现了我的问题,但他发布了评论,所以我会回答这个问题。由于我在此过程中解决了其他一些问题,因此我将其全部发布在此处。
真正的问题出在我的MainWindow.xaml
中Text="{Binding Path=GetValue,Mode=OneTime,UpdateSourceTrigger=PropertyChanged}"
应该是
Text="{Binding Path=GetValue,Mode=OneWay}"
稍后将成为
Text="{Binding Path=Value,Mode=OneWay}"
正如ASh所指出的,在这种情况下,UpdateSourceTrigger完全没有意义。
Loris156指出了其他一些问题。一些建议是错误的,但我偶然发现了好处并且来到了这一点。
命名惯例
我来自java,一切都是GetThis或SetThis,这个是变量。
MS建议Value(){get; set;},其中 value 是变量。 loris156更喜欢 _value ,我看到很多人在做。因为它恰好发生了#34;价值&#34;是一个非常糟糕的例子,因为在auto属性setter中使用。尽管如此,由于我使用他们的编译器,我坚持使用MS的方式。
所以我的getter / setter变成了#34; Value&#34;。
我还要指出Lori156使用了int返回,但这对我正在使用的文本框没有用。
我之前无法让[CallerMemberName]正常工作。这也与我的getter / setter有关。通过排除setter,我破坏了自动属性功能。见understanding private setters
正如我之前提到的,int getter不能使用文本框,因此我选择将字符串转换回setter中的int。这无关紧要,因为我还是不会使用它。 以下是App.xaml.cs的代码更改
private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
Console.WriteLine("NotifyPropertyChanged Fired.");
}
...
public string Value {
get => value.ToString();
private set => this.value = Int32.Parse(value);
}
如果有人有这些建议,我愿意接受进一步的建议。