3
\$\begingroup\$

I recently started making a media player, this is the first time I'm using WPF and I found that most project are utilizing the MVVM architectural pattern, I haven't used that one either but I decided to give it a go.

For this question I'd like to focus on the event binding from the ViewModel to the View.

VideoPlayerViewModel.cs

public class VideoPlayerViewModel : ViewModelBase
{
    public event EventHandler<MouseEventArgs> OnMovieSliderMouseEnter;
    public event EventHandler<MouseEventArgs> OnMovieSliderPreviewMouseMove;
    public event EventHandler<MouseEventArgs> OnMovieSliderMouseLeave;

    public void MovieSliderMouseEnter(object sender, MouseEventArgs args)
    {
        OnMovieSliderMouseEnter?.Invoke(sender, args);
    }

    public void MovieSliderPreviewMouseMove(object sender, MouseEventArgs args)
    {
        OnMovieSliderPreviewMouseMove?.Invoke(sender, args);
    }

    public void MovieSliderMouseLeave(object sender, MouseEventArgs args)
    {
        OnMovieSliderMouseLeave?.Invoke(sender, args);
    }
}

This is the ViewModel, since accessing anything UI-related is violation of MVVM, I made 3 events which the view will subscribe to and execute the code there.

I see few problems here:

  1. You can invoke the event from outside the class.
  2. It doesn't provide any greater form of reusability than just plain code in the View.cs.

MainWindow.xaml - View

<customControls:ThumbDragSlider x:Name="sMovieSkipSlider"
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseEnter">
            <ei:CallMethodAction TargetObject="{Binding}"
                MethodName="MovieSliderMouseEnter"/>
        </i:EventTrigger>
        <i:EventTrigger EventName="MouseMove">
            <ei:CallMethodAction TargetObject="{Binding}"
                MethodName="MovieSliderPreviewMouseMove"/>
        </i:EventTrigger>
        <i:EventTrigger EventName="MouseLeave">
            <ei:CallMethodAction TargetObject="{Binding}"
                MethodName="MovieSliderMouseLeave"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</customControls:ThumbDragSlider>

This is the slider where I've attached the vm methods to.

MainWindow.xaml.cs - View/Code behind

Here is where I subscribe to the vm events:

ViewModel.OnMovieSliderMouseEnter += SMovieSkipSlider_OnMouseEnter;
ViewModel.OnMovieSliderPreviewMouseMove += SMovieSkipSlider_OnPreviewMouseMove;
ViewModel.OnMovieSliderMouseLeave += SMovieSkipSlider_OnMouseLeave;

And the event handlers:

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 = SimulatePosition(e.GetPosition(sMovieSkipSlider), _movieSkipSliderTrack);
    simulatedPosition = Math.Min(Math.Max(simulatedPosition, 0), sMovieSkipSlider.Maximum);
    lbTimeTooltip.AddToLeftMargin(Mouse.GetPosition(sMovieSkipSlider).X - lbTimeTooltip.Margin.Left + 35);
    lbTimeTooltip.Content = TimeSpan.FromSeconds(simulatedPosition).ToShortString();
}

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

private double CalculateTrackDensity(Track track)
{
    return (track.Maximum - track.Minimum) / (track.ActualWidth - track.Thumb.DesiredSize.Width);
}

private double SimulatePosition(System.Windows.Point point, Track track)
{
    return (point.X - track.Thumb.DesiredSize.Width / 2) * CalculateTrackDensity(track);
}

I also have a RelayCommand class ready, but I cant seem to find a place to incorporate it in to:

public class CommandBase<T> : ICommand
{
    private readonly Action<T> _execute;
    private readonly Predicate<T> _canExecute;

    public event EventHandler CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    public CommandBase(Action<T> execute, Predicate<T> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        if (_canExecute != null)
        {
            return _canExecute((T)parameter);
        }
        return true;
    }

    public void Execute(object parameter)
    {
        _execute((T)parameter);
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.