4

In my application I use two sliders to control the brightness and contrast of certain images and the image has to be completely recalculated pixel by pixel every single time when either one of the two sliders changes its value-property. The recalculation of smaller images goes completely fine and doesn't cause any problems, however, larger images need longer to be recalculated and the slider thumb moves with a slight delay compared to the mouse pointer. I do need the image to be recalculated more or less in real time so simply having an event on DragCompleted or similarly is not acceptable.

The recalculation is initialized using the ValueChanged-event of the slider. I think a good solution to this problem would be if the event is not fired as quickly as possible but will at least wait say 50ms before it is fired again, but is there a property of a slider that can control that?

Another solution I was thinking of, is removing the event from the slider right at the start when the event gets handled and adding it again some short time later, but that might cause some delay itself which is also not preferred here.

I couldn't really find anything on this topic anywhere, so if somebody has any good suggestions or directions I could use, I would be very greatful.

philkark
  • 2,417
  • 7
  • 38
  • 59

4 Answers4

6

You can also make use of BindingBase.Delay property introduced in WPF 4.5.

Simply bind Slider's value to a dependency property setting Delay on the binding. This will cause value updates only after certain time (e.g. 500 ms) and this can make your app smoother.

Libor
  • 3,285
  • 1
  • 33
  • 41
4

If you think your application don't need to do the calculations every time the ValueChanged event is triggered,You can use the DragCompleted Event in Thumb control to determine the position after the user finished dragging the control.

<Slider Thumb.DragCompleted="Slider_DragCompleted_1"  
    Height="27" Margin="132,162,0,0" VerticalAlignment="Top" Width="303"/>

When the user stopped dragging,

private void Slider_DragCompleted_1(object sender, DragCompletedEventArgs e)
{
    Slider s = sender as Slider;
    // Your code
    MessageBox.Show(s.Value.ToString());
}

But beware that this works only when user drags the slider.This doesn't get triggered when user clicks on the slider.

Refer this for handling other events like mouse click etc..

If you want to calculate with some time delay then you can use a timer .

EDIT: Based on your request you can do like this. In the 'ValueChanged' event.

 // Start a new thread only if the thread is stopped 
 // or the thread has not been created yet.
 if (threadPopular == null || threadPopular.ThreadState == ThreadState.Stopped)
 {
           threadPopular = new Thread(new ThreadStart(Your function));
           threadPopular.Start();
  }
Community
  • 1
  • 1
Naren
  • 2,231
  • 1
  • 25
  • 45
  • Thank you for your answer, but as I mentioned in my question, the image has to be recalculated in real time while the slider is moving. – philkark Aug 29 '13 at 12:52
  • 1
    @phil13131 So you want to reduce the lag in moving the slider? – Naren Aug 29 '13 at 12:58
  • Well, yes that is what I specified in my question. – philkark Aug 29 '13 at 12:59
  • I have, but the Problem is that the event of `ValueChanged` is fired very often when the user is sliding so I would need to keep checking using a timer if a new thread should be started or not but I am afraid that itself is causing a lot of lag as well and also, since I want to wait long enough for the calculations to be completed, I wonder if a background thread is really that important... – philkark Aug 29 '13 at 13:07
  • @phil13131 Instead of using timers,check the state of thread using `thread.ThreadState == System.Threading.ThreadState.Running`.If the thread is not running create a new thread to do the calculations. – Naren Aug 29 '13 at 13:12
  • That probably is the best solution, thank`s for the idea. If you add it as an answer I will accept it. – philkark Aug 29 '13 at 13:16
0

I might implement this using the Backgroundworker where image processing will be done on Backgroundworker asynchronously.

Also what I will suggest is you can use Timer here and set its tick time to the comfortable value. On every sliderchanged event, you start the timer if it is not enabled. In timer tick event handler you can check if the background worker is working then you can cancel the previous operation and put the new operation on it. In bacgroundworkerdone event handler, just stop the timer.

Thanks

Nitin
  • 18,344
  • 2
  • 36
  • 53
  • Thanks for the response, though I have Feeling that starting a timer every time on `ValueChanged` is not a good solution, since the Event is fired quite often when the user is sliding so you would basically start the timer maybe a little bit too often. Also I don't know if the timer is needed if I want to see if the Backgroundworker is still working.. – philkark Aug 29 '13 at 13:17
  • timer is just to provide the timeinterval till the next processing is done. e.g if i want that i will process image at the interval of 250 ms only if i get continous events. And we will start timer only if it is stopped. – Nitin Aug 29 '13 at 13:21
0

While you could use BindingBase.Delay, this causes a delay even when a single change is required. another approach might be to use a OneWay binding in the Slider Value and use an asynchronous command like so:

XAML code:

<Slider Value="{Binding MyValue, Mode=OneWay}">
    <i:Interaction.Triggers>
     <i:EventTrigger EventName="ValueChanged">
        <mvvmlight:EventToCommand 
        Command="{Binding SetValueCommand, Mode=OneWay}"
        EventArgsConverter="{StaticResource 
        RoutedPropertyChangedEventArgsToDoubleConverter}"
        PassEventArgsToCommand="True" />
        </i:EventTrigger>
     </i:Interaction.Triggers>

Value Converter:

using GalaSoft.MvvmLight.Command;

    public class RoutedPropertyChangedEventArgsToDoubleConverter : IEventArgsConverter
    {
        public object Convert(object value, object parameter)
        {
            var args = (RoutedPropertyChangedEventArgs<double>)value;
            var element = (FrameworkElement)parameter;

            return args.NewValue;
        }
    }

And the callback for the command:

    double _updateVal;
    Task _delay;

    private async void SetValue(double val)
    {            
        if (_delay != null)
        {
            // in case of high frequency updates, most updates will return here
            _updateVal = val; 
            return;
        }

        // only the first update reaches here

        // caluclate the image here 
        MyValue = val; // update slider

        _delay = Task.Delay(500);
        await _delay;

        // in case there are pending updates:
        while (_updateVal.HasValue)
        {
            // caluclate the image here 
            MyValue = _updateVal.Value; // update slider

            _updateVal = null;

            _delay = Task.Delay(500);
            await _delay;
        }

        _delay = null;

    }

This way you only get to reduce the frequency of the image calculations without a significant delay on the first value change.

Elad Maimoni
  • 3,703
  • 3
  • 20
  • 37