【问题标题】:How to show 'Loading...' overlay while the View Model reloads the bound data视图模型重新加载绑定数据时如何显示“正在加载...”覆盖
【发布时间】:2017-01-17 22:03:06
【问题描述】:

我想做一些听起来非常简单,但我发现很难实现的事情。

假设我有一些内容绑定到慢速加载操作。例如,从本地 SQL 检索并需要几秒钟的可观察列表。在发生这种情况时,我想用“正在加载...”文本或任何其他“请稍候”类型的内容覆盖内容演示者(例如 Groupbox)。

我很快得出结论,在操作之前和之后简单地切换绑定到 UI 的布尔标志是行不通的。在整个操作完成之前,UI 不会刷新。也许是因为操作是CPU密集型的,我不知道。

我现在正在研究Adorners,但我在“忙碌指示器”覆盖的上下文中搜索它时得到的信息很少。从大约 5 年前开始,互联网上只有几个解决方案,我无法让它们中的任何一个起作用。

问题:

听起来很简单 - 如何在视图模型正在更新绑定数据的同时临时在屏幕上显示某些内容?

【问题讨论】:

  • 投给Close的触发快乐Mod——也许这个问题太琐碎了,你可以用一两句话非常清楚地描述解决方案吗?
  • 布尔标志是通知属性吗?它是一个属性,有一个支持字段和一个引发属性更改通知的属性吗?

标签: wpf xaml adorner


【解决方案1】:

我很快得出结论,在操作之前和之后简单地切换绑定到 UI 的布尔标志是行不通的。在整个操作完成之前,UI 不会刷新。也许是因为操作是CPU密集型的,我不知道。

是的,如果您实际上是在后台线程上执行长时间运行的操作,它应该可以工作。

请参考以下简单示例。

查看:

<Window x:Class="WpfApplication2.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication2"
        mc:Ignorable="d"
        xmlns:s="clr-namespace:System;assembly=mscorlib"
        Title="Window1" Height="300" Width="300">
    <Window.DataContext>
        <local:Window1ViewModel />
    </Window.DataContext>
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
    </Window.Resources>
    <Grid>
        <TextBlock>Content...</TextBlock>
        <Grid Background="Yellow" Visibility="{Binding IsLoading, Converter={StaticResource BooleanToVisibilityConverter}}">
            <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">Loading...</TextBlock>
        </Grid>
    </Grid>
</Window>

查看模型:

public class Window1ViewModel : INotifyPropertyChanged
{
    public Window1ViewModel()
    {
        IsLoading = true;
        //call the long running method on a background thread...
        Task.Run(() => LongRunningMethod())
            .ContinueWith(task =>
            {
                //and set the IsLoading property back to false back on the UI thread once the task has finished
                IsLoading = false;
            }, System.Threading.CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
    }

    public void LongRunningMethod()
    {
        System.Threading.Thread.Sleep(5000);
    }

    private bool _isLoading;
    public bool IsLoading
    {
        get { return _isLoading; }
        set { _isLoading = value; NotifyPropertyChanged(); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

【讨论】:

    【解决方案2】:

    下面是一个示例,说明如何在 ViewModel\Model 处理一些长期任务时设置具有“正在加载”显示的视图。

    窗口

    <Window x:Class="Loading.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Loading"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:VisibilityConverter x:Key="visibilityConverter" />
    </Window.Resources>
    <Window.DataContext>
        <local:ViewModel x:Name="viewModel" />
    </Window.DataContext>
    <Grid>
        <Button Content="Perform" VerticalAlignment="Bottom" HorizontalAlignment="Center" Width="100" Height="30" Command="{Binding HandleRequestCommand}" />
        <Border Visibility="{Binding Path=IsLoading,Converter={StaticResource visibilityConverter}}" Background="#AAAAAAAA" Margin="5">
            <TextBlock Text="Loading..." VerticalAlignment="Center" HorizontalAlignment="Center" />
        </Border>
    </Grid>
    

    VisibilityConverter.cs(简单的辅助转换器)

    class VisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (bool)value ? Visibility.Visible : Visibility.Collapsed;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    ViewModel.cs

    public class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        private bool isLoading;
    
        public ViewModel()
        {
            HandleRequestCommand = new Command(HandleRequest);
        }
    
        public bool IsLoading
        {
            get
            {
                return isLoading;
            }
            set
            {
                if (value != isLoading)
                {
                    isLoading = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLoading)));
                }
            }
        }
    
        public ICommand HandleRequestCommand
        {
            get;
        }
    
        public void HandleRequest()
        {
            IsLoading = true;
    
            Task.Factory.StartNew(LongRunningOperation);
        }
    
        private void LongRunningOperation()
        {
            // *** INSERT LONG RUNNING OPERATION ***
    
            Dispatcher.CurrentDispatcher.Invoke(() => IsLoading = false);
        }
    }
    

    【讨论】:

    • 只需使用 ProgressRing 并将 isActive 属性绑定到您的视图模型工作属性。确保工作属性具有更改通知并且引发了 RaisePropertyChanged 事件。例如:IsActive="{Binding Working}"
    • 基本上,解决方案围绕着将数据加载到单独的Task 中,然后通过Dispatcher.CurrentDispatcher 更新用户界面,对吧?
    猜你喜欢
    • 1970-01-01
    • 2021-06-21
    • 2013-07-02
    • 1970-01-01
    • 2015-06-25
    • 1970-01-01
    • 2017-01-06
    • 2011-12-04
    • 1970-01-01
    相关资源
    最近更新 更多