Display an Adorner over a WebBrowser control - wpf

I'm using the System.Windows.Controls.WebBrowser for various things in my app and I've noticed that adorners are cut off when they are supposed to appear over a WebBrowser. I realize that the WebBrowser control is really a wrapper around a COM component and probably renders differently, but I wondered if anyone figured out how to solve this.
This is the problem I'm seeing. Here I have just a sample adorner that is supposed to draw a big red circle in the top corner of something (as a sample).
When I adorn the WebBrowser with this, I get this result:
I expect to see the full circle.
Here's the code for this worthless adorner, in case that is helpful:
public class SillyAdorner : Adorner
{
public SillyAdorner(UIElement element) : base(element)
{
}
protected override void OnRender(DrawingContext drawingContext)
{
drawingContext.DrawEllipse(new SolidColorBrush(Colors.Red), new Pen(), new Point(7, 7), 30, 30);
base.OnRender(drawingContext);
}
}
And here is how I apply it to the browser in the OnRender method of the host control:
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
var layer = AdornerLayer.GetAdornerLayer(browser);
layer.Add(new SillyAdorner(browser));
}
Anyone have any hacks or workarounds for this?
Edit: I'm using .NET 4.0, if that makes a difference.
Edit #2: WebBrowser appears to inherit from HwndHost, which I've seen another question or two regarding adorners and hwndsources, but I'm not seeing anything that looks like I could implement it for the WebControl, but hopefully this is useful information for someone.

I don't think that will work with an Adroner, but you can float content over a WebBroswer control using a transparent Popup control. More details and a code sample can be found here.

Here's my blog post introducing a library I wrote for layering a WPF adorner over any hwnd. A simple web browser demo looks like this:

This is caused by airspace issues. Since a WebBrowser is a native, non-WPF control, there is no way to directly render adorners (or other WPF content) on top of it.
In order to do this, you need to use a separate window of some sort, and put the content into that separate window. This typically means using a transparent WPF window layered over the top of your main window. Unfortunately, this will not be as integrated of a solution as a true native WPF control would provide.

Try use another transparent TOP LEVEL overlay window. Only supported on Windows 2000 or higher of course, Win9x does not have layered windows.

Related

Hosted Winform control does not respond to events from WPF

This is my first question in StackOverflow. Due to lack of reputations, I couldn't post any links or images.
I've been working on the following issue for more than 2 days. Any help would greatly be appreciated.
Before I get into my question, here is what I have and what I'm expecting:
I have a Windows Form which hosts WPF in an ElementHost control.
And then, I have a Winforms UserControl similar to DateTimePicker. This
is hosted inside a WindowsFormsHost control.
The above scenario is un-avoidable for the following reasons:
The authorization dialog to all our applications is developed in
Winforms, and takes a Winforms instance as its parameter. There is no
WPF version introduced yet. Therefore, I had to use an ElementHost to
host my View inside the Windows Form.
The Winforms control hosted inside my WPF is also un-avoidable. We
have our own DateTime Winforms UserControl that behaves similar to
the DateTimePicker Winforms control, but has lot more complexities
involved. So, replacing this control with a WPF version is out of
question.
Expected Functionality:
I have a
WPF control (say, a textbox)
A DateTime Winforms UserControl that I was mentioning above.
And a Cancel button that basically resets the above controls.
When I hit the Cancel button, I'm publishing an event from the ViewModel, say RunViewModel to the WPF UserControl code behind file, say RunView.xaml.cs.
eventAggregator.GetEvent<ResetDateTimeEvent>().Publish(true);
In the code behind file, I've subscribed to the event as follows
eventAggregator.GetEvent<ResetDateTimeEvent>().Subscribe(ResetDateTimeHandler);
The WPF control resets to its default value, but the DateTime UserControl does not reset.
So, for testing purposes, I removed the ElementHost control, and just had my WPF View with a WindowsFormsHost control that hosts the DateTime Winforms UserControl, and a WPF "Cancel" button.
When I click on the button, the value on the DateTime control resets to its default value.
Then, I thought this might be an issue with my DateTime Winforms UserControl.
So, I replaced my DateTime Winforms UserControl with a Winforms Textbox control in my actual application. So now the nesting is as follows:
WinForms-ElementHost-WPF-WindowsFormsHost-Winforms Textbox
Here is the xaml code.
<WindowsFormsHost x:Name="ReportFromDtTmHost" Margin="8,0" Grid.Column="0"
LostFocus="ReportFromDtTmHost_LostFocus">
<WindowsFormsHost.Child>
<winforms:TextBox x:Name="ReportFromDateTime"/>
</WindowsFormsHost.Child>
</WindowsFormsHost>
On Initial load, I’m loading the Textbox with Initial Load Text text
private void Window_Loaded(object sender, EventArgs e)
{
ReportFromDateTime.Text = "Initial Load Text";
}
As I was mentioning above, when I hit the Cancel button, this is what happens:
Publish the event from ViewModel
eventAggregator.GetEvent().Publish(true);
Subscribe to the event in the code behind file (xaml.cs):
eventAggregator.GetEvent().Subscribe(ResetDateTimeHandler);
EventHandler for the published event.
private void ResetDateTimeHandler(bool cancelClicked)
{
ReportFromDateTime.Text = "Reset to Default";
}
As you can see in the above code, I’m resetting the Text on clicking the Cancel button.
During Debugging, I could see the Text property being changed to "Reset to Default", but the UI does not show these changes.
Here is the wierd part:
The Child property on the WindowsFormsHost control is different from the actual “ReportFromDateTime” Textbox control.
While debugging, I could see that the Child and Name property on the WindowsFormsHost control were different.
The Name property is empty,
ReportFromDtTmHost.Child.Name = ""
which rather should be ReportFromDateTime.
It almost seems like the Host and the Child controls are getting re-created.
As far as I see it, I think the extra level of nesting (WinForms-ElementHost-WPF-WindowsFormsHost-Winforms Textbox) might be causing issues during the interoperations between WPF and Winforms.
I’ve done a lot of research and searched lot of links for suggestions. I found none pointing out this issue. Some of them were close. Here are a couple of links:
The this suggests to reproduce the message loop under the “Surrogate Windows Forma Message Loop” section.
Here is one more link that explains the issue with nesting under the Nesting section.
I apologize for being verbose. Just wanted you guys to get a clear picture of my problem. Please let me know if you have any questions regarding the post. Any suggestions would be greatly appreciated.
EDIT:
We were able to resolve the issue, but still, it is a work-around. Here is what we did:
There were two ways to resolve this issue, but both were related to using static.
Static Winforms control:
We used the following static Winforms control
public static class ControlHolder
{
public static TextBox ReportFromDateTimeInstance;
}
In the OnChanged event of the "actual" control, we dump the actual control, ReportFromDateTime to the static control, ReportFromDateTimeInstance.
private void ReportFromDateTime_TextChanged(object sender, EventArgs e)
{
ControlHolder.ReportFromDateTimeInstance = (TextBox)sender;
}
And from then on, wherever we update the actual control (as in ResetDateTimeHandler method), we update the static control
private void ResetDateTimeHandler(bool cancelClicked)
{
ControlHolder.ReportFromDateTimeInstance = "Text changed";
}
This shows the updated value on the Front-End
Static EventAggregator
This work-around was provided by one of our colleague.
In this case, we are using our actual control, ReportFromDateTime, rather than the static control, ControlHolder.ReportFromDateTimeInstance
We used a static event aggregator for publishing/subscribing the ResetDateTimeEvent instead of using the Event Aggregator instance provided by Unity Container. So, instead of
eventAggregator.GetEvent<ResetDateTimeEvent>.Publish(true);
we used:
ResetDateTimeEvent.Instance.Publish(true);
And in the subscription:
ResetDateTimeEvent.Instance.Subscribe(ResetDateTimeHandler);
I know that we need not use a static event aggregator in this scenario since we are using the instance provided by Unity Container (which makes sure that a single instance is shared by all the ViewModels), but this also has resolved the issue.
So, I'm still confused on why the above two scenarios are solving the problem. Is it the static-ness that is solving the issue ?
As I was already saying, I feel that the controls are getting re-created, and by the time we have the controls in hand, they have been already re-created.
Any suggestions would greatly be appreciated.
I've done the same thing before in an app that had a WPF control inside of a WinForms control. The WPF used Prism/Unity (later switched to MEF) to start everything up. However, this created a whole new EventAggregator by default in the bootstrapper, so I had override the default IEventAggregator in the container with a static one b/c the WinForm side had already been created and was using its own IEventAggregator instance. The symptom was similar in that published events were not received.
In a mixed system such as yours, singletons are great for ensuring that everything's feeding off of the same reference, especially when your startup is in stages (WinForms then WPF).
Simple answer: yes, use singletons for shared references between WinForms code and WPF code. Those singletons can be fed into the container in the WPF bootstrapper so that injection still occurs in the WPF side, too.

Window transparency in WPF vs Winforms

Why is it that I have to set the WindowStyle property to None on a WPF form to get transparency, but in Winforms I can do it on any form, and retain borders, standard buttons, etc? Clearly the API supports this, so I'm not clear on what's special about WPF that would make this an issue.
I'm guessing that WPF is jumping through some DirectX or OpenGL hoops, while Winforms is just setting the alpha for the window via the API, but I could be way off base.
Agreed, this is heavy handed:
private void VerifyConsistencyWithAllowsTransparency(WindowStyle style)
{
if (AllowsTransparency && style != WindowStyle.None)
{
throw new InvalidOperationException(SR.Get(SRID.MustUseWindowStyleNone));
}
}
WPF uses the exact same mechanism to implement this as Windows Forms, layered windows. There is no obvious reason it wouldn't work the same way in WPF. The code snippet, lifted from Window.cs, simply rules it out. There is however one hint from the UsesPerPixelOpacity property:
When you enable per-pixel opacity, the system no longer draws the non-client area. This is because the intended purpose of UsesPerPixelOpacity is to show non-rectangular top-level UI that works in interoperation scenarios, and showing the rectangular non-client area defeats that purpose.
"interoperation scenarios", I guess.

Image Resize Aliasing in WPF v4 but not under v3.5

I am using WPF for an image resizing pipeline which has been working beautifully under .NET v3.5. I just upgraded the project to target v4.0 and now all of my resized images are heavily aliased. None of the image pipeline code has changed.
Has a default WPF setting changed between v3.5 and v4.0?
How do I control the dithering of my resized bitmap images in WPF?
I'm using BitmapImage, DrawingVisual, DrawingContext, RenderTargetBitmap, BitmapEncoder, and BitmapFrame but I'm not seeing any properties related to dithering. GDI+ had a bunch of settings, so I'm guessing that I'm missing something.
Update: it appears that all of the solutions I've seen assume a Window object or XAML environment. This runs inside a Windows Service which has no UI. I need a way to programmatically affect this setting.
I specifically switched from GDI+ to WPF because GDI+ has memory leaks in long running processes like services & web apps.
The only way I've been able to affect the setting of BitmapScalingMode is to inherit from the DrawingVisual class and set it via its protected accessor:
// exposes BitmapScalingMode (also works for other protected properties)
public class MyDrawingVisual : DrawingVisual
{
public BitmapScalingMode BitmapScalingMode
{
get { return this.VisualBitmapScalingMode; }
set { this.VisualBitmapScalingMode = value; }
}
}
If anyone else knows of a better way to set this, I would be excited to hear about it.
It seems that this would work:
RenderOptions.SetBitmapScalingMode(myDrawingVisual, BitmapScalingMode.HighQuality);
...but it does not. Apparently being outside of the XAML windowing runtime must mean that it cannot set the appropriate values.
The default BitmapScalingMode was Fant in 3.0 but in 4.0 it is now BiLinear. You can change the default a few different ways. A couple described here.

performance in C# Application

i use some pictures in my 'MainForm' And My Windows Application was writing by c sharp.
i use this form to start other forms in my project.
And I use some label and panel with Transparent Color.
but when the program started i see many blink in transparent label and panel.
And it is very bad.
How I Can Fix this problem?
Enabling DoubleBuffered as stax suggested above is helpful but it may not be sufficient.
In your form, add the following method override:
protected override void OnPaintBackground(PaintEventArgs e) {}
And, in the OnPaint method, paint the background yourself instead. If you don't do this, drawing the background and painting are separate events, and background painting has higher priority, meaning that it will happen earlier.
Furthermore, if you add child controls (like labels), they receive their own paint background/paint events. You may be able to disable the Label's background. If I do stuff like this, I tend to not use controls but paint the text and the images in one OnPaint.
did you test it on multiple machines.
did you use an updated machine with all the .net service packs needed.
etc

What causes a window to not appear in the taskbar until it's Alt-Tabbed to in Vista?

When our app is started programatically (either through custom action in MSI installer or when starting a new instance) in Windows Vista (also happens in Windows 7 Beta) it won't appear in the taskbar and isn't focused. Alt-tabbing to it will make it appear in the taskbar properly and stay there.
What causes this? I've seen this in some other apps before as well, but not sure why. Out app is .NET WinForms app. Never see this happen in XP, only Vista and 7
Edit: Well, it seems like the only time this happens reproducibly is when it's run by the installer, I believe there's other times it occurs, but I might just be crazy. The launching code is a bit complex to post because we handle various command line launch parameters and it launches a signin form before actually launching the main app etc.
Has anyone had to deal with this scenario before and worked it out?
Try checking your main application form "Form Border" property.
If it's ToolWindow (Fixed or Sizable), try changing it to FixedDialog for example.
This solved the problem in my case.
The usual reason for this is that your main application window doesn't have the window styles that let Windows know it's a main application window (rather than a tool window or a dialog box). So Windows is having to guess based on how the app was started, etc.
Use Spy++ to complare the window styles (especially extended styles) if your window to that of some other window that doesn't have this problem. Are you missing the WS_EX_APPWINDOW style? Are any other styles/extended styles different from other top level windows?
G.So's answer made me find the solution for my problem, wich was caused by the fact that i had my form sizable from launch, but set to borderless in the load void.
If anyone is interested in how i managed to keep the switch to borderless and have it pop up as it should in the taskbar without any dirty hacks.. here it is..
Make a new event from the form on the form's "Shown" event, and put your line of code for switching to borderless in here. Problem solved :)
private void Form1_Shown(object sender, EventArgs e)
{
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
}
and for the lazy ones ;) >>>>
this.Shown += new EventHandler(Form1_Shown);
Thanks again G.So for clearing up what could cause this in the first place.
I stuggled with this issue as well, and found like a previous commenter said, you cannot have anything in the form's Load() event that changes that FormBorderStyle property. Move anything that changes it to the Shown() event.
Well, one solution is to use a hack like this. That's really not what it's for.
Usually the decision of whether a window will be in the taskbar or not is based on the border styles it uses. The article I linked to provides a bit more detail. The article's comment about the window having an owner or not is quite possible highly relevant to your issue, since the window might somehow be getting a different owner when launched by the installer.
That article is in VB but it's all based around API calls so the info it provides is pretty language independent.
Never see this happen in XP, only Vista and 7
Maybe it's a bug in Vista...?
What happens if you call SetForegroundWindow() (or equivalent in .Net)?
Edit
I did of course mean "BringWindowToTop()".
Or do both.
We had this same problem and fixed it by setting the form property showintaskbar property to true.
Weird that all windows os's dont run apps in the same way!
In our situation, this was tracked down to the form's text property being changed within the Load event.
After putting this inside a BeginInvoke, this odd behaviour no longer happened.
Hope this helps anyone else.
Example
private void Form_Load(object sender, EventArgs e)
{
...
...
...
// this needs to be inside a BeginInvoke otherwise it messes with the taskbar visibility
this.BeginInvoke(new Action(() =>
{
this.Text = "Something new";
}));
...
...
...
}
We encountered the same issue, also in Windows 8. Sometimes the form was receiving correctly the focus, but say just ~30% of the time.
We tried different solutions, but actually the one that worked was the following:
private void OnFormShown(object sender, EventArgs e)
{
// Tell Windows that the Form is a main application window
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
// Even if true, enforce the fact that we will the application on the taskbar
this.ShowInTaskbar = true;
// Put the window to the front and than back
this.BringToFront();
this.TopMost = true;
this.TopMost = false;
// 'Steal' the focus.
this.Activate();
}
Moreover, we ensure also not to set the title of the form during the Load event.

Resources