简单的数据绑定不起作用

发布于 2024-10-13 21:19:42 字数 3890 浏览 1 评论 0原文

我是 WPF 新手,我正在尝试制作一个简单的应用程序,一个秒表。如果我不进行数据绑定,它工作得很好。这是我的 XAML。

<Window x:Class="StopWatch.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="clr-namespace:StopWatch"
    Title="MainWindow" Height="318" Width="233">
<Window.Resources>
    <s:StopWatchViewModel x:Key="swViewModel" x:Name="swViewModel"></s:StopWatchViewModel>
</Window.Resources>
<Grid DataContext="{StaticResource swViewModel}"> 
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="128*" />
    </Grid.RowDefinitions>
    <TextBlock Grid.Row="1" Height="49" HorizontalAlignment="Left" Margin="42,50,0,0" Name="txtTime" Text="{Binding Path=Message}" VerticalAlignment="Top" Width="147" FontSize="20" TextAlignment="Center" />
    <Button Content="Start" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="12,15,0,0" Name="startBtn" VerticalAlignment="Top" Width="58" Click="startBtn_Click" />
    <Button Content="Stop" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="76,15,0,0" Name="stopBtn" VerticalAlignment="Top" Width="58" Click="stopBtn_Click" />
    <Button Content="Reset" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="140,15,0,0" Name="resetBtn" VerticalAlignment="Top" Width="59"/>
</Grid>

这是 MainWindow 中的代码

public partial class MainWindow : Window
{
    private StopWatchViewModel stopwatch;

    public MainWindow()
    {
        InitializeComponent();
        stopwatch = new StopWatchViewModel();
    }

    private void startBtn_Click(object sender, RoutedEventArgs e)
    {
        stopwatch.Start();
    }

    private void stopBtn_Click(object sender, RoutedEventArgs e)
    {
        stopwatch.Stop();
    }
}

,这是 StopWatchViewModel.cs 中的代码,

class StopWatchViewModel : INotifyPropertyChanged
{
    private DispatcherTimer timer;
    private Stopwatch stopwatch;
    private string message;

    public event PropertyChangedEventHandler PropertyChanged;

    public string Message
    {
        get
        {
            return message;
        }
        set
        {
            if (message != value)
            {
                message = value;
                OnPropertyChanged("Message");
            }
        }
    }

    public StopWatchViewModel()
    {
        timer = new DispatcherTimer();
        stopwatch = new Stopwatch();
        timer.Tick += new EventHandler(timer_Tick);
        timer.Start();
        stopwatch.Reset();
    }

    public void Start()
    {
        stopwatch.Start();
    }

    public void Stop()
    {
        stopwatch.Stop();
    }

    private void timer_Tick(object sender, EventArgs e)
    {
        Message = stopwatch.Elapsed.ToString(); // Doesn't work.
        // Message = "hello"; does not work too!
    }

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

}

我不知道哪里出错了。

编辑:我成功了。所以这是供任何人参考的工作代码。

XAML,将原来的更改为此

<Window x:Class="StopWatch.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:StopWatch"
Title="MainWindow" Height="318" Width="233">
<Grid> // partial code

,并在后面的代码中,根据Erno的建议更改构造函数。

public MainWindow()
    {
        InitializeComponent();
        viewModel = new StopWatchViewModel();
        this.DataContext = viewModel;
    }

谢谢你们!

I'm new to WPF and I'm trying to make a simple app, a stopwatch. It works fine if I'm not doing the data binding. Here's my XAML.

<Window x:Class="StopWatch.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="clr-namespace:StopWatch"
    Title="MainWindow" Height="318" Width="233">
<Window.Resources>
    <s:StopWatchViewModel x:Key="swViewModel" x:Name="swViewModel"></s:StopWatchViewModel>
</Window.Resources>
<Grid DataContext="{StaticResource swViewModel}"> 
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="128*" />
    </Grid.RowDefinitions>
    <TextBlock Grid.Row="1" Height="49" HorizontalAlignment="Left" Margin="42,50,0,0" Name="txtTime" Text="{Binding Path=Message}" VerticalAlignment="Top" Width="147" FontSize="20" TextAlignment="Center" />
    <Button Content="Start" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="12,15,0,0" Name="startBtn" VerticalAlignment="Top" Width="58" Click="startBtn_Click" />
    <Button Content="Stop" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="76,15,0,0" Name="stopBtn" VerticalAlignment="Top" Width="58" Click="stopBtn_Click" />
    <Button Content="Reset" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="140,15,0,0" Name="resetBtn" VerticalAlignment="Top" Width="59"/>
</Grid>

and here is the code in MainWindow

public partial class MainWindow : Window
{
    private StopWatchViewModel stopwatch;

    public MainWindow()
    {
        InitializeComponent();
        stopwatch = new StopWatchViewModel();
    }

    private void startBtn_Click(object sender, RoutedEventArgs e)
    {
        stopwatch.Start();
    }

    private void stopBtn_Click(object sender, RoutedEventArgs e)
    {
        stopwatch.Stop();
    }
}

and here's the code in StopWatchViewModel.cs

class StopWatchViewModel : INotifyPropertyChanged
{
    private DispatcherTimer timer;
    private Stopwatch stopwatch;
    private string message;

    public event PropertyChangedEventHandler PropertyChanged;

    public string Message
    {
        get
        {
            return message;
        }
        set
        {
            if (message != value)
            {
                message = value;
                OnPropertyChanged("Message");
            }
        }
    }

    public StopWatchViewModel()
    {
        timer = new DispatcherTimer();
        stopwatch = new Stopwatch();
        timer.Tick += new EventHandler(timer_Tick);
        timer.Start();
        stopwatch.Reset();
    }

    public void Start()
    {
        stopwatch.Start();
    }

    public void Stop()
    {
        stopwatch.Stop();
    }

    private void timer_Tick(object sender, EventArgs e)
    {
        Message = stopwatch.Elapsed.ToString(); // Doesn't work.
        // Message = "hello"; does not work too!
    }

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

}

I don't know where I got it wrong.

EDIT: I got it working. So here's the working code for anyone's reference.

XAML, change the original to this

<Window x:Class="StopWatch.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:StopWatch"
Title="MainWindow" Height="318" Width="233">
<Grid> // partial code

and in behind code, change the constructor based on Erno's suggestion.

public MainWindow()
    {
        InitializeComponent();
        viewModel = new StopWatchViewModel();
        this.DataContext = viewModel;
    }

Thanks guys!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

玩心态 2024-10-20 21:19:43

这里的问题是您没有任何机制让 WPF 知道您的属性已更新。基本上,您在这里有两个选择:

  1. 将消息设为依赖属性。
  2. 实现 INotifyPropertyChanged 以便 GUI 知道消息何时更新。

为了确保您拥有使 INotifyPropertyChanged 正常工作的所有部分,请检查您是否已完成所有这些操作:

  1. 定义事件 PropertyChanged
  2. 创建一个私有 NotifyPropertyChanged 方法来引发该事件。此方法应采用字符串参数(属性名称)并引发如下事件:PropertyChanged(this, new PropertyChangedEventArgs()。使用此方法的原因是将 。
  3. 在属性设置器中,在值更改后使用属性的正确名称(区分大小写)调用 NotifyPropertyChanged

Your problem here is that you don't have any mechanism for letting WPF know that your property is updated. Basically you have two options here:

  1. Make Message into a Dependancy Property.
  2. Implement INotifyPropertyChanged so that it let's the GUI know when the message have been updated.

To make sure that you have all parts for getting INotifyPropertyChanged to work, check that you did all of this:

  1. Define the event PropertyChanged.
  2. Make a private NotifyPropertyChanged method to raise the event. This method should take a string parameter (name of the property) and raise the event like this : PropertyChanged(this, new PropertyChangedEventArgs(<nameofproperty>). The reason to make this method is to put the null check and invocation details in one place.
  3. In the property setter, call NotifyPropertyChanged with the correct name (case-sensitive) of the property after the value has changed.
红玫瑰 2024-10-20 21:19:43

只需像这样替换您的 Message 属性,它将起作用:

 public string Message
 {
     get { return (string)GetValue(MessageProperty); }
     set { SetValue(MessageProperty, value); }
 }

 public static readonly DependencyProperty MessageProperty =
     DependencyProperty.Register("Message", typeof(string), 
     typeof(MainWindow), new UIPropertyMetadata(String.Empty));

编辑 在发布我的解决方案后,我注意到您将所有代码更改为 ViewModel 解决方案...请随意忽略或返回到您的第一组代码。

在新代码中,您将创建 ViewModel 的两个实例,一个在代码中,一个在资源中。这不好,因为您正在操作代码中的内容并绑定到资源(xaml)中的内容

编辑:
将您的构造函数更改为:

public MainWindow()
{
    InitializeComponent();
    stopwatch = new StopWatchViewModel();
    this.DataContext = stopwatch;
}

仅此而已

Just replace your Message property like this and it will work:

 public string Message
 {
     get { return (string)GetValue(MessageProperty); }
     set { SetValue(MessageProperty, value); }
 }

 public static readonly DependencyProperty MessageProperty =
     DependencyProperty.Register("Message", typeof(string), 
     typeof(MainWindow), new UIPropertyMetadata(String.Empty));

EDIT after posting my solution I noticed you changed all the code into a ViewModel solution... feel free to ignore or go back to your first set of code.

In your new code you are creating TWO instances of the ViewModel, one in code and one in the resources. That is not good as you are manipulating the one in code and binding to the one in the resources(xaml)

EDIT:
change your constructor to this:

public MainWindow()
{
    InitializeComponent();
    stopwatch = new StopWatchViewModel();
    this.DataContext = stopwatch;
}

That's all

古镇旧梦 2024-10-20 21:19:43

如果您没有正确实现 INotifyPropertyChanged,则不会注意到对属性的更改。所以你应该先这样做。也许你之前做的时候漏掉了一些东西。

Changes to the property won't get noticed if you don't implement INotifyPropertyChanged correctly. So you should do that first. Maybe you missed something when you did it earlier.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文