4
votes

I need to control video position (and also document page in another case) with a slider. But problem is that I don't know how to decide if a slider value vas changed by user (click, thumb drag...), or from binding. It is possible to fire some event only when slider value is updated from UI, not from binding?

I've implemented a custom slider:

public class NotifyingSlider : Slider
{

    public static readonly DependencyProperty ValueChangedFromUIProperty =
        DependencyProperty.Register("ValueChangedFromUI", typeof(ICommand), typeof(NotifyingSlider));

    public ICommand ValueChangedFromUI
    {
        get
        {
            return (ICommand)GetValue(ValueChangedFromUIProperty);
        }
        set
        {
            SetValue(ValueChangedFromUIProperty, value);
        }
    }

    protected override void OnThumbDragCompleted(DragCompletedEventArgs e)
    {
        base.OnThumbDragCompleted(e);
        ValueChangedFromUI?.Execute(null);
    }

    protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonUp(e);
        ValueChangedFromUI?.Execute(null);
    }
}

But ValueChangedFromUI command is executed only when user drag a thumb to the specific position or click on bar exactly to the position of tick. When user click between two ticks (so value/thumb is moved to the nearer one), command is not executed - this is a main problem for me.

1

1 Answers

2
votes

Unfortunately, I don't think there's an easy way to do this. The way I would do it is to simply not bind the value at all and add a ValueChanged event to the slider. Then, when you need to do something when the slider is visually changed, that something will occur in ValueChanged. You would also want to update the property containing the value (that is not bound) in this event. That way, the slider value and the value that should be bound (but is not) is always the same, and allows you to specify different action depending on which was changed.

You can specify an additional flag (bool IgnoreUpdate), which you can set to true when updating the unbound property.

Then, when you update the unbound property directly, update the actual slider value with a method that checks IgnoreUpdate. If IgnoreUpdate is true, don't attempt to update the unbound property (this will result in a infinite loop, I believe) and set back to false for next value change. If it's false, we changed the slider visually and in order to reflect this change in the property we care about (the unbound one), we update it.

private bool IgnoreUpdate = false;

private int value = 0;
public int Value
{
    get
    {
        return this.value;
    } set
    {
        this.value = value;
        this.IgnoreUpdate = true;
        this.UpdateValue(value);
    }
}

public void UpdateValue(int ProgressValue)
{
    this.Slider.Value = ProgressValue;
    if (IgnoreUpdate)
    {
        IgnoreUpdate = false;
        return;
    }
    //Do stuff when the value changes visually
    this.Value = ProgressValue;
}

private void Slider_ValueChanged(object sender, ProgessChangedEventArgs e)
{
    this.UpdateValue(e.Value);
}

Not sure why you'd want to do it this way anyway, but this is the most forward-thinking approach I could devise.