Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jquery-ui/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将方法从视图移动到视图模型-WPF MVVM_C#_Wpf_Mvvm_View_Viewmodel - Fatal编程技术网

C# 将方法从视图移动到视图模型-WPF MVVM

C# 将方法从视图移动到视图模型-WPF MVVM,c#,wpf,mvvm,view,viewmodel,C#,Wpf,Mvvm,View,Viewmodel,我的代码隐藏中包含以下代码: public partial class MainWindow { private Track _movieSkipSliderTrack; private Slider sMovieSkipSlider = null; private Label lbTimeTooltip = null; private MediaElement Player = null; public VideoPlayerViewModel Vie

我的代码隐藏中包含以下代码:

public partial class MainWindow
{
    private Track _movieSkipSliderTrack;
    private Slider sMovieSkipSlider = null;
    private Label lbTimeTooltip = null;
    private MediaElement Player = null;

    public VideoPlayerViewModel ViewModel
    {
        get { return DataContext as VideoPlayerViewModel; }
    }

    public MainWindow()
    {
        InitializeComponent();
    }

    private void SMovieSkipSlider_OnLoaded(object sender, RoutedEventArgs e)
    {
        _movieSkipSliderTrack = (Track)sMovieSkipSlider.Template.FindName("PART_Track", sMovieSkipSlider);
        _movieSkipSliderTrack.Thumb.DragDelta += Thumb_DragDelta;
        _movieSkipSliderTrack.Thumb.MouseEnter += Thumb_MouseEnter;
    }

    private void Thumb_MouseEnter(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed && e.MouseDevice.Captured == null)
        {
            var args = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, MouseButton.Left)
            {
                RoutedEvent = MouseLeftButtonDownEvent
            };
            SetPlayerPositionToCursor();
            _movieSkipSliderTrack.Thumb.RaiseEvent(args);
        }
    }

    private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
    {
        SetPlayerPositionToCursor();
    }

    private void SMovieSkipSlider_OnMouseEnter(object sender, MouseEventArgs e)
    {
        lbTimeTooltip.Visibility = Visibility.Visible;
        lbTimeTooltip.SetLeftMargin(Mouse.GetPosition(sMovieSkipSlider).X);
    }

    private void SMovieSkipSlider_OnPreviewMouseMove(object sender, MouseEventArgs e)
    {
        double simulatedPosition = SimulateTrackPosition(e.GetPosition(sMovieSkipSlider), _movieSkipSliderTrack);
        lbTimeTooltip.AddToLeftMargin(Mouse.GetPosition(sMovieSkipSlider).X - lbTimeTooltip.Margin.Left + 35);
        lbTimeTooltip.Content = TimeSpan.FromSeconds(simulatedPosition);
    }

    private void SMovieSkipSlider_OnMouseLeave(object sender, MouseEventArgs e)
    {
        lbTimeTooltip.Visibility = Visibility.Hidden;
    }

    private void SetPlayerPositionToCursor()
    {
        Point mousePosition = new Point(Mouse.GetPosition(sMovieSkipSlider).X, 0);
        double simulatedValue = SimulateTrackPosition(mousePosition, _movieSkipSliderTrack);
        SetNewPlayerPosition(TimeSpan.FromSeconds(simulatedValue));
    }

    private double CalculateTrackDensity(Track track)
    {
        double effectivePoints = Math.Max(0, track.Maximum - track.Minimum);
        double effectiveLength = track.Orientation == Orientation.Horizontal
            ? track.ActualWidth - track.Thumb.DesiredSize.Width
            : track.ActualHeight - track.Thumb.DesiredSize.Height;
        return effectivePoints / effectiveLength;
    }

    private double SimulateTrackPosition(Point point, Track track)
    {
        var simulatedPosition = (point.X - track.Thumb.DesiredSize.Width / 2) * CalculateTrackDensity(track);
        return Math.Min(Math.Max(simulatedPosition, 0), sMovieSkipSlider.Maximum);
    }

    private void SetNewPlayerPosition(TimeSpan newPosition)
    {
        Player.Position = newPosition;
        ViewModel.AlignTimersWithSource(Player.Position, Player);
    }
}
我希望遵循MVVM模式,并将此代码移动到我的ViewModel中,目前它只有很少的属性。我在这里和StackOverflow之外读了很多关于这个主题的答案,我下载了一些github项目来检查有经验的程序员是如何处理特定情况的,但这些似乎都不能消除我的困惑。我想看看如何重构我的案例以遵循MVVM模式

这些是额外的扩展方法以及ViewModel本身:

static class Extensions
{
    public static void SetLeftMargin(this FrameworkElement target, double value)
    {
        target.Margin = new Thickness(value, target.Margin.Top, target.Margin.Right, target.Margin.Bottom);
    }

    public static void AddToLeftMargin(this FrameworkElement target, double valueToAdd)
    {
        SetLeftMargin(target, target.Margin.Left + valueToAdd);
    }
}

public class VideoPlayerViewModel : ViewModelBase
{
    private TimeSpan _movieElapsedTime = default(TimeSpan);
    public TimeSpan MovieElapsedTime
    {
        get { return _movieElapsedTime; }
        set
        {
            if (value != _movieElapsedTime)
            {
                _movieElapsedTime = value;
                OnPropertyChanged();
            }
        }
    }

    private TimeSpan _movieLeftTime = default(TimeSpan);
    public TimeSpan MovieLeftTime
    {
        get { return _movieLeftTime; }
        set
        {
            if (value != _movieLeftTime)
            {
                _movieLeftTime = value;
                OnPropertyChanged();
            }
        }
    }

    public void AlignTimersWithSource(TimeSpan currentPosition, MediaElement media)
    {
        MovieLeftTime = media.NaturalDuration.TimeSpan - currentPosition;
        MovieElapsedTime = currentPosition;
    }
}

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }
}

我已经试着按照注释中的要求准备好代码复制/粘贴,如果您想完全复制它,视图代码中的所有控件都是在XAML中创建的。

想法是在VM中为您想要更新的UI的每个区域或需要处理的事件分别设置一个属性和命令

只要看一下您当前的代码,我想如果您直接挂接到slider的Value属性并将其(双向)绑定到VM上的属性,您将有一个更轻松的时间(您将能够删除一些事件处理程序)。每当用户拖动时,您将能够看到值何时更新,并且可以相应地进行处理

至于你的擦洗条的“隐藏”效果,你可能会有一个更容易的时间只是挂钩到你的滑块的视觉状态

编辑:

在滑块上:

Value={Binding ElapsedTime, Mode=TwoWay}
Maximum={Binding MaxTime, Mode=OneWay} //could also be OneTime, depending on the lifecycle of the control

不能在viewmodel中使用事件。所以您必须创建命令模式类,而只需创建viewmodel类。之后,可以在xml文件中使用viewmodel的名称空间,或者使用“xmlns标记”查看文件,并为类创建资源,提供完整的键名,并在中设置datacontext
现在执行键绑定


注意:如果您需要更清晰的信息,请回复

我建议使用Caliburn Micro。 如果使用该库,则可以绑定如下事件:

还是那样

查看他们的网站,了解更高级的可能性:


您的viewmodel中不应该有鼠标处理程序。这些事件属于UI,因此属于视图。相反,请将臃肿的视图代码移动到。从行为中,您可以选择通过接口调用viewmodel。例如:

var vm = AssociatedObject.DataContext as IPlayerViewModel;
vm?.AlignTimersWithSource(...);

我在XAML应用程序中遵循一些简单规则:

  • ViewModel不应该知道视图,因此在ViewModel中找不到与UI相关的代码
  • 所有与UI相关的代码都在代码隐藏(xaml.cs)中
  • 用户控件和依赖属性是您最好的朋友,所以请使用它们。视图应由用户控件组成,每个控件都有自己的ViewModel
  • 通过构造函数注入注入依赖项,以便在编写单元测试时模拟它们

  • 看起来大多数方法都应该由视图模型中的属性替换,并且视图应该绑定到这些属性properties@ASh这不会违反MVVM模式吗?我直接引用了视图中的一些项目。@ASh您能详细说明一下吗,也许是一个答案,我对使用属性的解决方案非常感兴趣,我准备提供我想的是:视图模型类实现了INotifyPropertyChanged和属性,例如:
    public类Vm:INotifyPropertyChanged{public bool State{get;set;}public string movielapsedtimestring{get;set;}
    (所有属性都会引发更改事件)。视图使用绑定:
    IsEnabled=”{Binding Path=State}“
    Content=“{Binding movielapsedtimestring}"
    。不确定MediaElement属性是否可绑定,但这种情况下有解决办法。注意:未测试,因为我无法复制您的代码样本并运行,因此无法编译代码样本。非常感谢:)Thumb事件如何?您是否可以详细说明滑块工具提示的视觉状态?@Deadzoneer拖动UI上的滑块,“值”将更改。您的VM将接受此更改,并且您可以根据需要调用一个方法(在这种情况下,您可以在setter中调用控制模型数据的方法).至于视觉状态,它太复杂了,无法直接解释。你需要看一个教程。我发现使用Blend最简单。要点是每种类型的控件都有“状态”这将在某些事件发生时触发。您将覆盖这些模板状态,以便在触发时可以创建动画效果。请使用questionCaliburn中的代码提供此类转换的示例。MIcro还有一些不错的约定。例如,如果您的xaml中有一个名为Persons的ListView,则它将不会请尝试将其绑定到ViewModel中具有此名称的列表。更好的是,如果ViewModel中有一个名为SelectedPerson的属性,则ListView的SelectedItem将自动绑定到该属性。
    var vm = AssociatedObject.DataContext as IPlayerViewModel;
    vm?.AlignTimersWithSource(...);