C# 使用WPF数据绑定设计响应性UI
我是WPF的新手。我试图理解使用WPF绑定的MVVM模式。我有两门以下的课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
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
用于。。。嗯,有依赖属性的类。对于视图模型,实现INotifyPropertyChangedDependencyObject
现在将您的继承绑定起来,而这不是它的预期用途调度程序
上的操作,调度程序
应用于在调度程序线程上运行函数,在本例中,该线程将是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.
}