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?
Related
I'm using UWP GridView with DataTemplateSelector to show data for different weeks. When I change the week I want to show loader when data is loading. I'm using MvvmLight for ViewModels binding and when I change the data I'm removing and adding elements to the GridView source. The problem is that when I change IsActive property to true before I run UpdateGrid method, the loader is not active and there is a lag on screen. If data loading (UpdateGrid method) takes more than one sec the loader is visible, so it means for me that the logic there is ok, but the problem can be with generating graphical elements on the screen and performance?
I was trying to make my UpdateGrid method async and sync (there is no api call inside, so can be sync). The method is called in the ViewModel class:
DispatcherHelper.CheckBeginInvokeOnUI(async () =>
{
SyncLoadingImageVisible = true;
await UpdateGrid();
SyncLoadingImageVisible = false;
});
You may be misunderstanding the way async/await works. When you mark a method async ans it contains no real await (meaning no I/O bound operation or operation that actually takes place on another thread), the whole method will essentially run synchronously. This is true in your case as well as you mentioned there is no actual async work inside UpdateGrid so the code will work as if there was really no await.
The UI thread will be busy all the time from the moment you set the SyncLoadingImageVisible to true to the moment you set it back to false - during that time UI thread is 100% dedicated to executing your code so user won't see any UI changes. This causes the behavior you are seeing - that there is a lag as the UI thread does not have a chance to update the UI until the UpdateGrid method finishes executing.
To solve this properly you will have to offload performance intensive, non-UI tasks in UpdateGrid method to another thread using awaited Task.Run and only the code that really does work with the app's UI should then be executed on UI thread. This way you will free the UI thread to be able to display progress to the user while the execution runs in the background.
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.
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.
EDIT 1 :
Apparently, i have started to achieve a 3D rendering in WPF environment in an incorrect way. Ofc there is a solution for my question here below, but i suggest to read the update of Sheridan's answer and use his recommendations to achieve this. It's not only secure, but also better for performance. Although it's a bit complex to understand it, but once you understand it, you can start rendering multiple 3D applications in WPF.
Thanks for your assistance Sheridan !
Question ;
i am quite new in WPF, and i want to design a continuous rendering (like in gaming applications) with WPF. I am using multithreading to provide a better UI control (start/stop button fe). Or the event could be disposed due of using an infinite loop to render the 3D world.
But, my problem is that when running the program, i get an Invalid operation was unhandled error. The issue is that there is an object which is a property of the main thread, thus the new thread might not access it.
from XAML file,
<Grid>
<!-- ui controls omitted ... -->
<Viewport3D Name="myViewport" ClipToBounds="True">
<!-- all inits, camera, pos, ... -->
</Viewport3D>
</Grid>
in main class;
/// <summary>this method is done to render the 3D app in other thread.</summary>
private void Runtime(Viewport3D vp) {
System.Diagnostics.Debug.WriteLine("runtime ");
Render3D r3d = new Render3D(vp);
// actual startup
while (keepRunning) {
r3d.Init3D();
}
}
/// <summary>this method toggles the game runtime</summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void StartOrStop(object sender, RoutedEventArgs e) {
keepRunning = !keepRunning;
if (keepRunning) {
buttonStartStop.Content = "Stop";
// thread
t1 = new Thread( () => Runtime(myViewport) );
t1.Start();
}
else {
buttonStartStop.Content = "Start";
t1.Abort();
}
}
The 3DViewport object is initialized in the XAML file. that's why i am passing it to the new thread, that it can create an object which is using that 3DViewport class.
Here below is a sample of the Render3D class.
// constructor
internal Render3D(Viewport3D v) {
currViewport = v;
}
/// <summary>get called in loops to render gfx</summary>
internal void Init3D() {
// clear rendered view
ClearRenderWindow();
// add landscape
AddLandScape();
}
/// <summary>clear window to re-render gfx</summary>
private void ClearRenderWindow() {
ModelVisual3D mv;
// ***** error got caught here below ******
for (int i = currViewport.Children.Count - 1; i >= 0; i--) {
mv = (ModelVisual3D)currViewport.Children[i];
if (mv.Content is DirectionalLight == false) currViewport.Children.Remove(mv);
}
}
The error is caught at the currViewport.Children.Count method. As previously said, the issue is that the current thread doesn't have ownership of that object. It's the first time in my multithreading experience to face with this issue. I have searched around, but couldn't find a solution.
Does anyone know how to pass the ownership of the Viewport3D-object, or a good workaround ?
Firstly, I would like to say that WPF is not a good framework for developing all but the simplest games... I would advise using a gaming framework like Microsoft's XNA instead.
However, if you insist on using WPF, then I would like to bring the CompositionTarget.Rendering event to your attention. It basically uses the frame rate of the host computer to render regulates graphics passes, avoiding the need to use timers.
You should also take a look at the How to: Render on a Per Frame Interval Using CompositionTarget page at MSDN for more helpful information and code examples.
Also, please read this extract from the book 'WPF Control Development Unleashed: Building Advanced User Experiences':
Some readers may recognize a similarity between this approach and higher-end graphics
subsystems like DirectX. Do not mistake CompositionTarget.Rendering for a good injection
point to create a WPF-based gaming graphics engine. High-end graphics and ultrahigh
frame rates are not the goal of this particular aspect of WPF animation.
Similar to the DispatcherTimer approach, animations based on CompositionTarget.Rendering
are also not time-bound. However, these events are synced with the render thread resulting
in smoother animations than the DispatcherTimer. Also there is no need to start and
stop a timer, although you may have to detach and attach the event handler to improve
performance.
UPDATE >>>
Having discovered that this is just for a course project, I would ignore my previous comment and your code example so far. Don't try to create a new rendering system when there is one already. Instead, you should follow this approach:
Create data objects that implement the INotifyPropertyChanged interface and have X, Y, and DirectionVector (could be a Size struct) public properties.
Add a Move method (or Swim method for your Fish class) in which you update the data objects' X and Y properties dependant on the value of the DirectionVector property.
Add a ListBox control to your UI.
Create a collection property to hold your data objects, add items and bind the collection to the ListBox.ItemsSource property.
Create a DataTemplate to define what your Fish objects look like... you can use the Path class to draw them and even use a RotateTransform to rotate them (the angle can be calculated from the DirectionVector property). In the DataTemplate, you can bind the X and Y properties to the `Margin' property.
Finally, add an infinite loop (possibly with a break out option) and in that loop, iterate through the collection of data objects and call Move() on each one. This will update the data objects' positions in the ListBox.
As a general rule, the only objects that can change thread allegiance in WPF are those that derive from Freezable. (E.g., Model3D is a freezable, and so, therefore, are things like Light and GeometryModel3D.)
Elements that participate directly in the visual tree do not derive from Freezable. They derive from Visual (usually, although not always, via FrameworkElement). Consequently, visual elements are forever associated with the thread on which you created them. Freezables are usually descriptive items that tell visual tree elements what to do. For example, brushes (whether solid, gradient fill, image brush or whatever) are freezables, but to do something with a brush you need to use it as a property of some visual element (i.e. something that's not a freezable) e.g., the Fill of a Rectangle.
So Model3D falls into this category - it is a description of a 3D model, but it doesn't actually know how to render itself. You provide this description to some visual element that does know how to render the model (e.g. Viewport3D).
So it's possible to build up Model3D on a worker thread, and then pass that over to the UI thread.
However, you can only start using a freezable object from some thread other than the one on which it was created after you have frozen it by calling Freeze. This, as the name suggests, prevents further modification. Once a freezable is frozen, it is no longer associated with any particular thread, so you can use it from whatever thread you like.
The intended usage model here is:
Build something complicated on a worker thread
Freeze it
Attach it to something that knows how to render it in the UI thread
This might be appropriate if you wanted to build a complex Model3D that was going to take a long time to construct, and you didn't want to make the application unresponsive while that was happening.
However, this is not of any use if you need the model to be modifiable as time goes on. If that's what you need (and it sounds like it is) then you tend to have no choice but to create the model on the UI thread - if you create a freezable that you never actually freeze (because you need the ability to change it) then you have to create it on the same thread that will render it. When you want to update the model, you either need to ensure updates are done on the UI thread, or you could use data binding, which is able to handle change notification events on any thread, and it will marshal those to the UI thread for you.
However, I'm wondering whether you really need multithreading at all. You give the reason as
to provide a better UI control (start/stop button fe).
That's not really a reason to use a separate thread. There's nothing stopping the UI thread from performing updates to the model and also responding to UI input. You just need to make sure the code that updates the model regularly returns control to the event loop.
The only reason for using a separate thread would be if the calculations that determine what the updates to the model should be are computationally expensive. For example, if you're writing code that performs a complex and highly detailed simulation of some process, and then renders the result, it might make sense to perform the calculations on a worker thread to enable the UI to remain responsive. But even then, once those calculations are complete, you'd need to ensure that the updates you make to the model based on the results of those calculations are done on the UI thread, not the worker thread.
It may be worth considering whether you can get away with building a new model each time round. The user might not actually notice if you drop the old model and instantly replace it with a newly built model. That can enable you to build the entire model on the worker thread, because you can then freeze it. If you build a new model each time, it's safe to freeze, because any time you want to change something, you just build a new model instead of updating the old one.
Yet another variation is to have a model that consists mostly of frozen pieces, contained by some unfrozen top-level elements.
Is there a specific time in the page's lifecycle that the Map.SetView() function should be called? In our app we use this on various map objects and it seems to work randomly, sometimes perfectly and sometimes with no effect but also no exception.
example code:
RouteMap.SetView(LocationRectangle.CreateBoundingRectangle(DirectionCoordinates));
Where RouteMap is the mapping component and DirectionCoordinates contains the start/end coordinates for the map.
I can see that the bounding box is being created properly, but the map's positioning is not always being affected even loading the same data. If I add a break point it does seem to work, so I was assuming it had something to do with the map loading, but adding the SetView() functionality to the Loaded event has the same issue. We currently process the map information in the page Loaded event.
Update
I've been testing more and added events to what I could, I know for a fact that the MapLoaded event is being called before SetView. After SetView is called, it is working sometimes and not others. Neither ViewChanging or ViewChanged events are called.
This is obviously not the best solution, but there must be something that is not quite finished loading when the Loaded event is called that is preventing this from finishing.
I added a 100ms sleep to the Map_Loaded event and it has solved the problem I was having.
System.Threading.Thread.Sleep(500);
update
100ms isn't working for some people, you may want to play around with the numbers, 200, 500 etc. It's still a very short delay on the map's load time. I have contacted Microsoft about this and they have told me that they are looking into the issue and we will hopefully have some sort of response from them shortly.
update and edit
Use the following code instead to prevent UI hanging:
await Task.Delay(250);
I tackled this issue using ResolveCompleted event and boolean flag.
private void Map_ResolveCompleted(object sender, MapResolveCompletedEventArgs e)
{
if (this.zoomReq)
{
this.zoomReq = false;
if (this.locationList != null && this.locationList.Count > 0)
{
var viewRect = LocationRectangle.CreateBoundingRectangle(this.locationList);
this.Map.SetView(viewRect);
}
}
}
There is noticeable pause before map zooms but at least this seems to work all the time. The flag is needed because ResolveCompleted is fired every time the map moves.
I was both constructing a map layer (Microsoft.Phone.Maps.Controls.MapLayer) and setting the view (public void SetView(LocationRectangle boundingRectangle);) in an async method:
public async Task CreateMap()
{
map.Add(mapLayer);
map.SetView(locationRectangle);
}
I was doing some loading, that's why I used async.
This would only set the view once, the first time I navigated to the page.
The solution was to dispatch the set view call:
public async Task CreateMap()
{
map.Add(mapLayer);
Dispatcher.BeginInvoke(() =>
{
map.SetView(locationRectangle);
});
}
Hope that helps.
The Loaded event is the proper place for SetView(). You could try creating your rectangle in you OnNavigatedTo method. When I'm working with locations I always start my watcher in OnNavigatedTo and work with any map layers in _Loaded.
I worked myself some time at this problem. It didn't help to put most of the stuff to load into the constructor of the page. I tried to the trick with System.Threading.Thread.Sleep(500) but it took far beyond 500ms to take effect and this wasn't acceptable for me. For some people it helped to trigger an ZoomLevelChanged event and set the view in it. For myself I used a DispatcherTimer in which I used SetView() and fired an `ViewChanging´ event to stop the timer. If you use an animation the difference is pretty small.
I had this problem for MapAnimationKind.Linear but for MapAnimationKind.None it works without any problem
map.SetView(LocationRectangle.CreateBoundingRectangle(...), MapAnimationKind.None);
I had a very similar problem. Basically the setview of map would work the first time a page loaded (i.e. after all the data had finished loading) but if I left the page and came back and did not need to reload all the data, it did not work. While debugging, it seemed like I was setting the information for the map before it was finished loading.
So what I did to resolve the challenge was:
In the XAML - I added an event handler for the Loaded event of the map.
Example: Loaded="myMap_Loaded"
In the myMap_Loaded event, I simply called an async method to wait for the data to load then map
it.
Example:
private void myMap_Loaded(object sender, RoutedEventArgs e)
{
WaitAndLoadMap();
}
Coded the WaitAndLoadMap method to wait for the data to finish loading before loading the
map.
private async void WaitAndLoadMap()
{
//Check if the data is loaded and if it is not - loop.
while (!App.NearbyLocationsViewModel.IsLocationDataLoaded)
await Task.Delay(250);
//Load the map content and set the mapview.
}
It seems to be working. Hope this helps others.