wpf 如何在视图模型中处理Slider.ValueChanged事件?

thtygnil  于 2023-10-22  发布在  其他
关注(0)|答案(3)|浏览(270)

我有一个 PlayerV.xaml 视图,里面有一个滑块:

<Slider Value="{Binding CurrentProgress}"/>

有一个按钮:

<Button Content="next song" Command="{Binding playNext}"/>

按钮工作正常。按钮的 playNext 命令包含在 PlayerVM.cs
我希望slider的ValueChanged调用存储在 PlayerVM.cs 中的函数:
[1]:<Slider Value="{Binding CurrentProgress}" ValueChanged="{Binding playNext}"/>
我知道[1]有一个不正确的语法,我使用它是为了解释清楚。

我知道我可以写:

<Slider ValueChanged="Slider_ValueChanged" Value="{Binding CurrentProgress}" />

在 * PlayerV.xaml.cs * 中,

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        //some logic (actions) 
    }    
}

但我不想有任何逻辑。我希望它在 PlayerVM.cs(像按钮的命令处理函数)。如何做到这一点?在我的App.xaml.cs启动函数中还有:

private void OnStartup(object sender, StartupEventArgs e)
{
    MainWindow _mainWindow = new MainWindow();
    PlayerVM playerVM = new PlayerVM();
    _mainWindow.DataContext = playerVM;
    _mainWindow.Show();
 }
q9rjltbz

q9rjltbz1#

你有两个选择首先,尽管你说过不想使用代码隐藏,但一个解决方案就是这样做。在ValueChanged事件处理程序中,当值发生变化时,您可以简单地调用视图模型方法:

private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    Slider slider = sender as Slider;
    PlayerVM viewModel = (PlayerVM)DataContext;
    viewModel.YourMethod(slider.Value);
}

我之所以提供这个解决方案,是因为我怀疑您是MVVM的新手,并且仍然认为不允许使用后面的代码。事实上,根本不是这样的,对于像这样纯粹的UI问题,这是一个很好的地方。
另一种选择是直接将属性数据绑定到Slider.Value。随着Slider值的改变,属性也会改变。因此,您可以简单地从数据绑定属性setter调用方法:

public double CurrentProgress
{
    get { return currentProgress; }
    set
    {
        currentProgress = value;
        NotifyPropertyChanged("CurrentProgress");
        YourMethod(value);
    }
}

另一个选项涉及在自定义附加属性中处理ValueChanged事件。这个解决方案比其他的解决方案要多一点,所以我宁愿引导你去看我已经为其他问题写过的答案,而不是重新写一遍。请参阅我对如何使用MVVM将焦点设置为WPF控件的回答?和WPF C# -通过绑定问题在鼠标点击上导航WebBrowser,以获得此方法的解释和代码示例。

h22fl7wq

h22fl7wq2#

通过使用事件来推荐逻辑,您可以将事件绑定到视图模型,但您需要帮助。您需要使用System.Windows.Interactivity库空间中的函数并包含MVVM Light(可能有其他MVVM库具有该功能,但我使用MVVM Light)。
标签:Is it possible to bind a WPF Event to MVVM ViewModel command?

vlju58qv

vlju58qv3#

还有一个选择您可以制作自己版本的Slider控件,当用户与它交互时可以执行命令。
一个不完美但可以工作的代码可能看起来像这样:

using System.ComponentModel;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

public class CommandSlider : Slider, ICommandSource
{
    public static readonly DependencyProperty CommandParameterProperty = ButtonBase.CommandParameterProperty.AddOwner(typeof(CommandSlider));
    public static readonly DependencyProperty CommandProperty = ButtonBase.CommandProperty.AddOwner(typeof(CommandSlider));
    public static readonly DependencyProperty CommandTargetProperty = ButtonBase.CommandTargetProperty.AddOwner(typeof(CommandSlider));
    private volatile int _commandArmed = 0;

    [Bindable(true), Category("Action")]
    [Localizability(LocalizationCategory.NeverLocalize)]
    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    [Bindable(true), Category("Action")]
    [Localizability(LocalizationCategory.NeverLocalize)]
    public object CommandParameter
    {
        get { return GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }

    [Bindable(true), Category("Action")]
    public IInputElement CommandTarget
    {
        get { return (IInputElement)GetValue(CommandTargetProperty); }
        set { SetValue(CommandTargetProperty, value); }
    }

    protected override void OnDecreaseLarge()
    {
        try
        {
            Interlocked.Increment(ref _commandArmed);

            base.OnDecreaseLarge();
        }
        finally
        {
            Interlocked.Decrement(ref _commandArmed);
        }
    }

    protected override void OnDecreaseSmall()
    {
        try
        {
            Interlocked.Increment(ref _commandArmed);

            base.OnDecreaseSmall();
        }
        finally
        {
            Interlocked.Decrement(ref _commandArmed);
        }
    }

    protected override void OnIncreaseLarge()
    {
        try
        {
            Interlocked.Increment(ref _commandArmed);

            base.OnIncreaseLarge();
        }
        finally
        {
            Interlocked.Decrement(ref _commandArmed);
        }
    }

    protected override void OnIncreaseSmall()
    {
        try
        {
            Interlocked.Increment(ref _commandArmed);

            base.OnIncreaseSmall();
        }
        finally
        {
            Interlocked.Decrement(ref _commandArmed);
        }
    }

    protected override void OnMaximizeValue()
    {
        try
        {
            Interlocked.Increment(ref _commandArmed);

            base.OnMaximizeValue();
        }
        finally
        {
            Interlocked.Decrement(ref _commandArmed);
        }
    }

    protected override void OnMinimizeValue()
    {
        try
        {
            Interlocked.Increment(ref _commandArmed);

            base.OnMinimizeValue();
        }
        finally
        {
            Interlocked.Decrement(ref _commandArmed);
        }
    }

    protected override void OnThumbDragCompleted(DragCompletedEventArgs e)
    {
        try
        {
            Interlocked.Increment(ref _commandArmed);

            base.OnThumbDragCompleted(e);
        }
        finally
        {
            Interlocked.Decrement(ref _commandArmed);
        }
    }

    protected override void OnThumbDragDelta(DragDeltaEventArgs e)
    {
        try
        {
            Interlocked.Increment(ref _commandArmed);

            base.OnThumbDragDelta(e);
        }
        finally
        {
            Interlocked.Decrement(ref _commandArmed);
        }
    }

    protected override void OnThumbDragStarted(DragStartedEventArgs e)
    {
        try
        {
            Interlocked.Increment(ref _commandArmed);

            base.OnThumbDragStarted(e);
        }
        finally
        {
            Interlocked.Decrement(ref _commandArmed);
        }
    }

    protected override void OnValueChanged(double oldValue, double newValue)
    {
        base.OnValueChanged(oldValue, newValue);

        if (_commandArmed > 0 && IsEnabled && oldValue != newValue)
        {
            ExecuteCommand();
        }
    }

    private void ExecuteCommand()
    {
        var command = Command;
        if (command == null)
            return;

        var parameter = CommandParameter;
        if (command is RoutedCommand routedCommand)
        {
            var target = CommandTarget ?? this;
            if (routedCommand.CanExecute(parameter, target))
            {
                routedCommand.Execute(parameter, target);
            }
        }
        else if (command.CanExecute(parameter))
        {
            command.Execute(parameter);
        }
    }
}

然后你可以这样使用这个:

<local:CommandSlider Value="{Binding CurrentProgress, Mode=OneWay}"
    Command="{Binding ChangeCurrentProgress, Mode=OneWay}"
    CommandParameter="{Binding Value, RelativeSource={RelativeSource Self}}" />

我还建议添加一些去抖动功能,因为在拖动滑块的手柄时,该命令将被执行数十次。

相关问题