异步更新WPF窗口

时间:2014-09-02 07:44:53

标签: wpf vb.net

我正在创建一个简单的WPF应用程序,当您单击按钮时,它会运行几个步骤,例如将文件复制到新位置,转换文件然后将新文件复制回原始位置。

这些步骤工作正常,但我想让WPF窗口更新到它所在的步骤,并在它运行时隐藏按钮。

只有在完成代码运行后,窗口才会更新。我认为我曾经能够使用me.refresh在经典表单上执行此操作,但这不适用于WPF。

在每个步骤完成后,我可以做些什么来更新窗口?

谢谢

Button1.Visibility = Windows.Visibility.Hidden

FileCopy("C:\Test.xsf", AppPath & "\Convert\test.xsf")
Image7.Visibility = Windows.Visibility.Hidden
Image3.Visibility = Windows.Visibility.Visible

Program.StartInfo.FileName = xDefs
Program.StartInfo.Arguments = "/q"
Program.Start()
Program.WaitForExit()
Image5.Visibility = Windows.Visibility.Visible

FileCopy("AppPath & "\Convert\test.csv, "C:\Test.csv")
Button1.Visibility = Windows.Visibility.Visible

2 个答案:

答案 0 :(得分:0)

为了在程序繁忙时更新UI,您需要使用Dispatcher class将更新请求添加到UI消息队列中。以这个同步为例:

public void DoWorkWithFile(string filePath)
{
    CopyFile(filePath);
    ConvertFile(filePath);
    CopyFileBack();
}

我们可以使用Dispatcher类来解决这个问题,并在任务之间将消息反馈给用户界面:

public void DoWorkWithFile(string filePath)
{
    CopyFile(filePath);
    RunOnUiThread((Action)delegate { SomeUiTextBlock.Text = "Copied" });
    ConvertFile(filePath);
    RunOnUiThread((Action)delegate { SomeUiTextBlock.Text = "Converted" });
    CopyFileBack();
    RunOnUiThread((Action)delegate { SomeUiTextBlock.Text = "Copied back" });
}

private object RunOnUiThread(Action method)
{
    return Dispatcher.Invoke(DispatcherPriority.Normal, method);
}

答案 1 :(得分:0)

我知道这是一个VB.NET标记的问题,但我会继续并分享一个C#解决方案。我希望你能够充分了解它将它移植到VB。这是第一次向stackoverflow发布任何内容,如果它解决了您的问题,请将其标记为答案: - )

您必须先了解数据绑定中的一两件事(实际上还有更多)。您基本上创建一个视图模型,定义随时间变化的属性并将其绑定到窗口。在这种情况下,您必须定义一个值以跟踪当前操作并让按钮控制。

免责声明,我在记事本中写了这个,并没有在visual studio上测试过。请注意拼写错误。

using System.ComponentModel;

namespace FileConverter
{

    //define the various states the application will transition to
    public enum OperationStatus
    {
        CopyingFileToNewLocation
        ConvertingFile,
        CopyingFileToOriginalLocation
        OperationCompelete
    }

    //Defines the view model that shall be bound to the window. 
    //The view model updates the UI using event notifications. Any control that had enabled
    //binding will get updated automatically
    public class ViewModel : INotifyPropertyChanged//This interface defines an event used to raise an event and notify subscribers of a changed in data
    {
        private OperationStatus _FileConvertionStatus;
        public event PropertyChangedEventHandler PropertyChanged;
        public OperationStatus FileConvertionStatus
        {
            get
            {
                return _FileConvertionStatus;
            }
            set
            {
                _FileConvertionStatus=value;
                //Notify all UIElements / objects that had subscribed to this property that it has changed
                RaisePropertyChanged(this,"FileConvertionStatus");
            }
        }

        public void RaisePropertyChanged(object sender,string propertyName)
        {
            //check if there is any object that had subscribed to changes of any of the data properties in the view model
            if(PropertyChanged!=null)
                PropertyChanged(sender,new PropertyChangedEventArgs(propertyName));

        }

        public void StartFileConvertion(string filePath)
        {
            //Any time we change the property 'FileConvertionStatus', an event is raised which updates the UI
            this.FileConvertionStatus=OperationStatus.CopyingFileToNewLocation;
            StartCopyingToNewLocation(); //call your copying logic

            this.FileConvertionStatus=OperationStatus.ConvertingFile;
            StartFileConvertion(); //call your conversion logic

            this.FileConvertionStatus=OperationStatus.CopyingFileToOriginalLocation();
            CopyFileToOriginalLocation(); //...

            this.FileConvertionStatus=OperationStatus.OperationCompelete;
        }

    }
}

//现在是UI部分 在窗口的构造函数中,必须在初始化此窗口后立即将窗口绑定到视图模型

public partial class MainWindow : Window
{   
    public MainWindow()
    {
        InitializeComponent();
        ViewModel vm=new ViewModel();

        //setting the data context property the window implicitly binds the whole window to our view model object
        this.DataContext=vm;

        string filePath="c:\file.txt";

        //start the file manipulation process
        vm.StartFileConvertion(filePath);
    }
}

//下一步我们需要将按钮绑定到位于视图模型中的“FileConvertionStatus”属性。我们不会将按钮绑定到整个视图模型,只是它感兴趣的属性。将窗口绑定到前面代码中的视图模型后,所有子元素都可以访问此视图模型的公共属性 (VM从现在开始)。我们在XAML中进行属性绑定

..按钮x:Name =“btnStartFileProcessing”Enabled =“{Binding FileConvertionStatus}”...

我们快到了。一个是缺少的。您会注意到'Enabled'属性是一个布尔值。 'FileConvertionStatus'属性是枚举。同样地,你不能直接将枚举赋值给布尔值,你需要进行一些转换。这就是转换器的用武之地。

转换器允许您定义如何在XAML中将一个属性转换为另一个属性。在这种情况下,我们希望仅在文件转换成功时才启用该按钮。请读一下这个。

创建一个类,如下所示:

using System.Windows.Data;
namespace FileConverter
{
    public class OperationStatusToBooleanConverter : IValueConverter
    {
        public object Convert(object value, Type targetType,object parameter,System.Globalization.CultureInfo culture)
        {
            OperationStatus status=(OperationStatus)value;
            switch(status)
            {
                case OperationStatus.OperationCompelete:
                    return true; //enable the button when everything has been done
                default:
                    return false;//disable the button as the file processing is underway
            }
        }

        public object ConvertBack(object value, Type targetType,object parameter,System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

下一步是在XAML代码中定义转换器。可以把它想象为初始化它虽然它不能远离真实:-)。它更多的是将命名空间导入xaml.Put下面的代码在App.XAML文件中。在App.XAML文件中执行此类声明会使代码全局可见。

<强>的xmlns:MyConverters = “CLR-名称空间:FileConverter”

在Application.Resources XAML标记中,声明转换器如下所示

<Application.Resources>
    <MyConverters:OperationStatusToBooleanConverter x:Key="OperationStatusToBooleanConverter"/>
</Application.Resources>

最后一步 重做按钮中的绑定代码以包含转换器。

... Button Enabled =“{Binding FileConvertionStatus,Converter = {StaticResource OperationStatusToBooleanConverter}}”x:Name =“btnStartFileProcessing”......

请注意我没有对此代码进行线程优化,主要问题是所有工作都在UI线程上完成,如果操作需要很长时间,可能会导致窗口挂起。

根据MVVM代码标准正确设置绑定所需的工作量很多。它可能看起来像是过度杀戮,有时候,它实际上是。记住这一点,一旦UI变得复杂,由于关注点和绑定策略的分离,MVVM肯定会节省一天。