在 MVVM 中实现长时间运行的业务逻辑,而无需 UI 锁定



我目前正在练习 MVVM 模式,我想在我的业务层中实现一个不会因长时间运行的逻辑而冻结的 UI。让我在下面解释一下。

视图: 我目前有一个进度条,随着后端操作进度的进行,该进度条应该会填满。

<ProgressBar x:Name="prgFeedback" Height="25" Margin="10,20,10,40" Value="{Binding ProgressValue}" />

视图模型: 目前,我已经实现了一个基本视图模型,其中包含绑定到 UI 的三个属性。

class MyViewModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
private string _InputPath { get; set; }
private string _OutputPath { get; set; }
private double _ProgressValue { get; set; }

public string InputPath
{
get { return _InputPath; }
set
{
if (_InputPath != value)
{
_InputPath = value;
PropertyChanged(this, new PropertyChangedEventArgs("InputPath")); 
}
}
}
public string OutputPath
{
get { return _OutputPath; }
set
{
if (_OutputPath != value)
{
_OutputPath = value;
PropertyChanged(this, new PropertyChangedEventArgs("OutputPath"));
}
}
}
public double ProgressValue
{
get { return _ProgressValue; }
set
{
if (_ProgressValue != value)
{
_ProgressValue = value;
PropertyChanged(this, new PropertyChangedEventArgs("ProgressValue"));
}
}
}
public ICommand Button_Command{get;set;}
public MyModel()
{
modelobj = new MyModel();
}

public void Button_Run()
{
ProgressValue = 0;
InputPath = modelobj.DialogGetInput();
//Get output here 
thing.ProgressChanged += new EventHandler<WorkerEventArgs>(OnProgressChanged);
OutputPath = modelobj.FlowLogic();
}
private void OnProgressChanged(object sender, WorkerEventArgs e)
{
ProgressValue = e.Progress;
}
}

在这里,我将命令移动到公共构造函数,以便我的应用程序将从应用程序的启动开始运行循环。但现在它绑定到 UI 上的一个按钮。

模型:所以这是模型的精简版本,我希望这足以让你了解我计划执行的操作。

public class MyModel
{

#region Properties
public  string OutputPath
{ get
{
return _OutputPath;
}
set
{ if (_OutputPath != value)
{
_OutputPath = value;
}
}
}
private  string _OutputPath{ get;  set; }
public  string InputPath
{
get
{
return _InputPath;
}
set
{
if (_InputPath != value)
{
_InputPath = value;
}
}
}
private string _InputPath { get; set; }
#endregion
public MyModel()
{
}
#region Helpers
public event EventHandler<WorkerEventArgs> ProgressChanged;
private void NotifyProgress(double current, double max)
{
if (ProgressChanged != null)
{
double progress = (current/max)*100 ; 
ProgressChanged(this, new WorkerEventArgs(progress));
}
}
#endregion  
#region Logic
public string DialogGetInput()
{
//Logic to get a path for the File containing the ID's
return path;
}

private string Generate_Date_Info(string ID)
{
string tolog = "";
//Logic to get date for each ID from Database.
return tolog;
}
public string FlowLogic()
{

string tolog="";
int i = 0; // used to get the current index from the list of ID's
int k = 0; // used to get the total count of ID's from file.

foreach(string ID in IDs)  //IDs will contain all the IDs read from the file
{
tolog +=Generate_runs(ID);
tolog +=Generate_ID_Change(ID);
tolog +=Generate_Date_Info(ID);
}
//Write 'tolog' to a file using StreamWriter
NotifyProgress(int i,int k);
return OutputPath;

}
private string Generate_ID_Change(string ID)
{
string tolog = "";
tolog += Environment.NewLine;
//Logic to retrive addidtional data from Database
return tolog;
}
private string Generate_runs(string ID)
{
string tolog = "";
tolog += Environment.NewLine;
//Logic to retrive Data from Database
return tolog;
}
#endregion
}

正如另一篇文章指出的那样,我使用工作线程事件通知我的 UI 有关下面正在进行的更改。

public class WorkerEventArgs : EventArgs
{
public double Progress { get; private set; }
public WorkerEventArgs(double progress)
{
Progress = progress;
}
}

当我在逐步调试模式下运行它时,我可以看到我的模型调用事件,这会触发 ViewModel 上的值更改,然后我看到调用 PropertyChanged,但我看不到我的 UI 更新它的进度条。

如何在运行此特定应用程序时使 UI 响应。

您应该在其自己的线程中运行长时间运行的操作。像这样:

public DoLongRunningOperation()
{
Task.Run(() =>
{
for (var i = 0; i < 100; i++)
{
// Do some really cpu intense stuff.. 
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
{
ProgressValue = i;
}));
}
});
}

通过呼叫调度员,您可以跳回到主胎面。

最新更新