我正在创建一个简单的WPF应用程序,当你点击一个按钮时,它会运行几个步骤,比如将文件复制到新位置,转换文件,然后将新文件复制回原始位置。
这些步骤运行得很好,但我想让WPF窗口更新到它所在的步骤,并在运行时隐藏按钮。
窗口只有在运行完我的代码后才会更新。我想我以前可以用me.refresh
在经典表单上这样做,但这在WPF上不起作用。
在每个步骤完成后,我能做些什么来更新窗口吗?
谢谢
Button1.Visibility = Windows.Visibility.Hidden
FileCopy("C:Test.xsf", AppPath & "Converttest.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 & "Converttest.csv, "C:Test.csv")
Button1.Visibility = Windows.Visibility.Visible
为了在程序繁忙时更新UI,您需要使用Dispatcher
类将更新请求添加到UI消息队列中。举个同步的例子:
public void DoWorkWithFile(string filePath)
{
CopyFile(filePath);
ConvertFile(filePath);
CopyFileBack();
}
我们可以使用Dispatcher
类来分解它,并在任务之间将消息反馈给UI:
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);
}
我知道这是一个带有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 中进行属性绑定
Button 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中。将以下代码放入App.XAML文件中。在App.XAML文件中进行这样的声明可以使代码全局可见。
xmlns:MyConverers="clr namespace:FileConverter"
在Application.Resources XAML标记中,声明转换器,如下所示
<Application.Resources>
<MyConverters:OperationStatusToBooleanConverter x:Key="OperationStatusToBooleanConverter"/>
</Application.Resources>
最后一步重做按钮中的绑定代码以包含转换器。
Button Enabled="{Binding FileConverterStatus,Converter={StaticResource OperationStatusToBooleanConverter}"x:Name="btnStartFileProcessing"。。。
请注意,我还没有对这段代码进行线程优化,主要问题是所有的工作都是在UI线程上完成的,如果操作需要很长时间,这可能会导致窗口挂起
根据MVVM代码标准正确设置绑定所需的工作量很大。这可能看起来像是一个过度杀伤,有时实际上确实如此。但请记住,一旦UI变得复杂,MVVM肯定会因为关注点和绑定策略的分离而化险为夷。