使用WPF数据绑定设计响应式UI
本文关键字:响应 UI WPF 数据绑定 使用 | 更新日期: 2023-09-27 18:08:28
我是WPF新手。我试图理解MVVM模式使用WPF绑定。我有以下两个类
- 主窗口。xamal
-
ViewModel
我有三个控件
- 显示ViewModel的Name属性的文本框
- 文本框,显示ViewModel的'Status'依赖属性
- 调用'ViewModel'类的' execute '方法的按钮。
现在,Execute()方法有点笨重,所以我创建了一个委托并异步调用它。但是我的UI仍然是阻塞的,它没有更新'状态'依赖属性的值
参考下面的类。
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();
}
}
}
MainWindow.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>
ViewModel.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;
}
}
有人能帮我一下吗?问候,赫曼特
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
似乎也很奇怪。
这里有几点:
-
DependencyProperty
为…有依赖属性的类。对于视图模型,实现INotifyPropertyChanged
。DependencyObject
现在绑定了你的继承,这不是它的预期用途。 -
您正在调用
Dispatcher
上的操作,并且Dispatcher
应该用于在调度线程上运行函数,在本例中该线程将是UI线程。难怪它被阻塞了,你在一个UI线程上调用一个方法。如果你想从后台任务更改ui绑定值(例如报告某种进度),Dispatcher
是有用的。你必须分离你的逻辑,在后台做处理,然后报告结果。
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.
}