Frequency of WPF-slider ValueChanged-Event - c#

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.

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.

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();
}

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

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.

Related

Trigger heavy task on view model's property change

I'm creating a video/frame editor and I wonder what would be the best approach for triggering a render cycle whenever the user scrubs (manually navigate to specific time) or playback is triggered.
Render is done using a WriteableBitmap back buffer, so I always need to mark the frame as dirty (in the UI thread) when the render cycle is complete.
Initially I thought about triggering render on a TimeSpan property changed, but that's doesn't look right as it may be too heavy of a task and it would hang the UI.
public TimeSpan CurrentTime
{
get => _currentTime;
set
{
_currentTime = value;
//Render frame animation based on current time.
Render();
}
}
My goals are:
Don't freeze the UI.
Drop frames if the rendering takes too much to complete or the current time is switched too fast.
Less delay possible to render when switching current time.
What's the most used (or better) approach in this case?
Maybe trigger a command?

How to correctly pause/delay Windows Forms application

I am a beginner to the OOP and the C#.
I am working on a quiz game using the Windows Forms.
My problem is related to two classes, the form and the game logic.
I have a basic UI with classic Froms controls. Take a look.
The thing I want to achieve is, when a player presses any answer button, it will higlight that pressed button by red or green color, depending on if it is right or wrong answer. After changing the color I want the program to wait for a while and then go to the next question.
Probelm is, that I don´t know how to achieve this correctly. I don´t know how to work with threads and how exactly the Form app works related to threads. Should I use a thread sleep or a timer or a async?
I will show you the method in game logic class which should handle this.
public static void Play(char answer) //Method gets a char representing a palyer answer
{
if (_rightAnswer == answer) //If the answer is true, the button should become green
{
Program.MainWindow.ChangeBtnColor(answer, System.Drawing.Color.LightGreen);
_score++;
}
else //Otherwise the button becomes Red
{
Program.MainWindow.ChangeBtnColor(answer, System.Drawing.Color.Red);
}
//SLEEP HERE
if (!(_currentIndex < _maxIndex)) //If it is the last question, show game over
{
Program.MainWindow.DisplayGameOver(_score);
}
else //If it is not the last question, load next question and dispaly it and finally change the button color to default
{
_currentIndex++;
_currentQuestion = Database.ListOfQuestions.ElementAt(_currentIndex);
_rightAnswer = _currentQuestion.RightAnswer;
Program.MainWindow.DisplayStats(_score, _currentIndex + 1, _maxIndex + 1);
Program.MainWindow.DisplayQuestion(_currentQuestion.Text);
Program.MainWindow.DisplayChoices(_currentQuestion.Choices);
}
Program.MainWindow.ChangeBtnColor(answer, System.Drawing.SystemColors.ControlLight);
}
I don´t want to completely block the UI but also I don´t want users to make other events by pressing other buttons during the pause. Because it will result in improper run of app.
If the program is really simple and you do not want to implement Threads I would suggest using Timer. Just start your Timer when clicking answer button. Your timer should contain function which would stop itself after some time and do other actions needed (e.g. pick another question).
Once the user has selected an answer you can disable all the buttons so they can't press anything else.
Then start a timer so you don't block the UI. The timer is basically a thread but handles all the threading for you so you don't have to worry about that aspect.
When the timer reaches the desired delay stop it and fire an event to select the next question.
At //SLEEP HERE add this line of code
Timer timer = new Timer(new TimerCallback(timerCb), null, 2000, 0);
The 2000 is milliseconds and is the wait time, timerCb is a call back method.
Also under that disable all your buttons so new events wont be generated.
private void timerCb(object state)
{
Dispatcher.Invoke(() =>
{
label1.Content = "Foo!";
});
}
You can do whatever you want in the callback, however if you do something that would change anything in the UI, you need to use the Dispatcher like I have changing the label content.
Suspending execution in a GUI scenario is very easy thanks to await:
await Task.Delay(2000);
This does not block the UI.
You should research what await does and how to use it. If you have never heard about it and are programming WinForms you are doing something wrong.
No timers or threads are needed. No callbacks, no Invoke.

Setting Visibility property does not show control

I am having problems with visibility of the spinner control.
The control itself works... If I set it to visible right after Initialize it shows and animates as expected.
But if I try to show it from the code it never gets drawn...
the .cs file (presenter)
private void SaveDocument(Document aDocument)
{
if (AllowFlag != null)
{
this.View.ShowDocumentProgressSpinner(true);
this.Save(aDocument);
this.View.ShowDocumentProgressSpinner(false);
}
}
the xaml.cs file
void IDocumentView.ShowDocumentProgressSpinner(bool show)
{
if (show)
{
this.DocumentProgressSpinner.Visibility = Visibility.Visible;
}
else
{
this.DocumentProgressSpinner.Visibility = Visibility.Hidden;
}
}
If i set the visibility to visible right after initialize the spinner works!
part of the xaml of the main control (the spinner is custom control)
...
<Viewbox Grid.Row="3" Width="30" Height="30"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<my:DocumentProgressSpinnerView x:Name="DocumentProgressSpinner" />
...
Probably another threading problem, but I have also tried:
Thread.CurrentThread == Dispatcher.CurrentDispatcher.Thread
TRUE
Dispatcher.FromThread(Thread.CurrentThread).CheckAccess()
TRUE
The control gets invoked, because the "windows spinner" gets activated, just the control never gets shown...
The problem is that you are running your save operation on the dispatcher thread and during the save operation the dispatch thread is blocked the whole time. It's only after your save operation has finished that the UI is updated and thus you will never see the "waiting" state. Instead you should spin off a new thread and from within the event dispatch and set the wait indicator to visible. In the separate thread perform the save operation and once the saving is done, use the dispatcher to hide the wait indicator again on the Dispatcher thread.
See this answer for more details on how to implement this.

Wait until UIElement is drawn (without updatelayout)

I have a function that takes around 5 seconds to complete if a button is clicked. If the button is clicked, I want to display some kind of notification to indicate that the buttonclick is being processed, something like
<Button Click="OnButtonClick" Content="Process Input" />
<Border x:Name="NotificationBorder" Opacity="0" IsHitTestVisible="False"
Width="500" Height="100" Background="White">
<TextBlock Text="Your input is being processed" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
And in code-behind on the button click:
private void OnButtonClick(Object sender, RoutedEventArgs e)
{
DoubleAnimation da = new DoubleAnimation
{
From = 5,
To = 0,
Duration = TimeSpan.FromSeconds(2.5),
};
// Making border visible in hopes that it's drawn before animation kicks in
NotificationBorder.Opacity = 1;
da.Completed += (o, args) => NotificationBorder.Opacity = 0;
NotificationBorder.UpdateLayout(); //Doesn't do anything
UpdateLayout(); // Doesn't do anything
NotificationBorder.BeginAnimation(OpacityProperty, da);
// Simulate calculationheavy functioncall
Thread.Sleep(5000);
}
Somehow UpdateLayout() isn't rendering fast enough, the notification is only displayed after the 5 seconds of Thread.Sleep are over.
Dispatcher.Invoke((Action)(() => NotificationBorder.Opacity = 1), DispatcherPriority.Render); won't work either.
Additionally, I can't let Thread.Sleep run in a separate worker thread - In the real application, it needs to read data from Dispatcher-owned objects and (re)build parts of the UI.
Is there a way to make it visible before Thread.Sleep() is called?
The problem is any long activity on main thread (UI thread) will lead freeze in UI, so you will not see any animation.
You need to do your calculations in a separate thread. I you need to access to Dispatcher-owned objects you can use uiObject.Dispatcher.BeginInvoke or Invoke. BackgroundWorker can help you the way to execute some long calculations and subscribe to the event Completed which will be fired by BackgroundWorker on the UI thread automatically.
If you are ok with UI freeze, change some property and other stuff re-schedule with Dispatcher.BeginInvoke(DispatcherPriority.Background,...), like
NotificationBorder.Opacity = 1;
Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(()=>Thread.Sleep(5000)));
If you want the UI to update, you need to change your long-running operation to be asynchronous, which means properly decoupling the UI dependencies from whatever logic or perhaps IO-bound operations are taking all the time. If you don't do this, none of the UI updates occurring in your long-running operation will actually be reflected in the UI until it's done, anyway.
If you're running with .NET 4.5, here is my preferred pattern for how to do this:
public async Task DoComplexStuff()
{
//Grab values from UI objects that must be accessed on UI thread
//Run some calculations in the background that don't depend on the UI
var result = await Task.Run((Func<string>)DoComplexStuffInBackground);
//Update UI based on results
//Possibly do more stuff in the background, etc.
}
private string DoComplexStuffInBackground()
{
return "Stuff";
}
This is somewhat 'backwards' from the older style BackgroundWorker approach, but I find it tends to result in cleaner logic. Instead of writing your background code, with explicit Invokes for UI updates, you write your UI code around explicit background work calls via Task.Run(). This also tends to lead to a natural separation between the UI-centric code and the underlying logic code, as you need to refactor your logic into methods that can be called in this way. That's a good idea for long-term maintainability anyway.
So you can dispatch both action (UI and your calculations) into the dispatcher and they will be executed in a line
Dispatcher.BeginInvoke(do ui things);
Dispatcher.BeginInvoke(do your logic things);
But you'd better move your calculation into the background thread.

Maintaining tab order when controls are disabled and enabled again at unpredictable times

So let me warn you; I am asking for a way to make a total hack work somewhat better. I admit that it is a hack and am certainly open to different takes on the problem as a whole. That said, I need to get this in soon if I want to make code cutoff and we have a somewhat aggressive release date.
As such, I will not be able to make large changes immediately, but I can easily do so for the first patch to this software. So, short and long term solutions are welcome, but a short term solution (if possible) is preferable.
Ok, so here is the issue; I have an application that send commands to a robotic hardware device. After a command is sent that requires a wait (for example, a physical motion that takes an unknown amount of time) the UI goes into a "Busy State". When this occurs all controls that would issue another blocking command are disabled as the machine cannot process them.
When the UI comes out of a busy state all controls are once again enabled, but the tab order is not maintained for obvious reasons. This makes tabbing through input areas impossible and, seeing as I myself use the keyboard almost exclusively, is not acceptable. My current scheme to "solve" this problem is to:
At startup, register to the GotFocus event for each control of interest in the application. This is made difficult due to the fact that this is a WPF MVVM app and also because some controls are created dynamically. Nevertheless, I can search the visual and/or logical trees and get by...
In the GotFocus event, save a reference to the control.
When we exit a busy state, attempt to set focus to the saved control.
This works... kinda sorta. The issue at hand (as I can think of more fail scenarios...) is that this logic will blow away a context menu if it was open and another. Consider:
Focus is in a text area.
User right clicks another control. This control does not get focus (even if I try to set it in a mouse handler).
System goes into a busy state as the right click performed a move.
When the busy state ends, text area is given focus and the context menu closes.
(Now I realize that you may say that performing a move on a right click and also displaying a context menu is a bad idea, but the context menu commands are non-blocking, enabled, and it has a domain specific use that is convenient.)
So there it is. I can't even get focus in a right click, and setting focus to the menu itself doesn't work either. I'm just curious if anyone has a better scheme for something like this. I realize it is awkward and a very narrow circumstance. Thanks for any help you can offer in advance, I will be playing around with this some more...
Interesting question and sadly i cannot think of an immediate solution to the problem, i would try to work around the issue by not blocking the application at all if this is such a frequent thing.
If the roboter can only take one command at a time it might work to just implement a command-queue so that the interface can still be used and while the roboter is busy issued commands get deferred. In terms of usability it might be a good idea to make this queue very visible so it is apparent that the command has been issued and will be taken into account.
How about instead of actually disabling controls you just do a check when they are invoked if they should send the command or not in the beginning of their event handlers. eg:
if(!bControlsEnabled) { return; }
You could also change the controls style when they should be "disabled".
Implement a custom behavior that listens when your control's enabled changes, saves the focused element and refocuses that element when enabled changes back to true:
public class KeyboardFocus
{
public static bool GetReturnFocusAfterEnable(DependencyObject obj)
{
return (bool)obj.GetValue(ReturnFocusAfterEnableProperty);
}
public static void SetReturnFocusAfterEnable(DependencyObject obj, bool value)
{
obj.SetValue(ReturnFocusAfterEnableProperty, value);
}
private static Dictionary<object, IInputElement> values = new Dictionary<object, IInputElement>();
// Using a DependencyProperty as the backing store for ReturnFocusAfterEnable. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ReturnFocusAfterEnableProperty =
DependencyProperty.RegisterAttached("ReturnFocusAfterEnable", typeof(bool), typeof(KeyboardFocus), new UIPropertyMetadata(false, PropertyChangedCallback));
static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
UIElement element = d as UIElement;
if (element != null)
{
element.IsEnabledChanged += (element_IsEnabledChanged);
}
}
else
{
UIElement element = d as UIElement;
if (element != null)
{
element.IsEnabledChanged -= (element_IsEnabledChanged);
}
}
}
static void element_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
if (values.ContainsKey(sender))
{
Keyboard.Focus(values[sender]);
values.Remove(sender);
}
}
else
{
values[sender] = Keyboard.FocusedElement;
}
}
}
This could then be implemented in XAML as such:
<Grid Name="layout" local:KeyboardFocus.ReturnFocusAfterEnable="True">
This works in my test setup, but if you press a button before disabling your stuff, that button would have keyboard focus at the time the thing gets disabled, and this fails.
This solution is independant of your architecture, and does not require code in the form. It's a bit quick and dirty but it does the job.
Workaround for this :-Instead of disabling the control You should use XamBusyIndicator as a parent of that control and set the property for IsBusy. If IsBusy is true it will disable the child control and also TabIndex property will run in a sensible manner.
For example:-
<igComboEditor:XamBusyIndicator IsBusy="{Binding IsBusy}" Height="22">
<igComboEditor:XamBusyIndicator.Animation>
<igComboEditor:CustomBusyAnimation DataTemplate="{StaticResource CustomBusyIndicatorTemplate}" />
</igComboEditor:XamBusyIndicator.Animation>
<igComboEditor:XamComboEditor Width="125" VerticalAlignment="Bottom" Style="{StaticResource XamComboEditorStyle}"
ItemsSource="{Binding DataList}" IsEditable="False"
SelectedItem="{Binding SelectedData}">
</igComboEditor:XamComboEditor>
</igComboEditor:XamBusyIndicator>
I would
OnCommandSentThatRequiresAWait Event Call SaveStateAndDisableUI()
SaveStateAndDisableUI()
{
Foreach control in controlsCollection
{
switch(controlType)
{
for each control extract and save all you need.
if it's a menu check if it is opened up and so on,
does control have focus,
where is the caret located in case of textbox etc
}
}
save position of mouse x,y
save position of form, state like if it is minimized, maximized
}
Corresponding RestoreState() should restore everything back up.

Categories