How to repaint a WinFormsHost when it becomes visible? - c#

I've a winforms control within a WindowsFormsHost on a WPF control. The WPF control is only visible some of the time, and when it becomes visible the contents of the winforms control have usually changed.
When the ViewModel for the WPF control changes I change the contents of the winforms control and the WPF control becomes visible.
Unfortunately, the previous contents of the winforms control is repainted, as if from a visual cache. I've run it through the debugger and I know that the winforms control is having its data updated, but it won't repaint until I re-size the program window (when a repaint is clearly triggered).
I've tried Invalidate() on the winforms control and InvalidateVisual(), InvalidateArrange() and InvalidateMeasure() on the WPF control inside the DataContextChanged event handler for the WPF control, but it seems that because the WPF control is not visible at this point (it's just about to become visible) these method calls are swallowed.
Anyone got any clever ideas on how to force a repaint of a WinFormsHost-ed winforms control immediately after the hosting WPF control becomes visible?

Did you try to invalidate the WinForms control in the IsVisibleChanged event handler ?

You should be able to call Refresh() on the hosted Control as soon as you make it visible. Refresh(), as per the documentation:
Forces the control to invalidate its client area and immediately redraw itself and any child controls.

This turned out to be due to the underlying data-structure failing to notify that it had changed - nothing to do with the paint methods at all! :( Thanks for your help guys.

Related

How to detect when the visual tree is destroyed?

In a WPF application I have, I have a control that is central to the application. This control, called ArtView, is visible for the entire lifetime of the main window. It performs hardware accelerated rendering, and in order to avoid bogging down the UI thread, I set up an event loop in the OnLoaded handler for this control. Events such as mouse clicks, keyboard input, and scrolling are added to a queue that is then consumed by the event loop, but the problem I have is that I do not know how to end the event loop when the window is closed. When I close the window that contains the control, OnUnloaded does not fire, so what is the best way to disable the event loop when the control is no longer visible?
I am aware that I can do something like Window.GetWindow(this).Closing += OnWindowClosing; but that feels like a hack.
dymanoid's solution of subscribing to IsVisibleChanged works flawlessly, and provides a more satisfying solution than depending on the Window for the cleanup of my control.

WebBrowser Control - Caret does not appear until click

I'm having an issue with the WPF WebBrowser control. It seems to have the logical focus (at least, when I press a key, my keyUp/keyDown events are fired), but the caret is not displayed on the control.
In fact, although the keyUp events are fired, nothing is written in the webbrowser since the caret is not displayed.
Focusable property is set to True not only on the Usercontrol, but also on the WebBrowser control until I click on the box.
I tried setting the focus on the webBrowser:
this.webBrowser.Focus();
Keyboard.Focus(this.webBrowser);
But also on the mshtml element (webBrowser.Document)
this.webBrowser.Document.Focus();
The property Keyboard.FocusedElement returns webBrowser.
This post sounds very similar to my problem, and the solutions posted there are not working in my case. I'm not sure if there's any solution:
WebBrowser control keyboard and focus behavior
Any hint? Thanks in advance.

Remove original event behaviour of WinForm Control

I would like to remove the original event behavior of controls within a form (similar to design mode).
So, when the user clicks on the button, i only want to capture that event. I do not want the original button event to be fired. Is this somehow possible?
I am looking for a generic solution. So it should work with any form and any control within the form.
Reason: I wrote a form validation rules designer. It uses reflection to enumerate all form-types in the entry assembly. The user can then select a form type, the designer creates that form, enumerates the controls, and embedds the form in the designer panel.
clicking on a control, opens a formular designer panel, and the user can now create a formular for that control and saves the formular to a DB.
When the form is then opened in the normal "runtime" mode, it loads its validation formulars.
Events are not in fact disabled in the Winforms designer. The designer executes the constructor of the form through Reflection, everything in the InitializeComponent() method executes, including the event subscriptions. Wherever this might cause a problem, the controls check the DesignMode property (prevents a Timer from starting for example) or by custom designers. The form is displayed underneath a transparent layered window on top of which the selection rectangle and drag handles are painted. Which prevents issues with mouse clicks and keyboard focus.
You probably ought to look at this magazine article to get this working for you.
From what I understand from your question, I guess, you can still use the "DesignMode" property for this as well. In your event handling routine, you may want to bypass execution by checking on this property:
if (this.DesignMode) return;
as the first statement in your event handling block of code.

C# Manually triggered MouseDown fails to trigger MouseUp

Let's say I have a created a Form class and a CustomControl class.
On my Form I have two instances of the CustomControl, and a Panel.
Panel has 4 event handlers: MouseEnter (to give a different cursor), MouseLeave (to reset the cursor), MouseDown (to start the dragging thread), and MouseUp (to kill the dragging thread and do post-drag logic).
I can drag the Panel onto the CustomControl. When I do this, the code in Form detects what I have done and deletes Panel from Form.Controls, passes some meta-information to CustomControl, which then creates a Panel on itself.
Basically, it is a hand-over. The Panel object now belongs to the CustomControl.
(This is necessary. It's complicated to explain why, but imagine the custom control has something like scrollbars, and it's necessary for the Panel to belong to the CustomControl so that it will scroll with the CustomControl.)
Now, when I click down on the Panel in the CustomControl, the Panel's MouseDown is triggered, it gets deleted from CustomControl.Controls and sends some meta-information back to the Parent (the Form), which then re-creates the Panel as it was at the start - however already in a dragging state so that the user can re-position the Panel onto the second CustomControl, or perhaps put it back onto the Form. The function which creates the Panel when the Form is first initialised is exactly the same function which creates it now.
However, the Panel's MouseDown has not been triggered. The mouse is down, but the event is not firing because the mouse was already down when it was created. So, I manually call the MouseDown handler in the function in Form which accepts the meta-information from CustomControl.
Unfortunately, this only half-works. The MouseUp handler isn't firing. I can pick up the Panel off the CustomControl and drag it around on the Form as expected, but when I release the mouse, the Panel is stuck to the cursor.
I'm not really sure how to get around this?
An ideal solution would be for, when the meta-information is passed back to Form and the new Panel is created, the MouseDown event to somehow fire naturally as though the user had just clicked down on the Panel.
It sounds like you are creating a new instance of Panel when you move it from CustomControl to Form and back and loosing it's state.
You should either try to pass the actual instance owned by Form to CustomControl without creating a new one or you could capture the state of the Panel in another object which you can pass to the constructor when you create a new Panel so that it is in the same state as the one you were dragging?
It seems as though you are trying to manually fire mouse events to compensate for problems in your design.
Always better to give some example code if you can than lengthy textual explanations.
Look at this
Instant of custom control is disappear when click outside it
I have problem like you.
you shouldn't use a local variable for handling mouseEvent.
Try to use "Capture" function. It's work for me.

C#, fire event of container

I have a MyButton class that inherits from Button. On this class I have placed several other controls (Labels, Progessbar). The problem with this is that the controls on the Button make it impossible to fire the Button.Click or Button.MouseHover event. How can I achieve it that the controls on the Button are only displayed but are "event transparent": A click/hover on the label and progessbar is the same as if I clicked/hover directly on the Button (including sender and everything). Something like "inheriting the events from the parent".
class MyButton : Button
{
Label foo = new Label();
ProgressBar bar = new ProgessBar();
}
You should derive from UserControl then have the button as a child control, and bubble up the button child's on click event.
This link is probably more than what you need, but it's a good starting point.
UPDATE
As pointed out, you may not be using ASP.NET. So here is another post that talks about different custom user controls, specifically what you're after is a Composite Control. This is for Windows Forms.
Write Click event handlers for the label and PB, have them call PerformClick().
Making controls transparent to the mouse is possible but is ugly to do. You'd have to override WndProc() to catch WM_NCHITTEST and return HTTRANSPARENT. The all-around better solution is to not use controls. A Label is just TextRenderer.DrawText() in the button's Paint event. ProgressBar isn't hard either, e.Graphics.FillRectangle().
Having the child controls be real controls in front of the button (either in a class inheriting from Button or from UserControl) may make it hard to get button-specific events working properly, as you have found. (Edit: Although it's hard, it's not impossible -- see Hans Passant's answer)
As a workaround, instead of using child controls, you could custom-paint them onto the button surface, since you don't need most of the functionality of the controls (events, focusing, etc.), just their display.
You can do the additional painting in the OnPaint method of your class. Something like:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawString("My fake label", Font,
SystemBrushes.ControlText, new Point(10, 10))
// draw progressbar
}
To update the display, you would need to trigger a repaint of the Button using Invalidate().
Take a look at Custom Bitmap Button Using C# on CodeProject for a more complete example.
This answer was here a moment ago, but got deleted:
Can you inherit from UserControl instead? This will allow you to place other controls on the control surface, including your button and subscribe to their events.
If you're using WPF, I guess what you're looking for would be something called Bubbled Events. It's a feature in WPF by which events are bubbled from a control to its parent (in your case, it would be from your ProgressBar and Label to your button). I found this article on the matter which I think would be of help to you.
Hope this helps :)

Categories

Resources