OnPaint event during a callback when the form is below? - c#

Imagine the following scenario:
this.SetStyle(ControlStyles.UserPaint, true); //this doesn’t change anything
…
void OpenSomeForm()
{
SomeForm sf = new SomeForm();
sf.SomeEvent += new … (SomeEventOcurred);
sf.ShowDialog();
}
private void SomeEventOcurred(…)
{
OnePanelInThisForm.Invalidate();
}
private void OnePanelInThisForm_Paint(object sender, PaintEventArgs e)
{
DoSomeDrawing(e.Graphics);
}
Now, OnePanelInThisForm draws correctly when the form loads. But if SomeEventOcurred is Fired from “SomeForm”, the paint event is not fired. If I close and reopen the form it correctly repaints.
If I add a button to the form that executes: OnePanelInThisForm.Invalidate(); the panel is correctly repaint.
What am I missing?
UPDATE: Clarification. (why don’t we do this in the first place…)
I have a FORM_A. This FORM_A has a Panel that overrides the Paint event. It’s a standard WinForm. In the Paint it draws a circle. This works. Turns out that FORM_A has a button that opens FORM_B. But before doing that, it subscribes to a custom event in FORM_B called: SomeEvent. (see the sample above). So FORM_B can tell FORM_A about “SomeEvent”.
Now, FORM_B is also a normal WinForm. And it has a normal Button. In the Click event of that button, it opens FORM_C. FORM_C also has an event called SomeEvent and obviously FORM_B subscribes to that event. Exactly like before. The idea is that FORM_C has a button that will trigger that event, notifying the interested subscribers. In this case, when FORM_C fires the event, FORM_B is subscribed and interested.
When FORM_B receives the Call Back, the only thing it does is… notify the interested parties (in this case, FORM A) that the event was fired.
Now, even while Form C is still the top form, the callstack goes back to FormA, to the method defined as callback from the 1st event.
This code Executes. All it does is really somePanel.Invalidate() (or Refresh(), same results).
A Breakpoint in the PAINT method of that panel, reveals that the code doesn’t get called. No Paint event is raised despite being invalidated. I assume that happens because the form (and therefore the panel) is actually covered by FORMB and FORMC (still open).
And that’s all. If I close form C and then Form B, form A still DOESN’T raise the paint event. I’ve tried invalidating the panel on Form activation, but that doesn’t happen.
If I close the form A and reopen it, the drawing is, of course, correct.
Hope this makes it more clear.
There isn’t really much code as this is pretty simple, FORM A > B > C (fire event) -> B -> A -> Invalidate().

Try using Refresh() instead of Invalidate(). That seems to work more consistently for me, anyways.

Related

Close form on deactivate breaks click event

I have a windows form in c# which should close when the user clicks anywhere outside of its bounds, eg:
form1.Deactivate += (o, e) => form1.Close();
My problem is, I would also like to catch the click event that caused the form to deactivate, but using the above code my mouse event handlers on the other forms in my application are never called.
For example, I have a click handler on a second visible form in my application:
form2.MouseClick += OnForm2Click
Normally OnForm2Click would get called fine if the user clicks on form2 when form1 is active, but with close-on-deactivate code above, OnForm2Click never gets called (this is a bit strange to me because double-click handlers are called just fine).
I cannot call form1.Close() from within OnForm2Click() as a solution because form2 has no reference to form1.
Thank you in advance for your solution.
You may solve your problem with a mouse hook. See:
Hooks Overview (Windows)

Why does FormClosing fire twice when calling Hide() in modal dialog?

We created a new form that we're showing via ShowDialog and added a "Cancel" button to it. Here's how we're opening the form from its parent:
// _modalForm is a class-level instance of our ModalForm class
var result = _modalForm.ShowDialog(this);
MessageBox.Show(result.ToString());
Here's the cancel button's Click event handler in ModalForm.
private void btnCancel_Click(object sender, EventArgs e)
{
Close();
}
In our FormClosing event, we have this code (based on this answer).
private void ModalForm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
Hide();
_parentForm.RefreshData();
}
Surprisingly, when we click the "Cancel" button (or use the "X" button at the top of the form), the FormClosing event is raised twice. Both times the CloseReason is UserClosing.
I double checked to make sure InitializeComponent isn't call twice and that we only subscribe to the event once. btnCancel is not set at the CancelButton property for the form. It also doesn't have DialogResult set in the designer. When I check the return value of ShowDialog though, it is set to DialogResult.Cancel.
Changing btnCancel_Click to just be DialogResult = DialogResult.Cancel instead of Close() and doing nothing except _parentForm.Refresh() in the FormClosing event fixes the issue of the event getting raised twice.
Does anyone know why in this particular scenario the FormClosing event gets raised twice?
That's because hiding a modal form will cause it to close with DialogResult.Cancel as dialog result. So if you call this.Hide() in FormClosing event, the event will be raised again.
Imagine if it didn't close the form, your application had been blocked by a hidden modal form!
Note: The answer describes about the reason of raising the event twice. But as described here and others mentioned, For modal forms (which you showed using ShowDialog), the Dispose method will not be called and the form exists after closing and you can use its properties to get some data or you can show it again. So you don't need to call hide method.
For more information take a look at: Do I need to Dispose a Form after the Form got Closed?
The workaround you mention is unnecessary, because modal dialogs are not disposed when closed. They are designed to keep their data after being closed (since it is required by the caller of the modal dialog), and to be reused as needed.
Just let it close properly. It causes no harm to a modal dialog :)
Note that this also means that unlike normal forms, you must dispose of modal dialogs manually if they are not persistent. In your scenario, this most likely means you'd want to dispose of the dialog when the parent form is disposed (this happens automatically if you added the dialog as a component, but needs to be done manually if you create it yourself).

C# Winform panel Shown event

Is there an event for panel that is equivalent to form event Shown?
I had a few couple of panel switching within a form which will never be closed.
However i couldn't find anything close to an event like Shown which is used in form.
The closes i had is Paint event. However i only wish to update the panel once every time it is shown.
Form.Shown is not raised every time the form is shown, rather it Occurs whenever the form is first displayed. This being said, there is no Panel.Shown event, and no event which is raised "whenever a panel is first displayed".
You can simulate this behavior with the Panel.Paint event, using a flag to keep track of whether it's been "shown" once before. This will make it behave similar to Form.Shown.
private bool panel1Painted = false;
private void panel1_Paint(object sender, PaintEventArgs e)
{
if (!panel1Painted)
{
// do your shown stuff here
panel1Painted = true;
}
}
To keep in the spirit of Form.Shown, you may want to reset the flag if the Panel is reconstructed. This is not the same as shown.
You could listen on the VisibleChanged event and only act on when visibility = true.
https://msdn.microsoft.com/en-us/library/system.windows.forms.panel_events%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
You could also experiment with the Enter and Invalidated events to see if these give you the results you want.
Or if disabling the panel when leaving it is an option, you might be able to use the EnabledChanged event in your toolbox.

How can I make MouseWheel work without first having to focus the form?

I have a form with only a ReportViewer control on it. When the form is displayed, if you click on the report you can then use the mouse wheel to scroll vertically.
I'd like to be able to scroll as soon as the form appears.
I've tried the following, but no dice...
private void ReportViewer_Load(object sender, EventArgs e)
{
rptViewer.Focus();
}
private void ReportViewer_Activated(object sender, EventArgs e)
{
rptViewer.Focus();
}
Put your code in the form's constructor, right after InitializeComponent();:
rptViewer.Select();
After set rptViewer.Focus call SendKeys.Send(Chr(Keys.Tab)) to move focus from menu to preview area.
Did you try calling rptView.Activate()?
Also it may be that your form is getting focus after the load event completes (I think I've had problems with that before). One solution is, although it is definitely not elegant, to create a single-use Timer that starts when your Load method runs, and fires after 1 ms, and then stops. When the Timer fires, it will activate/focus your ReportViewer.
You could also try adding a MouseWheel event handler to your form. When the event is fired, send a scroll message to your ReportViewer to scroll it up or down. Then it doesn't matter whether or not your ReportViewer has focus, it (should) always scroll when the form has focus.

Is there any case in which the Form's Activated event is not raised?

I don't understand why it could be that, I thought Activated should be raised when the form is shown. In fact my form has TopLevel set to false and it's added to another form. When the main form shows, it's also visible, and I can interact with its controls but I tested and saw that the Activated is not raised.
public MainForm(){
InitializeComponent();
Form child = new Form();
child.Activated += (s,e) => {
MessageBox.Show("Activated!");
};
child.Size = new Size(200,100);
child.TopLevel = false;
child.Show();
child.Parent = this;
}
After running the MainForm the child form is appeared inside the main one and there isn't any MessageBox displayed with the message "Activated!".
What is the additional job to do to make it raise?
If the second form comes to screen for the first time, you can use Shown event.
Activate event is only fired when a form gets focus, but that does not contain showing for the first time. But, if the previous form which is active is outside of your app, it will not raise activate event. I mean it is valid when only viewing forms of same project.
Here is my answer, I noticed that only Form has Activated event, other controls don't have and once the TopLevel of Form is set to false, I think it's treated as a normal control and in that case, Activate() method will do nothing and Activated event won't be raised in any case. I think this is the reason why Activated is not raised.
Thank Kuzgun for a suggestion of using Shown instead, but this is focused on explaining why the Activated is not raised!
This answer is just my guess, even the MSDN page about Form.Activated event doesn't mention this. It should not be missed that way especially in an official documentation page.
Once the TopLevel property of Form is set to false then the form becomes a normal control, hence Activated() event will not fire.

Categories