【问题标题】:WPF Binding not working when property is updated by a event handler事件处理程序更新属性时 WPF 绑定不起作用
【发布时间】: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_FileLoadedXCore_ProgressUpdatedXCore_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


【解决方案1】:

正如Selvin 所说,当我调用await XCore.LoadAsync(FileName); 时,耗时任务阻塞了OnEventHappened 功能。然后将它们分开并将耗时的任务放到一个新线程中解决了问题。

【讨论】:

    猜你喜欢
    • 2019-09-08
    • 1970-01-01
    • 1970-01-01
    • 2016-11-12
    • 1970-01-01
    • 1970-01-01
    • 2015-01-22
    • 1970-01-01
    • 2017-02-20
    相关资源
    最近更新 更多