Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/291.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用WPF数据绑定设计响应性UI_C#_Wpf_Multithreading_Data Binding - Fatal编程技术网

C# 使用WPF数据绑定设计响应性UI

C# 使用WPF数据绑定设计响应性UI,c#,wpf,multithreading,data-binding,C#,Wpf,Multithreading,Data Binding,我是WPF的新手。我试图理解使用WPF绑定的MVVM模式。我有两门以下的课 MainWindow.xamal 视图模型 我有三个控件 显示ViewModel的“名称”属性的文本框 显示ViewModel的“状态”依赖属性的文本框 调用“ViewModel”类的“Execute”方法的按钮 现在,Execute()方法有点笨重,所以我创建了一个委托并异步调用它。但我的UI仍然被阻塞,并且它没有更新“Status”依赖属性的值 请参阅下面的课程 App.xaml.cs namespace bindi

我是WPF的新手。我试图理解使用WPF绑定的MVVM模式。我有两门以下的课

  • MainWindow.xamal
  • 视图模型

    我有三个控件

  • 显示ViewModel的“名称”属性的文本框
  • 显示ViewModel的“状态”依赖属性的文本框
  • 调用“ViewModel”类的“Execute”方法的按钮
  • 现在,Execute()方法有点笨重,所以我创建了一个委托并异步调用它。但我的UI仍然被阻塞,并且它没有更新“Status”依赖属性的值

    请参阅下面的课程

    App.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();
            }
        }
    }
    
    有人能帮我吗

    问候,,
    Hemant

    视图模型通常不包含依赖属性。为了能够通过数据绑定更新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));
        }
    
        // ...
    }
    
    公共类视图模型:INotifyPropertyChanged
    {
    私有字符串_状态;
    公共字符串状态
    {
    获取{return\u status;}
    设置
    {
    如果(_status==值)
    返回;
    _状态=价值;
    不动产变更(“状态”);
    }
    }
    公共事件EventHandler属性已更改;
    私有void OnPropertyChanged(字符串propertyName)
    {
    var handler=PropertyChanged;
    if(处理程序!=null)
    处理程序(新PropertyChangedEventArgs(propertyName));
    }
    // ...
    }
    
    在视图模型上实现
    ICommand
    似乎也很奇怪。

    这里有几件事:

  • dependencProperty
    用于。。。嗯,有依赖属性的类。对于视图模型,实现INotifyPropertyChanged
    DependencyObject
    现在将您的继承绑定起来,而这不是它的预期用途

  • 您正在调用
    调度程序
    上的操作,
    调度程序
    应用于在调度程序线程上运行函数,在本例中,该线程将是UI线程。难怪它会被阻止,因为您正在UI线程上调用一个方法<如果要从后台任务更改UI绑定值(例如,报告某种进度),则代码>调度程序非常有用。你必须分离你的逻辑,在后台进行处理,然后报告结果

  • 也就是说,您的
    Execute
    应该是这样的(使用C#5):

    对于C#4(未经测试):


    首先想到的是:NotifyPropertyChanged,仔细看看,这会告诉UI背景中的某些内容发生了变化。@djerry根据我的理解DependencyProperty也能够通知UI背景中的某些内容发生了变化。事实上,我可以在UI上看到最后一个状态,即“Data saved.”,但因为它似乎是对UI线程的阻塞调用,所以它不会更新/反映其他状态消息。如果我错了,请纠正我…确保您的
    PropertyChanged
    通知在UI线程上运行。尝试在UI线程上分配状态。@Mathew如何在ViewModel中提取UI线程的实例?在winform中,我们用来做一些事情,比如control.InvokeRequired==true,然后是control.BeginInvoke(…),但这里我所有的数据都在ViewModel中。那么,我该如何应对这种情况呢?
        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;
        }
    }
    
    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));
        }
    
        // ...
    }
    
    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";
    }
    
    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.
    }