Search code examples
wpfxamlmvvmdata-bindingslider

How to DataBind WPF Slider's Thumb's DragCompleted event


I want to bind the DragCompleted event to one of my ViewModel's Command. I tried the following using Blend but it doesn't work:

<Slider x:Name="slider" HorizontalAlignment="Left" Margin="41,147,0,0" VerticalAlignment="Top" Width="412">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Thumb.DragCompleted">
            <i:InvokeCommandAction Command="{Binding DragCompletedCommand}"></i:InvokeCommandAction>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Slider>

But this doesn't work. When I use the normal binding of event to code behind, it works:

<Slider x:Name="slider" Thumb.DragCompleted="slider_DragCompleted"  HorizontalAlignment="Left" Margin="41,147,0,0" VerticalAlignment="Top" Width="412"></Slider>

I tried searching but strangely couldn't find answer to this.


Solution

  • You can write an attached property for this which can look like:

    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Input;
    
    namespace MyTestApplication
    {
        internal class SliderExtension
        {
            public static readonly DependencyProperty DragCompletedCommandProperty = DependencyProperty.RegisterAttached(
                "DragCompletedCommand",
                typeof(ICommand),
                typeof(SliderExtension),
                new PropertyMetadata(default(ICommand), OnDragCompletedCommandChanged));
    
            private static void OnDragCompletedCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                Slider slider = d as Slider;
                if (slider == null)
                {
                    return;
                }
    
                if (e.NewValue is ICommand)
                {
                    slider.Loaded += SliderOnLoaded;
                }
            }
    
            private static void SliderOnLoaded(object sender, RoutedEventArgs e)
            {
                Slider slider = sender as Slider;
                if (slider == null)
                {
                    return;
                }
                slider.Loaded -= SliderOnLoaded;
    
                Track track = slider.Template.FindName("PART_Track", slider) as Track;
                if (track == null)
                {
                    return;
                }
                track.Thumb.DragCompleted += (dragCompletedSender, dragCompletedArgs) =>
                {
                    ICommand command = GetDragCompletedCommand(slider);
                    command.Execute(null);
                };
            }
    
            public static void SetDragCompletedCommand(DependencyObject element, ICommand value)
            {
                element.SetValue(DragCompletedCommandProperty, value);
            }
    
            public static ICommand GetDragCompletedCommand(DependencyObject element)
            {
                return (ICommand)element.GetValue(DragCompletedCommandProperty);
            }
        }
    }
    

    And your Slider-Definition then looks like:

    <Slider x:Name="slider" HorizontalAlignment="Left" Margin="41,147,0,0" VerticalAlignment="Top" Width="412" 
            extensions:SliderExtension.DragCompletedCommand="{Binding SlideCompletedCommand}"/>
    

    extensions is the namespace where your attached property is located.

    And in your ViewModel you have an ICommand-Property called SlideCompletedCommand, which can look like:

    private ICommand slideCompletedCommand;
    public ICommand SlideCompletedCommand
    {
        get { return slideCompletedCommand ?? (slideCompletedCommand = new RelayCommand(p => SlideCompleted())); }
    }
    
    private void SlideCompleted()
    {
        // Your slide-completed-code here
    }