在没有UI锁定的情况下在MVVM中实现长时间运行的业务逻辑

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在没有UI锁定的情况下在MVVM中实现长时间运行的业务逻辑相关的知识,希望对你有一定的参考价值。

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

视图:我目前有一个进度条,随着后端操作的进度继续进行填充。

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

VIEWMODEL:目前我已经实现了一个基本视图模型,其中包含三个绑定到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上的一个按钮。

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

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;
                }));
            }
        });
    }

通过调用调度程序,您可以跳回主线程。

以上是关于在没有UI锁定的情况下在MVVM中实现长时间运行的业务逻辑的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有 Flash 的情况下在 Firefox 中实现复制到剪贴板。需要实施[重复]

如何在不冻结 GUI 的情况下在单个插槽中实现阻塞进程?

如何在没有选择按钮的情况下在 GridView 中实现全行选择?

如何在没有外部库的情况下在 React 中实现 Google Maps JS API?

是否可以在没有动态多态性的情况下在 C++ 中实现状态设计模式?

如何在没有中间副本的情况下在标准 C 中实现 memmove?