SharpGL - Entering Active Rendering Loop - c#

I've successfully setup my frame with the OpenGLControl and delegated my render method to the OpenGLDraw event of it. However, I'd like to do game programming so I want to enter into an active rendering loop where I'd update the game state and render, both from another thread but can't figure it to do how.

Not positive, but if it works the same as most other systems you will need to make sure that all the OpenGL calls come from the thread, past that you can put your logic and everything else is however mean and whichever threads you want. Hope that helps!

Got it working by not calling Application.Run and instead using P/Invoke to create a C++ styled MessagePump.

Have you tried using the Winforms Application.Idle event?
Example (assuming you have a standard static class Program):
In Main - Application.Idle += Tick
static void Tick(object sender, EventArgs e)
{
//Insert rendering/updating code here
}

Related

BackgroundWorker to fill a DataGridView

EDIT: Solved using this: http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/
In my project (.net/windows forms) I'm filling a DataGridView with a large DataTable. Filling can take up to 20 seconds, so I'd like an animated loading window. This animation freezes if the thread is busy, so I'll have to use a new thread either for the window or for filling the DataGridView.
I've tried using the BackgroundWorker to show the form, but it'll be a blank white window in the correct shape.
I've also tried using the BackgroundWorker to fill the DataGridView, but it'll throw an error saying the DataGridView is being accessed by a different thread than the one it has been created for. Since the DataGridView is being created in the designer class, I can't just create it in the new thread - plus that solution doesn't sound very elegant.
What's the best way for me to show a working animated form while filling the DataGridView?
Edit: The answer didn't solve the issue for me, so I've tried to break the code down to something that can be presented here. I didn't do it before because it didn't really seem relevant enough to work through some 1k lines of code. Might be something missing or some remnants from previous experiments in the code presented here. Please ignore bad naming of function, that's a legacy thing I'll fix once I got it working. Parts of the code are ancient.
I've made it run without errors, but the frmLoading still isn't animated (it is if I keep it alive while the working thread isn't busy, though).
namespace a
{
public partial class frmMain : DockContent, IPlugin
{
//...
private delegate void SafeCallDelegate(DataTable dt);
private Thread thread1 = null;
private frmLoading frmLoading = new frmLoading();
public frmMain()
{
//...
}
//...
private void FillDataGrid(DataTable dt)
{
if(this.InvokeRequired)
{
var d = new SafeCallDelegate(FillDataGrid);
Invoke(d, new object[] { dt });
}
else
{
//...
DataGridFiller(dt);
}
}
private void DataGridFiller(DataTable dt)
{
BindingSource dataSource = new BindingSource(dt, null);
//...
dgvData.DataSource = dataSource;
//...
frmLoading.Hide();
}
private void btnGetData_Click(object sender, EventArgs e)
{
DataTable dt = [...];
// Wenn Daten vorhanden sind, dann anzeigen
if (dt != null)
{
//...
frmLoading.Show();
thread1 = new Thread(() => FillDataGrid(dt));
thread1.Start();
}
}
}
}
The second approach is the correct one: use a BackgroundWorker to do any work that will freeze the UI if done in the main thread. About the exception you're getting, that's because the call you're making isn't thread-safe (Because the control was created on a different thread that the one whos calling its methods). Plase take a look at this link to MSDN to understand how to make this kind of call between threads, using backgroundWorker's event-driven model.
From the link:
There are two ways to safely call a Windows Forms control from a
thread that didn't create that control. You can use the
System.Windows.Forms.Control.Invoke method to call a delegate created
in the main thread, which in turn calls the control. Or, you can
implement a System.ComponentModel.BackgroundWorker, which uses an
event-driven model to separate work done in the background thread from
reporting on the results.
Take a look at the second example which demonstrates this technique using the backgroundWorker
EDIT
From your comments I get that the real problem here is size. The problem is that the thread that owns the DataGridView control, is the thread that is displaying it, so no matter how, if you load all the data at once it will freeze for the time it takes this thread to draw all that data on the screen. Fortunately you're not the first that had this problem, and microsoft got you covered this time. This is, I think, a great example of what the XY problem is, the real problem is that you want to load a huge dataset (X) and the answer on how to do that is here, but instead you asked how to display a loading icon while filling your datagrid whithout the UI freezing (Y), which was your attempted solution.
This was from a technical perspective but, from an UI/UX perspective I would like you to think about the actuall use of the form. Do you really need all that data loaded every time you visit this section? If the answer is no, maybe you could think about implementing some pagination (example here). Also 200 colums means horizontal scroll even for an ultrawide monitor. I can't really figure out the user case for you to need all that information on a list/table view at once. I think that maybe implemeting a Master-Detail kind of interface could be more useful.
EDIT 2.0
I think that you can try to create a whole new Form window in another thread, place it over your datagrid and paint the loading animation there. Meanwhile in the main window you can draw the grid without its painting making the loading animation freeze (the other thread is taking care of the drawing). You may want to hold a reference to the thread and kill it, or maybe better try to hold a reference to the From and close it more gracefully.
I've never done this but, I think I recall from some legacy application doing something like that, and it was ugly.
Use an asynchronous task that displays the pop-up loading window until the DataGridView is filled up.
Here's a link to a write-up Microsoft made for async programming: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

Picturebox tick event behaving inconsistently

So recently i have been trying to get into C# programming after spending alot of time in C.
Naturally i jumped right into learning a few new things, in this case i wanted to try some forms, classes and events - simple right?
Well apparently not, i am rather dumbfounded, i have been running the program, analyzing step by step, the tick event triggers as its supposed to, but the picture in the picturebox is not updated.
And here is the kicker, i made a button that runs almost identical code, and that works, what gives? i can barely get my head around it as it is.
http://pastebin.com/psYzQSLE - here is the code i am running currently.
And here is the specific segment of code i cant get to behave.
private void timer1_Tick(object sender, EventArgs e)
{
if(swapper)
{
swapper = false;
pictureBox1.Image = ima1;
pictureBox1.Refresh();
}
else
{
swapper = true;
pictureBox1.Image = ima2;
pictureBox1.Refresh();
}
}
Your code looks fine, and is working for me. I suspect the Form1_Load method is not hooked up to the form's Loaded event; you can check that in the designer.
Okay, i figured it out!
Being unfamilar with the design document, the timer was added to the tick in there and i also added it myself in my code, the result being a timer that triggers twice immediately, and thus i saw no result.
Problem is solved now, thanks for the warm welcome :)

Delay loading of combobox when form loads

I've got a Windows Forms (C#) project with multiple comboboxes/listboxes etc that are populated when the form loads.
The problem is that the loading of the comboboxes/listboxes is slow, and since the loading is done when the form is trying to display the entire form isn't shown until all the controls have been populated. This can in some circumstances be 20+ seconds.
Had there been a Form_finished_loaded type of event I could have put my code in there, but I can't find an event that is fired after the form is done drawing the basic controls.
I have one requirement though - the loading has to be done in the main thread (since I get the items from a non-threading friendly COM-application).
I have found one potential solution, but perhaps there is a better way?
I can create a System.Timer.Timer when creating the form, and have the first Tick be called about 1 second later, and then populate the lists from that tick. That gives the form enough time to be displayed before it starts filling the lists.
Does anyone have any other tips on how to delay the loading of the controls?
There is the Shown event that "occurs whenever the form is first displayed.". Also you may want to use the BeginUpdate and EndUpdate functions to make the populating of your combobox faster.
It has that certain smell of workaround, but this approach should fulfil your needs:
private bool _hasInitialized = false;
private void Form1_Shown(object sender, EventArgs e)
{
if (!_hasInitialized)
{
ThreadPool.QueueUserWorkItem(state =>
{
Thread.Sleep(200); // brief sleep to allow the main thread
// to paint the form nicely
this.Invoke((Action)delegate { LoadData(); });
});
}
}
private void LoadData()
{
// do the data loading
_hasInitialized = true;
}
What it does is that it reacts when the form is shown, checks if it has already been initialized before, and if not it spawns a thread that will wait for a brief moment before calling the LoadData method on the main thread. This will allow for the form to get painted properly. The samething could perhaps be achieve by simply calling this.Refresh() but I like the idea of letting the system decide how to do the work.
I would still try to push the data loading onto a worker thread, invoking back on the main thread for populating the UI (if it is at all possible with the COM component).
Can you get your data from a web service that calls the COM component?
That way, you can display empty controls on a Locked form at the start, make Asynchronous calls to get the data, and on return populate the respective combos, and once all of them are loaded, you can unlock the form for the user to use.
You could listen for the VisibleChanged event and the first time it's value is true you put your initialization code.
Isn't FormShown the event you're looking for?
When you say that you cannot use a background thread because of COM what do you mean? I am using many COM components within my apps and running them on background threads.
If you create a new thread as an STAThread you can probably load the ComboBox/ListBox on a Non-UI thread. IIRC the ThreadPool allocates worker threads as MTAThread so you'll need to actually create a thread manually instead of using ThreadPool.QueueUserWorkItem.

What is the purpose of a Paint() method vs the _Paint() event in a Win Form?

I am working with a WinForm application that was designed by the previous, now unreachable, develeper. In this app farms are embedded in TabControls through some custom code. My question is, Can anyone help to try and explain why there is a custom _Paint() function in each form that is called from the Load event for that form.
This Paint() method is not actually tied to the form outside of the previously stated daisy chaining. What purpose could this serve? In the code below you will notice That I created a Paint() event and moved part of the code there and everything still seems "Peachy."
Can anyone help me to understand this? Is it simply because of the Public declaration on the custom one?
private void frmWWCModuleHost_Load(object sender, EventArgs e)
{
FormPaint();
}
public void FormPaint()
{
WinFormCustomHandling.ShowFormInContainerControl(tpgCaseNotes, new FrmCaseNotes());
WinFormCustomHandling.ShowFormInContainerControl(tpgMCP, _frmWWCMCPHost);
WinFormCustomHandling.ShowFormInContainerControl(tpgMember, _frmWWCMemberHost);
WinFormCustomHandling.ShowFormInContainerControl(tpgEnrollment, _frmWWCEnrollmentHost);
WinFormCustomHandling.ShowFormInContainerControl(tpgWWCSearch,_frmWWCSearch);
WinFormCustomHandling.ShowFormInContainerControl(tpgAudit, FrmAudit);
// Call each top-Level (visible) tabpage's form FormPaint()
_frmWWCMCPHost.FormPaint();
}
private void FrmModuleHost_Paint(object sender, PaintEventArgs e)
{
new psTabRenderer(tclWWCModuleHost, Color.LightSteelBlue, Color.Tomato, Color.Black, Color.Black);
}
The code looks weird, for sure, but I think you are reading more into the name of the "FormPaint" method than you should. To me, it appears to be just an "initialization" routine, having essentially nothing to do with the Paint event (other than the name).
Also, it appears that any code inside FormPaint is called one time per form, versus any code inside the Paint event handler being called...a lot.
I'm not sure you posted enough code for it to be understandable. But in general, if you feel the code is unnatural, go ahead and refactor it... one step at the time.

Forced Redraw in Silverlight 3 application

I have a function in my silverlight app, that takes one or two seconds to finish. While executing this function, I want to show my "just loading" UC to the user:
private void ComboBoxGranularity_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
WaitScreenObject.Visibility = Visibility.Visible;
OperationThatTakesALittleTime();
WaitScreenObject.Visibility = Visibility.Collapsed;
}
Problem is, that Silverlight doesn't redraw while the function is executed, so my WaitScreen doesn't show up. I tried the trick from this question:
this.Visibility = Visibility.Visible;
but it didn't work. I wanted to avoid the Backgroundworker-Overhead, so is there any possibilty to make the WaitScreenObject visible?
Thanks in advance,
Frank
Your method runs on the UIThread since you mentioned that you are not using a background thread. The thread only redraws the screen when it is not busy with something else, since the queue is filled with the other instructions, and those are considered more important than a redraw.
I tried what Andrew suggested, but I could not find the InvalidateVisual() method on the UIElement. Maybe I was just being daft.
The reason why the linked example did not work for you, is because the other question just dealt with an element not being invalidated because it did not have focus. However, the UIThread was available for a refresh at that time.
I also tested the dispatcher.BeginInvoke() on a delegate, and it did not work either. I am afraid that from my point of view, you might just have to use a separate thread.
I could be wrong because I simulated my "work" by making the thread sleep instead, however, I cannot see what the difference will be.
Hope this helps.
Have you tried invalidating the visual for the root object to try convince it to redraw? (Havn't checked if it would work, just comes to mind as a hack)
Edit : InvaldiateVisual is the method in WPF on the UIelement, in Silverlight you use a diff call, such as InvalidateArrange
myCanvasRoot.InvalidateArrange();
Bit hacky but might convince it to perform the update of the screen.

Categories

Resources