在MVVM中等待游标

时间:2016-10-12 23:54:26

标签: .net wpf mvvm cursor

长时间操作需要等待光标 Mouse.OverrideCursor在ViewModel中工作,但不是真正的MVVM模式 我有一个IsBusy属性 - 如何在视图中获得等待光标?

这种作品 但我只把等待光标放在按钮上 我想在整个Window上等待光标 我在转换器中攻击了Mouse.OverrideCursor = Cursor.Wait,但感觉很乱 有一个更好的方法吗?

<Window x:Class="WaitCursor.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:WaitCursor"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BVC"/>
        <local:ViewModel x:Key="ViewModel"/>
        <local:BusyMouseConverter x:Key="BusyToCursorConverter"/>
    </Window.Resources>
    <Window.Cursor>
        <Binding Path="IsBusy" Converter="{StaticResource BusyToCursorConverter}"/>
    </Window.Cursor>
    <Grid DataContext="{StaticResource ViewModel}">
        <Button  Content="Search" VerticalAlignment="Top"  Command="{Binding SearchCommand}" Margin="2,0,2,2"
                 Cursor="{Binding Path=IsBusy, Converter={StaticResource BusyToCursorConverter}}"/>
    </Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Markup;

namespace WaitCursor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
    [ValueConversion(typeof(bool), typeof(Cursors))]
    public class BusyMouseConverter : MarkupExtension, IValueConverter
    {
        public BusyMouseConverter()
        {
        }

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is bool)
            {
                if ((bool)value)
                {
                    Mouse.OverrideCursor = Cursors.Wait;  // this works but it feels hacky
                    return Cursors.Wait;
                }
                else
                {
                    Mouse.OverrideCursor = null;
                    return null;
                }
            }

            return null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is Cursors)
            {
                if (value == Cursors.Wait)
                    return true;
                else
                    return false;
            }

            return null;
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return instance;
        }

        private static BusyMouseConverter instance = new BusyMouseConverter();

    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.ComponentModel;
using System.Diagnostics;


namespace WaitCursor
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    class ViewModel : ViewModelBase
    {
        private ICommand _SearchCommand;
        public ICommand SearchCommand
        {
            get
            {
                if (_SearchCommand == null)
                {
                    _SearchCommand = new RelayCommand(param => this.Search(), null);
                }
                return _SearchCommand;
            }
        }
        private bool isBusy = false;
        public bool IsBusy
        {
            get { return isBusy; }
            set
            {
                if (isBusy == value)
                    return;
                isBusy = value;
                NotifyPropertyChanged("IsBusy");
                NotifyPropertyChanged("Cursor");
            }
        }
        private async void Search()
        {
            IsBusy = true;
            //UIServices.SetBusyState();
            //Mouse.OverrideCursor = Cursors.Wait;
            await Task.Delay(5000);
            Debug.WriteLine("Search");
            //Mouse.OverrideCursor = null;
            IsBusy = false;
        }
    }
    public class RelayCommand : ICommand
    {
        public RelayCommand(Action<object> execute) : this(execute, null)
        {
        }
        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
            _execute = execute;
            _canExecute = canExecute;
        }
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
        public void Execute(object parameter)
        {
            _execute(parameter);
        }
        private readonly Action<object> _execute;
        private readonly Predicate<object> _canExecute;
    }
}

2 个答案:

答案 0 :(得分:1)

正确的MVVM方法是使用IValueConverter。您的ViewModel可以通过IsBusy继续提供指示程序是否繁忙的逻辑。您的ViewModel永远不应该决定要显示什么 - 这是View的范围。

然后在您的视图中,使用转换器将UserControl或Window的Cursor属性绑定到IsBusy

XAML:

<UserControl ...... Cursor="{Binding IsBusy, Converter={StaticResource MyBusyToCursorConverter}}" ....>

转换器:

public class BusyToCursorConverter : System.Windows.Data.IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null || !(value is bool))
            return System.Windows.Input.Cursors.Arrow;

        var isBusy = (bool)value;

        if (isBusy)
            return System.Windows.Input.Cursors.Wait;
        else
            return System.Windows.Input.Cursors.Arrow;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

更新

您的绑定可能没有触发,这就是您的转换器未被调用的原因。这可能是因为DataContext没有正确踢。

<Window.Cursor>
    <Binding Path="DataContext.IsBusy" RelativeSource="{RelativeSource Self}" Converter="{StaticResource BusyToCursorConverter}"/>
</Window.Cursor>

答案 1 :(得分:1)

此代码存在问题:

<Window (...)>
    <Window.Resources>
        <local:ViewModel x:Key="ViewModel"/>
        <local:BusyMouseConverter x:Key="BusyToCursorConverter"/>
    </Window.Resources>
    <Window.Cursor>
        <Binding Path="IsBusy" Converter="{StaticResource BusyToCursorConverter}"/>
    </Window.Cursor>
    <Grid DataContext="{StaticResource ViewModel}">
        <Button Cursor="{Binding Path=IsBusy, Converter={StaticResource BusyToCursorConverter}}"/>
    </Grid>
</Window>

Binding仅适用于Button,因为它具有适当的DataContext可用(继承自Grid)。但是,您的Window没有DataContext设置(null是默认值),您的绑定将无效。为了使工作正常,您需要将视图模型移动到Window.DataContext(然后它将被嵌套在窗口中的所有控件继承):

<Window (...)>
    <Window.DataContext>
        <local:ViewModel />
    </Window.DataContext>
    <Window.Resources>
        <local:BusyMouseConverter x:Key="BusyToCursorConverter"/>
    </Window.Resources>
    <Window.Cursor>
        <Binding Path="IsBusy" Converter="{StaticResource BusyToCursorConverter}"/>
    </Window.Cursor>
    <Grid>
        <Button Cursor="{Binding Path=IsBusy, Converter={StaticResource BusyToCursorConverter}}"/>
    </Grid>
</Window>

我个人不会将您的视图模型保留为资源,因为资源实例不会将实例设置为Window.DataContext值,这可能会导致意外行为。