I am currently working on a Level Editor for XNA which is built by combining Windows Forms and XNA.
I am using stuff from the example on App Hub link text, but I have made some changes, so I have some ViewportController class which manages all the viewport controls, instead of the form manages them directly.
The problem is that when I need to update the Draw method I have to shake the window.
Anybody knows how to fix this, so the Draw will update realtime?
Not sure what a ViewportController might be. However, the Draw() method is triggered by a Windows paint request. That runs the Control.OnPaint() method, there is an override for it in the GraphicsDeviceControl class which causes Draw() to run.
The sample SpinningTriangleControl shows how you'd get a control to redraw itself repeatedly, what you'd need to get it animated:
// Hook the idle event to constantly redraw our animation.
Application.Idle += delegate { Invalidate(); };
Note the Control.Invalidate() method call, that's what eventually causes the OnPaint() method to run. Using the Idle event ensures this is done over and over again but only if the main thread isn't otherwise busy with anything. This isn't necessary for controls that show only static content, like the sample SpriteFontControl. Another way to do it is to use a Timer with a short Interval, its Tick event handler can call Invalidate().
Related
I'm overriding WPF's OnRender to draw complex graphics. This may rarely take a long time. I would like to indicate to the user that the app did not crash, but is "merely" taking a long time to render.
How would I do that? It seems not possible to modify the UI in any way during the OnRender call.
You are talking about placing a "Busy Indicator" on top of your drawing.
Just place a half transparent grid on top with the writing "Loading..."
and you're done.
Bind its visibility to some bool that you update once when entering the complex rendering, and once when finished.
Or put a spinning Ellipse with gradient background if you want to see motion..
Since the OnRender method will execute on the UI thread you cannot do anything else such as displaying an interactive ProgressBar or respond to user input during the execution of your code.
What you can do is to pop up a new window that launches in a separate thread and display this one right before you begin to draw your complex graphics.
Please refer to my answer in the following similar question for more information about this.
Wait screen during rendering UIElement in WPF
Once the OnRender method has returned you then close the temporary "loading" window:
Can I stop WPF from executing UI actions out of the order in my code
A custom WPF Control overrides OnRender. The method generates and displays a Path from custom data. The data provider is bound using a Dependency Property. The Dependency Property registers for an event when data changed. This event in turn calls InvalidateVisual().
However, after the InvalidateVisual() the OnRender is not always called.
We use the Prism Framework and the Region functionallity. The Control in question is embedded in such a Region, which is activated and deactivated. However, the Control's property "IsVisible" is true whenever the region is active. But still, when calling InvalidateVisual() the OnRender method is not called...
What could prevent the OnRender method from being called?
I just had this problem, too.
Context
I've got a load of controls based on the DynamicDataDisplay graph components inside a VirtualizingStackPanel (inside a ListBox).
When there are more controls that are visible at once, but not enough for the VirtualizingStackPanel to start re-using them when you scroll then I see this issue with the D3 AxisControl class. For some reason it does a lot of work in it's OnRender method, which it tries to trigger by calling InvalidateVisual when something changes.
In the problem case the problem controls call InvalidateVisual but they never get a call to MeasureOverride, ArrangeOverride or OnRender. Interestingly, most of the controls still work, in one particular problem case I get the last 3 out of a set of 11 failing to work properly. Notably those 3 (and only those 3) receive a call to MeasureOverride immediately before the data binding update that triggers the call to InvalidateVisual.
My Fix
In the end I managed to fix it by adding a call to InvalidateMeasure alongside the call to InvalidateVisual.
It's a horrible solution, but it's not a performance critical part of our application, so I seem to be getting away with it.
If the size of your control is staying the same, you should not be using InvalidateMeasure() or InvalidateVisual() because they trigger an expensive re-layout.
WPF is a retained drawing system. OnRender() might be better called AccumulateDrawingObjects(), because it doesn't actually draw. It accumulates a set of drawing objects which WPF uses to draw your UI whenever it wants. The magic thing is, if you put a DrawingGroup into the DrawingContext during OnRender(), you can actually efficiently update it after OnRender, anytime you like.
See my answer here for more details..
https://stackoverflow.com/a/44426783/519568
I just had this problem, too.
I had a scrollbar for a control which only figured out during OnRender() how much space is really needed to display all content, which could be bigger than the available display space and therefor needed a scrollbar. It could happen that OnRender() called some methods which ultimately changed the value of the scrollbar which was supposed to start OnRender() with InvalidateVisual().
However, OnRender() did not get called again after InvalidateVisual(). I guess the reason is that InvalidateVisual() sets some flags which tells WPF that the control needs to get drawn again, but once OnRender() finishes, that flag gets reset. Here some pseudo code how I expect it to happen:
//someCode:
control.InvalidateVisual()
//code of InvalidateVisual()
control.RedrawFlag = true;
//WPF some time later:
if (control.RedrawFlag){
control.OnRender()
//OnRender code
//do some stuff
//decide control needs to be redrawn
//however, RedrawFlag is alreday true!
//next line is not changing anything
control.RedrawFlag = true;
//WPF finished executing control.OnRender
control.RedrawFlag = false;
}
I didn't further investigate if WPF really works this way, but it would explain why OnRender() does not get called a second time.
Instead of wasting even more time, I changed how to calculate the total width of the control content can be and put this code outside of OnRender().
I am a newbie in Windows phone app development. On one of my pages I have a button that does some substantial amount of work. So when I press the button the screen freezes and then resumes when all the work is done. Now I wanted to show some waiting image or page to be shown during this while.
So I created and destroyed an image on top on the page at the start and end of button click action respectively but it still freezes and the screen renders only after all the work is done making it indifferent. I am using Silverlite C#.
You'll want to push the heavy work onto a background thread. What's currently happening is that the work is being done on the UI thread, so it 'freezes' since the UI thread is waiting for the work to be done. There are various ways you can push the work to a background thread. One way would be to use a BackgroundWorker.
A simple example, which doesn't include things like reporting when the work is done, would look like this
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(BW_DoWork);
bw.RunWorkerAsync();
private void BW_DoWork(object sender, DoWorkEventArgs e)
{
//Your heavy work code
}
Important thing to remember when you work on a background thread, is that if you want to change anything on the UI, you need to marshal the data to the UI thread and update it there. You can do that like this:
Deployment.Current.Dispatcher.BeginInvoke(() => {
myTextBlockExample.Text = "Changing the UI";
}
If you tried to change the TextBlock without the Dispatcher.BeginInvoke method, you'll get a Invalid Cross-Thread Access error. If you see that error in WP development, it's likely that you're trying to update a UI element from a non-UI thread.
You can put your code that displays the loading image before calling bw.RunWorkerAsync.
I have a wpf application that takes ~30 seconds to create a map/graphic. I have read there is no easy way to tie into the UI rendering thread to get a progress update. So I was going to use a counter on a value converter which colors my map but that is also on the UI Thread, so my question is has anyone found any slick methods of working with the rendering thread yet?
Thanks.
You could create your map/graphic in a BackgroundWorker which allows you to call ReportProgress in your function, where you can set your percentage of completion and raise the ProgressChanged event to update your UI.
When you say UI rendering thread, you mean that hidden rendering thread from WPF internals or UI thread?
In any case, having a separate thread that builds your map and notifies UI about progress doesn't help you?
im not sure if this is what you are looking for.
I use something similar to the code below to load in around 300 images( about 200 mb ) and have no UI slow down at all. (the user can see each image being loaded in, I just keep an empty placeholder image up till the final image is loaded)
The images are loaded in a background thread, and then the function is called to actually put them into the WPF scene.
here is a simple example using a textbox. You can call this function from any thread and it will work out if it needs to change the to the GUI thread. (for my project of course i am doing it with bitmaps, not a textbox ).
delegate void UpdateUIThreadDelegate(String str);
public void DisplayString(String strMessage)
{
if (this.InvokeRequired)
{
UpdateUIThreadDelegate updateDelegate = DisplayString;
this.BeginInvoke(updateDelegate, strMessage);
return;
}
myTextBox.Text = strMessage;
}
Cheers
Anton
If you use binding to tie your UI with a datasource which can take long time to return, you can set 'IsAsync=True' on your binding so that the binding become asynchronous.
If you want to display some other datas (even an animation I guess) during the time your datasource is loading, you can use a PriorityBinding
HTH
Riana
We've got a Model-View-Presenter setup with our .NET Compact Framework app. A standard CF Form is implementing the view interface, and passed into the constructor of the presenter. the presenter tells the form to show itself by calling view.Run(); the view then does a Show() on itself, and the presenter takes over again, loading up data and populating it into the view.
Problem is that the view does not finishing showing itself before control is returned to the presenter. since the presenter code blocks for a few seconds while loading data, the effect is that the form is not visible for a few seconds. it's not until after the presenter finishes it's data load that the form becomes visible, even though the form called Show() on itself before the presenter started its data loading.
In a standard windows environment, i could use the .Shown event on the form... but compact framework doesn't have this, and I can't find an equivalent.
Does anyone know of an even, a pinvoke, or some other way to get my form to be fully visible before kicking off some code on the presenter? at this point, i'd be ok with the form calling to the presenter and telling the presenter to start it's data load.
FYI - we're trying to avoid multi-threading, to cut down on complexity and resource usage.
The general rule is: never do anything blocking on the UI thread
The UI in Windows (and in Windows CE as well) has an asynchronous nature. Which means that most API calls do not necessarily do whatever they're supposed to do immediately. Instead, they generate a series of events, which are put into the event queue and later retrieved by the event pump, which is, essentially, an infinite loop running on the UI thread, picking events from the queue one by one, and handling them.
From the above, a conclusion can be drawn that if you continue to do something lengthy on the UI thread after requesting a certain action (i.e. showing the window in your case), the event pump cannot proceed with picking events (because you haven't returned control to it), and therefore, your requested action cannot complete.
The general approach is as follows: if you must do complex data transformation/loading/preparing/whatever, do it on a separate thread, and then use Control.BeginInvoke to inject a delegate into the UI thread, and touch the actual UI controls from inside that delegate.
Despite your irrational fear of "complexity" that multithreading brings with it, there is very little to be afraid of. Here's a little example to illustrate the point:
public void ShowUI()
{
theForm = new MyForm();
theForm.Show();
// BeginInvoke() will take a new thread from the thread pool
// and invoke our delegate on that thread
new Action( PrepareData ).BeginInvoke(null,null);
}
public void PrepareData()
{
// Prepare your data, do complex computation, etc.
// Control.BeginInvoke will put our delegate on the UI event queue
// to be retrieved and executed on the UI thread
theForm.BeginInvoke( new Action( PutDataInTheForm ) );
}
public void PutDataInTheForm()
{
theForm.textBox1.Text = "data is ready!";
}
While you may play with alternative solutions, the general idea always remains the same: if you do anything lengthy on the UI thread, your UI will "freeze". It will not even redraw itself as you add new UI elements on the screen, because redrawing is also an asynchronous process.
Therefore, you must do all the complex and long stuff on a separate thread, and only do simple, small, guaranteed to run fast things on the UI thread. There is no other alternative, really.
Hope this helps.
If your key problem is that the form won't paint before your presenter data loading methods are completed, and you have a call to this.Show() in your Form_Load, try putting Application.DoEvents() directly after this.Show() to force/allow the form to paint.
protected void Form_Load(blah blah blah)
{
this.Show();
Application.DoEvents();
... data loading methods ...
}
No need to create another thread if you don't want to (although a couple of seconds have to be dealt with somehow).
You can use the activated event. Because it will fire when the form is activated, you need a boolean local to the form to check wether or not the form has been created for the first time.
Another option for you is to disconnect the event handler right after you finish presenting the form.