WPF MVVM 混淆

3

我最近几天一直在研究MVVM,想尝试一个简单的例子,用时间更新文本框。然而,我对此有些困惑。我的解决方案中有两个项目。一个叫做TimeProvider,目前只返回Datetime事件;另一个叫做E。最终,我将使用TimeProvider提供更多信息,但我想先了解一些简单的东西。请问为什么我的GUI没有更新?

namespace E.TimeProvider
{
    public interface ITimeSource
    {
        void Subscribe();

        event Action<Time> TimeArrived;
    }
}
namespace E.TimeProvider
{
    public class Time
    {
        private DateTime _earthDate;

        public Time()
        {

        }

        public Time(DateTime earthDate)
        {
            this._earthDate = earthDate;
        }

        public DateTime EarthDate
        {
            get { return _earthDate; }
            set { _earthDate = value; }
        }
    }
}

namespace E.TimeProvider
{
    public class TimeSource : ITimeSource
    {
        private const int TIMER_INTERVAL = 50;

        public event Action<Time> TimeArrived;

        private bool subscribe;

        public TimeSource()
        {
            subscribe = false;
            Thread timeGenerator = new Thread(new ThreadStart(GenerateTimes));
            timeGenerator.IsBackground = true;
            timeGenerator.Priority = ThreadPriority.Normal;
            timeGenerator.Start();
        }

        public void Subscribe()
        {
            if (subscribe)
                return;

            subscribe = true;
        }

        private void GenerateTimes()
        {
            while (true)
            {
                GenerateAndPublishTimes();
                Thread.Sleep(TIMER_INTERVAL);
            }
        }

        private void GenerateAndPublishTimes()
        {
            DateTime earthDate = DateTime.Now;
            Time time = new Time(earthDate);
            TimeArrived(time);
        }
    }
}

那么我有我的项目E xaml

<Window x:Class="E.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="300" Width="200" xmlns:my="clr-namespace:Exiled" WindowStyle="None" WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
    <Grid>
        <my:TimeControl HorizontalAlignment="Left" x:Name="timeControl1" VerticalAlignment="Top" Height="300" Width="200" />
    </Grid>
</Window>

<UserControl x:Class="E.TimeControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="200" Background="Black" Foreground="White">
    <Grid>
        <TextBlock Height="41" HorizontalAlignment="Left" Margin="12,31,0,0"  Text="{Binding Path=EarthTime}"  VerticalAlignment="Top" Width="176" FontSize="35" TextAlignment="Center" />
    </Grid>
</UserControl>

以及其他的部分

namespace E
{
    public class TimeControlViewModel : DependencyObject
    {
        private readonly ITimeSource _source;


        public ObservableCollection<TimeViewModel> Times { get; set; }

        public TimeControlViewModel()
        {
            this.Times = new ObservableCollection<TimeViewModel>();
        }

        public TimeControlViewModel(ITimeSource source)
        {
            this.Times = new ObservableCollection<TimeViewModel>();

            _source = source;
            _source.TimeArrived += new Action<Time>(_source_TimeArrived); 
        }

        public void Subscribe()
        {
            _source.Subscribe();
        }

        void _source_TimeArrived(Time time)
        {
            TimeViewModel tvm = new TimeViewModel();
            tvm.Time = time;
        }   
    }
}

namespace E
{
    class SubscribeCommand
    {
        private readonly TimeControlViewModel _vm;

        public SubscribeCommand(TimeControlViewModel vm)
        {
            _vm = vm;
        }

        public void Execute(object parameter)
        {
            _vm.Subscribe();
        }
    }
}

namespace E
{
    public class TimeViewModel : DependencyObject
    {
        public TimeViewModel()
        {

        }

        public Time Time
        {
            set
            {
                this.EarthDate = value.EarthDate;
            }
        }

        public DateTime EarthDate
        {
            get { return (DateTime)GetValue(DateProperty); }
            set { SetValue(DateProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Date.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DateProperty =
            DependencyProperty.Register("EarthDate", typeof(DateTime), typeof(TimeViewModel), new UIPropertyMetadata(DateTime.Now));
    }
}
2个回答

2
你有一些问题:
  1. 你没有看到更新,因为你的窗口或用户控件的DataContext没有设置为你创建的TimeViewModel实例。
  2. 通常,ViewModel实例不应该是DependencyObjects。相反,应该实现INotifyPropertyChanged。这使它们不依赖于WPF,但仍然可以正确地与MVVM一起使用。
我建议阅读详细的MVVM介绍,例如我在此处写的介绍。特别是,你需要了解模板如何工作以及DataContext以正确地使用绑定。

谢谢您,先生。我发现您的一系列文章非常有启发性。 - poco
嗯,我认为ViewModels不应该扩展DependencyObject。但是,我认为您应该尽可能保持它们的轻巧,这意味着您想要重用的内容不应具有这些依赖项。 - user1228
@ReedCopsey:很少?我不同意。从KISS到DRY等有很多原则。我不认同VM的纯粹主义观点。没有人会在其他应用程序中重复使用ViewModels,所以为什么要假装呢? - user1228
@Will:即使在同一个应用程序中,DO也会增加很多额外的开销,并引入一些相当奇怪的用法。与使用INPC相比,扩展DO在您的VM中的目的是什么?我同意KISS-这就是为什么它对我来说似乎是一个边缘案例的原因... - Reed Copsey
@Will:我并不是在谈论性能开销,而是可维护性开销。但是无论怎样,只要有效就好了。我倾向于先做VM而不是View,这种情况下,将ViewModel作为DO似乎非常奇怪。 - Reed Copsey
显示剩余2条评论

0

首先,看起来您正在绑定地球时间:

Text="{Binding Path=EarthTime}"

但是属性本身被称为EarthDate

public DateTime EarthDate

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接