How to push a message loop in WinForms? - winforms

In WPF, I can push a message loop using Dispatcher.PushFrame.
What is the equivalent in WinForms? I'm familiar with DoEvents but that must be called in a loop which can spin the CPU instead of the very efficient approach of just waiting for a message or for an event to signal to exit (like Dispatcher.PushFrame has).

I was able to include WindowsBase to my project's references and just use Dispatcher.PushFrame and frame.Continue = false as usual.
Any caveats with wpf-winforms interop apply, and it does require part of wpf to be referenced, but it should still be better than DoEvents (which has severe pitfalls).

This is the equivalent:
System.Threading.SendOrPostCallback callback = o =>
{
this.Text = "Hello" + o.ToString(); // "Hello42"
};
WindowsFormsSynchronizationContext.Current.Post(callback, 42);
The 42 is a state parameter that gets past to the callback.
You can also do this:
this.BeginInvoke((Action)(() => this.Text = "Hello"));
BTW, you should never ever ever use DoEvents - it's a great way to introduce bugs in to your code and is really only there for VB6 compatibility.

Related

How to break while-loop in VFP (FoxPro) by button click?

I'm trying to make simple FoxPro application with two buttons. First button starts while-loop and works correctly. Second button must break this loop. But I can't find any correctly working solution.
There are loop code:
DO WHILE !thisForm.cancelpressed
IF thisForm.cancelpressed
EXIT
ENDIF
randNum=VAL(TRIM(STR(ROUND( 3*RAND(), 1))))
&&oneVar=VAL("1")
IF randNum = VAL("1")
ThisForm.shape1.BackColor = greenColor
ThisForm.shape2.BackColor = orangeColor
ThisForm.shape3.BackColor = redColor
ENDIF
cs = seconds()
IF abs(cs - seconds()) >= 15
doevents
cs = seconds()
ENDIF
IF thisForm.cancelpressed
EXIT
ENDIF
DOEVENTS
WAIT TIMEOUT 1
ENDDO
This code is in the first button Click method. Form object have user-defined 'cancelpressed' property. In the second button click method I put this:
thisForm.cancelpressed = .T.
Thisform.Release()
Clear Events
But it not work. When loop started it dont want to listen any commands and clicks, and it dont let to close the program. Please help if you can
You could try setting
Application.AutoYield = .T.
as per https://msdn.microsoft.com/en-us/library/aa977324(v=vs.71).aspx
But if that doesn't work then I think you are stuck as VFP is not a true multithreaded application. You might be able to get around the problem using a timer object as per http://fox.wikis.com/wc.dll?Wiki~TimerExample or using a multi threading workaround from https://blogs.msdn.microsoft.com/calvin_hsia/2006/05/16/create-multiple-threads-from-within-your-application/ but you would be very much on your own.
I have to say that starting a new application in VFP today probably isn't the best idea. There are plenty of current alternatives.
The problem is with your wait window. Remove it.
Local greenColor, orangeColor, redColor
m.greenColor = 0x00FF00
m.orangeColor = 0x3060FF
m.redColor = 0xFF0000
Do While !Thisform.cancelpressed
randNum=Val(Trim(Str(Round( 3*Rand(), 1))))
If m.randNum = Val("1")
Thisform.shape1.BackColor = m.greenColor
Thisform.shape2.BackColor = m.orangeColor
Thisform.shape3.BackColor = m.redColor
Endif
Doevents
Enddo
(I don't understand why you are doing such a weird randNum setting and check nor the purpose of setting the same colours again and again but it is not the point in this question)
Normally you should prefer using a Timer with a code like this.
And BTW this has nothing to do with multithreading neither in VFP nor in another language. You are simply doing a loop with a cancel flag check. Updating the UI, say in C#, with the same approach wouldn't be any easier.

ADF Invoke operation manually from code

I want to execute a data control operation (CreateInsert and Delete) from a buttons ActionListener. I am aware a data control button can be inserted from the Data Controls menu, but for various reasons I need to do it this way, a prominent one being I need to perform extra runtime checks.
I found the following code:
OperationBinding operation = bindings.getOperationBinding("operation_name");
operation.getParamsMap().put("parameter_name", parameterValue);
operation.execute();
But don't know which variables to use for myself. First of all, I don't know which binding I should use. Then, the operation name should, as far as I know, be CreateInsert, and for the next button, CreateInsert1. Thats whats used for UIBinding now (which I will remove).
The Data control I want to use the operation of is 'ARNG1'.
So in short, I need to know how to manually invoke this Data control's CreateInsert operation.
Thanks in advance.
See if this will help you:
https://blogs.oracle.com/shay/entry/doing_two_declarative_operatio
the code you want to execute an operation behind a actionlistener:
public BindingContainer getBindings() {
if (this.bindings == null) {
FacesContext fc = FacesContext.getCurrentInstance();
this.bindings = (BindingContainer)fc.getApplication().
evaluateExpressionGet(fc, "#{bindings}", BindingContainer.class);
}
return this.bindings;
}
BindingContainer bindings = getBindings();
OperationBinding operationBinding =
bindings.getOperationBinding("doQueryResultReset");
operationBinding.execute();
Similar to Joe's answer but does not use EL Expression evaluator and uses direct access instead to get the BindingContainer
//Get binding container
BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();
// get an Action or MethodAction
OperationBinding method = bindings.getOperationBinding("methodAction");
method.execute();
List errors = method.getErrors();

How to cancel individual async computation, being run in parallel with others, from a button click event

I've prepared the following WinForms code to be as simple as possible to help answer my question. You can see I have a start button which sets up and runs 3 different async computations in parallel which each do some work and then update labels with a result. I have 3 cancel buttons corresponding to each async computation being run in parallel. How can I wire up these cancel buttons to cancel their corresponding async computations, while allowing the others to continue running in parallel? Thanks!
open System.Windows.Forms
type MyForm() as this =
inherit Form()
let lbl1 = new Label(AutoSize=true, Text="Press Start")
let lbl2 = new Label(AutoSize=true, Text="Press Start")
let lbl3 = new Label(AutoSize=true, Text="Press Start")
let cancelBtn1 = new Button(AutoSize=true,Enabled=false, Text="Cancel")
let cancelBtn2 = new Button(AutoSize=true,Enabled=false, Text="Cancel")
let cancelBtn3 = new Button(AutoSize=true,Enabled=false, Text="Cancel")
let startBtn = new Button(AutoSize=true,Text="Start")
let panel = new FlowLayoutPanel(AutoSize=true, Dock=DockStyle.Fill, FlowDirection=FlowDirection.TopDown)
do
panel.Controls.AddRange [|startBtn; lbl1; cancelBtn1; lbl2; cancelBtn2; lbl3; cancelBtn3; |]
this.Controls.Add(panel)
startBtn.Click.Add <| fun _ ->
startBtn.Enabled <- false
[lbl1;lbl2;lbl3] |> List.iter (fun lbl -> lbl.Text <- "Loading...")
[cancelBtn1;cancelBtn2;cancelBtn3] |> List.iter (fun cancelBtn -> cancelBtn.Enabled <- true)
let guiContext = System.Threading.SynchronizationContext.Current
let work (timeout:int) = //work is not aware it is being run within an async computation
System.Threading.Thread.Sleep(timeout)
System.DateTime.Now.Ticks |> string
let asyncUpdate (lbl:Label) (cancelBtn:Button) timeout =
async {
let result = work timeout //"cancelling" means forcibly aborting, since work may be stuck in an infinite loop
do! Async.SwitchToContext guiContext
cancelBtn.Enabled <- false
lbl.Text <- result
}
let parallelAsyncUpdates =
[|asyncUpdate lbl1 cancelBtn1 3000; asyncUpdate lbl2 cancelBtn2 6000; asyncUpdate lbl3 cancelBtn3 9000;|]
|> Async.Parallel
|> Async.Ignore
Async.StartWithContinuations(
parallelAsyncUpdates,
(fun _ -> startBtn.Enabled <- true),
(fun _ -> ()),
(fun _ -> ()))
Cancelling threads un-cooperatively is generally a bad practice, so I wouldn't recommend doing that. See for example this article. It can be done when you're programming with Thread directly (using Thread.Abort), but none of the modern parallel/asynchronous libraries for .NET (such as TPL or F# Async) use this. If that's really what you need, then you'll have to use threads explicitly.
A better option is to change the work function so that it can be cooperatively cancelled. In F#, this really just means wrapping it inside async and using let! or do!, because this automatically inserts support for cancellation. For example:
let work (timeout:int) = async {
do! Async.Sleep(timeout)
return System.DateTime.Now.Ticks |> string }
Without using async (e.g. if the function is written in C#), you could pass around a CancellationToken value and use it to check if cancellation was requested (by calling ThrowIfCancellationRequestsd). Then you can start the three computations using Async.Start (creating a new CancellationTokenSource for each of the computations).
To do something until they all complete, I would probably create a simple agent (that triggers some event until it receives a specifies number of messages). I don't think there is any more direct way to do that (because Async.Parallel uses the same cancellation token for all workflows).
So, I guess that the point of this answer is - if work is meant to be cancelled, then it should be aware of the situation, so that it can deal with it appropriately.
As Tomas mentioned, forcibly stopping a thread is a bad idea, and designing something that doesn't realize it is a thread to be able to stop raises flags, in my mind, but, if you are doing a long calculation, if you are using some data structure, such as a 2 or 3D array, then one option would be to be able to set that to null, but, this violates many concepts of F#, since what your function is working on should be not only immutable, but if there is some array that is going to be changed, then nothing else should be changing it.
For example, if you need to stop a thread that is processing a file (I had to do this before), then, since the file couldn't be deleted, as it was open, then I was able to open it in Notepad, then just delete all the content, and save it, and the thread crashed.
So, you may want to do something like this in order to accomplish your goal, but, I would suggest that you re-evaluate your design and see if there is a better way to do this.

Why is this method executing twice each time I call it?

I have the following method that is executing twice every time it is called:
public static void ChangeToRepository(RepositoryTextBox textBox, int repositoryNumber)
{
MessageBox.Show("you");
int indexOfLastRepository = (textBox.RepositoryCollection.Count - 1);
if (repositoryNumber > indexOfLastRepository)
{
AddTextRepositoriesThrough(textBox, repositoryNumber, indexOfLastRepository);
}
textBox.RepositoryCollection[textBox.CurrentRepositoryNumber].CurrentText = textBox.Text;
textBox.PreviousRepositoryNumber = textBox.CurrentRepositoryNumber;
textBox.CurrentRepositoryNumber = repositoryNumber;
textBox.Text = textBox.RepositoryCollection[textBox.CurrentRepositoryNumber].CurrentText;
}
The first time that the method executes, it executes all of the code except for its last line:
textBox.Text = textBox.RepositoryCollection[textBox.CurrentRepositoryNumber].CurrentText;
The second time, it executes all of the code. What's up?
When you assign to CurrentRepositoryNumber on the text box, it probably triggers an event handler that calls back to this function again. This seems likely because the property name suggests that it controls the current repository, which this method then is responsible for displaying somehow.
You might want to temporary delist, assign to the property and then re-enlist that event handler. Or maybe you need more of a redesign to get the responsibilities clear - often with GUI frameworks that is hard to do, and the simplest option is to just delist, assign, re-enlist, with this kind of pattern:
textBox.TextChange -= YourHandler;
textBox.Text = newValue;
textBox.TextChange += YourHandler;

What is a parent freezable? What does this error mean?

I'm getting this error:
Cannot use a DependencyObject that belongs to a different thread than
its parent Freezable
What does that even mean? Is it in English? Is the parent frozen, or is it just freezable? Any way to make a parent not freezable, if it makes the error go away?
What's happening:
I have two opengl winforms controls in a WPF app, and so far, everything's been working smoothly (I think). Now, I've added an update so that when one winform control updates the image, the other should as well. That actually used to work, and now I'm getting that error. Stepping through the code has the crash happen in random places, which leads me to believe that it's a garbage collection fault (ie, some update in another thread is creating something that's getting garbage collected, and that collection happens at a random time).
The exception is caught in the main run method, and it's an InvalidOperationException.
I'm grasping at straws here. Where do I start?
EDIT: It looks like the call that's causing the problem is this one:
if (imagePanel.InvokeRequired)
{
imagePanel.Invoke(new System.Windows.Forms.MethodInvoker(delegate{
imagePanel.ClearImages();
}));
}
else
{
imagePanel.ClearImages();
}
I'm still tracking it down; if that series of lines is commented out, the crash still happens, and the thread status has a 'just ended' thread (hence the garbage collection assumption).
OK, I've figured it out. Ordinarily, I'd just delete this question, but it was a pain to find any information about how to fix this.
The problem was a call that looked like this:
ImageBrush theBrush = new ImageBrush(new Bitmap(new Uri(...)));
if (labelStatus.Dispatcher.Thread == System.Threading.Thread.CurrentThread) {
button.background = theBrush;
}
else {
labelStatus.Dispatcher.BeginInvoke((System.Threading.ThreadStart)(delegate {
button.background = theBrush;
}));
}
But! if you do that, then the dispatcher works, and then it tries to delete the brush, but the brush itself is also apparently getting deleted in another location.
So, the take-home lesson is, if you declare an ImageBrush, then delete it in the same thread, like so:
void MyFunc(){
ImageBrush theBrush = new ImageBrush(new Bitmap(new Uri(...)));
button.background = theBrush;
}
if (labelStatus.Dispatcher.Thread == System.Threading.Thread.CurrentThread) {
MyFunc();
}
else {
labelStatus.Dispatcher.BeginInvoke((System.Threading.ThreadStart)(delegate {
MyFunc();
}));
}

Resources