2

我是 WPF 的新手。我正在尝试使用 WPF 绑定来理解 MVVM 模式。我有以下 2 节课

  1. 主窗口.xamal
  2. 视图模型

    我有三个控件

    1. 显示 ViewModel 的“名称”属性的文本框
    2. 显示 ViewModel 的“状态”依赖属性的文本框
    3. 调用“ViewModel”类的“Execute”方法的按钮。

    现在,Execute() 方法有点笨重,所以我创建了一个委托并异步调用它。但我的 UI 仍然处于阻塞状态,并且它没有更新“状态”依赖属性的值

请参阅以下课程。

应用程序.xaml.cs

namespace bindingDemo
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            MainWindow mw = new MainWindow();
            ViewModel vm = new ViewModel();

            ///Set data context property of main windows.
            mw.DataContext = vm;
            mw.Show();
        }
    }
}

主窗口.xaml

<Window x:Class="bindingDemo.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>
    <TextBox Text="{Binding Name, Mode=TwoWay}"   Height="23" HorizontalAlignment="Left" Margin="76,26,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
    <Button Command="{Binding Path=MyCommand}" Content="Button" Height="23" HorizontalAlignment="Left" Margin="76,127,0,0" Name="button1" VerticalAlignment="Top" Width="120" />
    <TextBox Text="{Binding Path=Status}"  Height="23" HorizontalAlignment="Left" Margin="76,55,0,0" Name="textBox2" VerticalAlignment="Top" Width="120" />
</Grid>

视图模型.cs

    namespace bindingDemo
{
    public class ViewModel : DependencyObject , ICommand
    {
        public string Status
        {
            get { return (string)GetValue(StatusProperty); }
            set { SetValue(StatusProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Status.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StatusProperty =
            DependencyProperty.Register("Status", typeof(string), typeof(ViewModel), new UIPropertyMetadata("In Progress..."));       

        private ICommand _command = null;

        public ViewModel()
        {
            Name = "Default Name";
        }


        public void Execute(object parameter)
        {            
            Action a = new Action(() =>
            {
                ///While this code is being executed, UI gets blocked.
                Console.WriteLine(Name);
                Name = "OK";
                Status = "Connecting to database....";
                Thread.Sleep(2000);
                Status = "Connected to database....";
                Thread.Sleep(2000);
                Status = "Performing validations....";
                Thread.Sleep(2000);
                Status = "Data saved.";

            });

            /// Even if I have invoked operation asynchronously, UI is not getting updated
            /// UI is freezing for 6 seconds and can directly see last 'Status' message on UI
            Dispatcher.BeginInvoke(a, null);            
        }

        public string Name { get; set; }

        public ICommand MyCommand
        {
            get
            {
                return this;
            }
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;
    }
}

有人可以帮我吗?

问候,赫曼特

4

2 回答 2

4

ViewModel 通常不包含依赖属性。为了能够通过数据绑定更新 UI,它必须实现INotifyPropertyChanged接口。
尝试像这样实现您的 ViewModel:

public class ViewModel : INotifyPropertyChanged
{
    private string _status;

    public string Status
    {
        get { return _status; }
        set
        {
            if(_status == value)
                return;
            _status = value;

            OnPropertyChanged("Status");
        }
    }

    public event EventHandler<PropertyChangedEventArgs> PropertyChanged;

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

    // ...
}

在您的视图模型上实现ICommand似乎也很奇怪。

于 2013-04-10T06:54:35.117 回答
2

这里有几件事:

  1. DependencyProperty适用于...好吧,具有依赖属性的类。对于视图模型,实现INotifyPropertyChanged. DependencyObject现在绑定你的继承,这不是它的预期用途。

  2. 您正在调用 a 上的操作Dispatcher,并且Dispatcher应该用于在调度程序线程上运行函数,在这种情况下将是 UI 线程。难怪它会被阻塞,你在 UI 线程上调用一个方法。Dispatcher如果您想从后台任务更改 UI 绑定值(例如报告某种进度),这很有用。您必须分离逻辑,在后台进行处理,然后报告结果。

所以,话虽这么说,你Execute应该看起来像这样(使用 C# 5):

private async Task DoStuff()
{
     await Task.Delay(5000);
    //or drop the async modifier and 'return Task.Delay(5000);'
}

public async void Execute(object parameter)
{
    await DoStuff();
    //Add some checks if it really was 'OK', catch exceptions etc
    Name = "OK";
}

使用 C# 4(未经测试):

private Task DoStuff()
{
    return Task.Factory.StartNew(() => Thread.Sleep(5000));
}

public void Execute(object parameter)
{
   DoStuff().ContinueWith(result => Name = "OK", TaskScheduler.FromCurrentSynchronizationContext());
   //Same as above, probably should specify appropriate TaskOptions to run the continuation
   //only when the task was completed successfully.
}
于 2013-04-10T06:54:47.613 回答