在PropertyChanged和Bind之间的WPF中等待光标

时间:2011-10-19 23:00:59

标签: wpf

我有一个WPF应用程序,我正在使用MVVM。

在我的视图模型中,我有:

    private string logs;
    public string Logs
    {
        get { return logs; }
        set
        {
            logs = value;
            OnPropertyChanged("Logs");
        }
    }

    private void ExecLoadData()
    {
        using (new WaitCursor())
            Logs = LogFile.ReturnContent();
    }

    private RelayCommand loadData;
    public ICommand LoadData
    {
        get
        {
            if (loadData == null)
                loadData = new RelayCommand(param => this.ExecLoadData());
            return loadData;
        }
    }

在视图中:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <i:InvokeCommandAction Command="{Binding LoadData}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

我注意到OnPropertyChanged的拍摄和页面上的数据显示之间发生了延迟。

我需要一种方法来显示等待光标到屏幕上显示的数据。

已经实现了WaitCursor()方法,但等待光标只出现,直到数据文件被加载到内存中,即在内存中加载数据之间,直到数据显示在页面上,光标保持正常。

任何提示?

编辑(在AngelWPF的帮助下最终解决方案):

    private Boolean isBusy = false;
    public Boolean IsBusy
    {
        get { return isBusy; }
        set
        {
            if (isBusy == value)
                return;
            isBusy = value;
            OnPropertyChanged("IsBusy");
        }
    }

    private string logs;
    public string Logs
    {
        get { return logs; }
        set
        {
            logs = value;
            OnPropertyChanged("Logs");
        }
    }

    public void ExecuteBusy(DoWorkEventHandler doWorkEventHandler)
    {
        IsBusy = true;

        var backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += doWorkEventHandler;
        backgroundWorker.RunWorkerCompleted += (sender, e) => { IsBusy = false; }; 
        backgroundWorker.RunWorkerAsync();
    }

    protected override void ExecLoadData()
    {
        LoadLogs();
    }

    private void LoadLogs()
    {
        ExecuteBusy((sender, e) => 
        { 
            Logs = LogFile.ReturnContent(); 
        });
    }

<Page.Resources>
    <ut:BooleanVisibilityConverter x:Key="BooleanVisibilityConverter" />
</Page.Resources>

<Page.DataContext>
    <vm:ManutencaoMonitoracaoLogsViewModel/>
</Page.DataContext>

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <i:InvokeCommandAction Command="{Binding LoadData}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

<Grid>

    <TextBox Text="{Binding Logs, Mode=OneWay}" VerticalScrollBarVisibility="Auto" IsReadOnly="True" BorderBrush="White" />

    <Border BorderBrush="Black" BorderThickness="1" Background="#80DBDBDB" Grid.RowSpan="3"
            Visibility="{Binding IsBusy, Converter={StaticResource BooleanVisibilityConverter}}">
        <Grid>
            <ct:LoadingAnimation HorizontalAlignment="Center" VerticalAlignment="Center"/>            
        </Grid>
    </Border>

</Grid>

2 个答案:

答案 0 :(得分:0)

您不应该在模型实现中对光标有任何引用,如何从UI调用ExecelLoadData()方法?我建议在进行调用之前更改游标状态,并在返回时重新进行

答案 1 :(得分:0)

这需要通过我们称之为AsyncWorker的东西来编排任何繁重的功能。这是使用后台工作程序的异步命令执行。它有一个触发器标志,从视图模型启动为true,因此当任何繁重的功能被委托给它时,它会在窗口的装饰器中运行。当功能执行时,动画指示用户可能延迟的功能正在运行并且他/她应该等待。然后,当委托完成时,AsyncWorker本身会隐藏动画并将页面正确显示给用户。

http://elegantcode.com/2009/08/21/a-simple-wpf-loading-animation/

我可以想象它可以这样做......

  Characteristics of `AsyncWorker` 
   1. It is a Control that runs an animation such as 
       a neverending progressing progress bar
         or rotating circles etc. in the adorner of the UI.
   2. It accepts the parent panel on which the waiter animation is shown.
   3. It has a boolean dependency property say "StartOperation".           
      When this changes to true we start the animation. 
      When true this also begins excuting the `WorkerDelegateCommand` given below.
   4. It also has a ICommand dependency property called "WorkerDelegateCommand"
      This will be supplied from your `ViewModel`.
      It will hold the time consuming operation as a delegate. 

所以基本上当我们将AsyncWorker.StartOperation设置为true时,我们会使用动画故事板渲染父面板的装饰器并启动后台工作程序。此后台工作程序在另一个线程上运行WorkerDelegateCommand。因此,您的慢速操作在除UI之外的另一个线程上运行。同时异步工作者动画继续运行。当WorkerDelegateCommand委托完成其缓慢的工作时,后台工作人员DoWork呼叫退出并调用RunCompleted。在此,我们将StartOperation设置为false。

我们可以通过这种方式配置此AsyncWorker ......

   <Grid>
       <Button Content="Do some slow work"
               Command="{Binding RunAsyncWorkerCommand}" />
       <AsyncWorker
               ParentPanel="{Binding RelativeSource={RelativeSource
                                       AncestorType={x:Type Grid}}}"
               StartOperation="{Binding StartSlowWork, Mode=TowWay}"
               WorkerDelegateCommand="{Binding MySlowDelegateCommand}"
               Visibility="Collapsed" />
    </Grid>

因此,在上面的示例中,当单击按钮时,包含按钮的网格显示服务员动画并开始执行慢速操作。为此,您的DataContext和/或ViewModel需要三个属性......

1. `StartSlowWork` - A boolean flag to start AsyncWorker which is TwoWay bound. 
2. `RunAsyncWorkerCommand` - Command to set the `StartSlowWork` flag to true.
3. `MySlowDelegateCommand` - Command to execute slow work.

完成此操作后,每个执行缓慢的操作都可以移动到AsyncWorker。