【发布时间】:2020-05-01 04:58:39
【问题描述】:
我正在使用 WPF 将我以前的玩具项目之一重写为 MVVM 结构。但是绑定不起作用。
我的项目将 .xef 文件转换为 .mat 文件。为了遵循 MVVM 结构,首先我创建了一个 xef2matcore 类来执行业务逻辑,并提供了几个事件(例如 FileLoaded、ConversionProgressUpdated)。然后我创建了一个 ViewModel 类,其中存储了将绑定到 UI 的属性(例如 Progress、IsButtionEnabled)。 ViewModel 实现了 INotifyPropertyChanged 接口。最后将属性绑定到 UI 元素(例如 progressbar.Value、buttion.IsEnabled)。
问题来了: 如果通过调用 ViewModel 中定义的方法直接更改绑定属性,则绑定工作正常。但是如果通过事件处理方法改变了绑定Property(最终调用了ViewModel中定义的相同方法),那么绑定将不起作用(即使绑定Property值已更改,UI也不会更新)。
代码解释如下:
public class ViewModel: ObservableObject
{
private double _progress;
public double Progress
{
get => _progress;
set => Set(ref _progress, value);
}
private string _fileName;
public string FileName
{
get => _fileName;
set => Set(ref _fileName, value);
}
private bool _isButtonEnabled;
public bool IsButtonEnabled
{
get => _isButtonEnabled;
set => Set(ref _isButtonEnabled, value);
}
private bool _isBusy;
public bool IsBusy
{
get => _isBusy;
set => Set(ref _isBusy, value);
}
Xef2MatCore XCore;
public ViewModel()
{
Progress = 0;
FileName = "";
IsButtonEnabled = true;
}
private void XCore_FileLoaded()
{
IsButtonEnabled = false;
}
private void XCore_ProgressUpdated(double progress)
{
Progress = progress;
}
private void XCore_ExportFinished()
{
IsButtonEnabled = true;
Progress = 50;
}
public IAsyncCommand SelectFileCommandAsync { get => new AsyncCommand(UpdateFileSelectionExecuteAsync, CanFileSelectionExecute); }
private bool CanFileSelectionExecute() => !IsBusy;
private async Task UpdateFileSelectionExecuteAsync()
{
var folder_path = Environment.CurrentDirectory;
if (!Directory.Exists(folder_path)) Directory.CreateDirectory(folder_path);
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.InitialDirectory = Environment.CurrentDirectory;
openFileDialog.Filter = "Kinect Studio Data File|*.xef";
openFileDialog.RestoreDirectory = true;
openFileDialog.FilterIndex = 1;
var result = openFileDialog.ShowDialog();
if (result.HasValue && result.Value)
{
FileName = openFileDialog.FileName;
IsBusy = true;
XCore = new Xef2MatCore();
XCore.FileLoaded += XCore_FileLoaded;
XCore.ProgressUpdated += XCore_ProgressUpdated;
XCore.ExportFinished += XCore_ExportFinished;
//await Do_work();
await XCore.LoadAsync(FileName);
IsBusy = false;
}
else
{
return;
}
}
private async Task Do_work()
{
XCore_FileLoaded();
await Task.Run(() =>
{
int i = 0;
while (i < 100)
{
XCore_ProgressUpdated(i++);
Thread.Sleep(100);
}
});
XCore_ExportFinished();
}
}
如果我取消注释 await Do_work(); 绑定有效,但如果我调用 await XCore.LoadAsync(FileName);(通过事件处理程序方法触发 XCore_FileLoaded、XCore_ProgressUpdated 和 XCore_ExportFinished)则绑定无效。
XAML 文件:
<Window x:Class="Xef2MatUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Xef2MatUI"
mc:Ignorable="d"
Title="Xef2Mat Converter" >
<Window.DataContext>
<local:ViewModel x:Name="_viewmodel"/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0"
x:Name="label1"
Content="Select Kinect Studio (.xef) file:"
HorizontalAlignment="Left" VerticalAlignment="Center"/>
<Button Grid.Column="0" Grid.Row="1"
x:Name="button"
Content="Select"
IsEnabled="{Binding IsButtonEnabled, Mode=OneWay}"
HorizontalAlignment="Left" VerticalAlignment="Center"
Command="{Binding SelectFileCommand}"
/>
<Button Grid.Column="1" Grid.Row="1"
Content="Select"
/>
<Label Grid.Column="0" Grid.Row="2"
x:Name="label2" Content="Progress:"
HorizontalAlignment="Left" VerticalAlignment="Center"/>
<ProgressBar Grid.Column="0" Grid.Row="3"
x:Name="progressBar"
Value="{Binding Progress, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
<Label Grid.Column="1" Grid.Row="3"
x:Name="label3" Content="{Binding Progress, Mode=OneWay, StringFormat={}{0}%}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>
感谢任何帮助。
【问题讨论】:
-
也许
XCore.LoadAsync并不是真正的异步 fx{Thread.Sleep(10000); return Task.FromResult(1);} -
感谢您的评论,塞尔文。但我没有明白你的意思
{Thread.Sleep(10000); return Task.FromResult(1);}。我应该试试这条线吗? -
这只是一个示例,它不允许执行绑定,因为即使您使用
await,主线程也会被阻止......正如我所写的那样 也许 XCore.LoadAsync 并不是真正的异步我> -
你指的是什么绑定?您的示例中没有。
-
嗨,mm8,我已修改问题以包含部分 xaml 文件供您参考。
标签: c# wpf mvvm data-binding inotifypropertychanged