opening WPF user control winform window shrinks parent winform window - wpf

I have a WPF user control like this...
namespace WpfApplication1
{
public partial class MyControl : UserControl
{
......
}
}
I also have a win form to contain this WPF user control...
namespace WindowsFormsApplication4
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
ElementHost ctrlHost = new ElementHost();
ctrlHost.Dock = DockStyle.Fill;
WpfApplication1.MyControl win = new WpfApplication1.MyControl();
ctrlHost.Child = win;
this.Controls.Add(ctrlHost);
}
}
}
I have one more parent win form that has a button. Clicking the button will open the Form1 that contains the ElementHost.
namespace WindowsFormsApplication4
{
public partial class Parent : Form
{
public Parent()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Form1 form1 = new Form1();
form1.Show();
}
}
}
My application runs the Parent form by default...
Application.Run(new Parent());
The problem I'm facing is strange. When i run the application, the parent form opens and on clicking the button the child window form containing the WPF control also opens. But the problem is the size of parent form window automatically shrinks(the window displaces, restores itself and the controls and font becomes smaller in it.) as soon as the WPF control form pops up. If I comment the content of the 'Form1_Load' function then the parent window does not shrink. To check the worst case i commented everything in 'Form1_Load' except
ElementHost ctrlHost = new ElementHost();
line. The mere presence of this line itself makes the parent form shrink as I mentioned earlier. I tried to search in internet for this problem extensively. I was not able to find a solution. Please help me with a answer. I'm exhausted....

I commented above that I had the same issue and have since resolved it. I'm writing up my changes here for anyone else.
As observed above, the behaviour seems to occur whenever using Windows UI scaling in an WinForms application and the Just In Time (JIT) Compiler processes anything from the WPF libraries. In my case, entering a method that contains code that opens the WPF version of MessageBox will make it happen.
Ordinarily Windows will handle basic scaling for you, rendering to a bitmap offscreen and then drawing it on screen but scaled up. When WPF loads it seems to take over, as if it's saying to Windows, "Hey.. i got this..". After that Windows stops scaling the WinForms for you and you end up with the 1x scale version and often some confused controls. The WPF portion however is handling its own scaling and looks fine.
So the way I went about solving it was to tell Windows that I would handle the WinForms scaling. To enable that you have to add this to your application manifest (dll manifest is ignored)
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
OR uncomment the following section if it is already in there:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
You can add a manifest file:
Right click on application project -> Add -> New item... -> Application Manifest File
Then in...
Application Project -> Properties -> Application -> Resources
Make sure "Manifest" is set to app.manifest
You can now find that file and add the XML above into the root <asmv1:assembly>element.
If you've taken the default application manifest and added that element it probably looks something like this
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
</applicationRequestMinimum>
</security>
</trustInfo>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</asmv1:assembly>
Now when you start your WinForms app you will notice it's much crisper because it's being rendered at high dpi instead of 96dpi and then scaled up to fill the space.
You'll probably notice that a lot of your images have shrunk!!
In my case Buttons, MenuStripItems and ToolStripItems did not scale as desired.
What I found was that most controls have a method that you can override as below
protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
{
base.ScaleControl(factor, specified);
}
This is called when the application launches and filters through your controls from the main form. My Windows is set to 200%, my main form's scaling mode was set to DPI and all the forms were designed at 100% scale (96dpi). I changed everything in my first attempts to fix the problem to inherit the scaling mode and this was what worked for me, if you're using font or none i suspect it will work just the same but I haven't tried it.
As mine was 200% UI scaling when this method was called factor was simply {2.0, 2.0} which I then used to recreate a scaled Image in Buttons, and to increase the ImageScalingSize on each of the Items of MenuStrip and ToolStrip since these do not receive the ScaleControl call. If you never added the XML above then this method is still called but will only ever have {1.0, 1.0} for factor, which isn't helpful.
Note: if you're using image list then don't dynamically set the image if in DesignMode or the ImageList will become unset and when you save then nothing will be set
Also not that factor is a factor from the current. What you will notice is if you move the application between different dpi monitors you will get 2.0, then 0.5, then 2.0, then 0.5, etc.
Now my WinForms application looks super crisp and it can call WPF ui elements without going crazy! yyayyyyy
Hope this helps someone

Related

Set DPI scaling for CefSharp window only in WPF application

I'm trying to convert IE web browser window into a CefSharp (v 96.0.180) window into as a part of a larger WPF application. The application itself follows the system dpi level (resizes UI with it), but the IE window kept 100% dpi no matter what was the system setting. ​When I converted the control to CefSharp, it would start to follow the system dpi.
The problem is that the page is rendered zoomed in and ugly on any dpi over 100%.
To check the Cefsharp window behavior I've tried setting dpi awareness to true or per monitor using app manifest, but it didn't work (and also I'm concerned that it would set it for the whole application, while I only need it for one window/project).
Is there any way to achieve it?
EDIT: code per request:
BrowserView.xaml:
<Grid>
<ContentControl Name="wBrowser" RenderOptions.BitMapScalingMode="HighQuality" />
</Grid>
BrowserView.xaml.cs:
public BrowserView()
{
CefRuntime.SubscribeAnyCpuAssemblyResolver();
Cef.EnableHighDPISupport();
LoadApp();
InitializeComponent();
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void LoadApp()
{
var settings = new CefSettings()
{
CachePath = _cachePath,
LogSeverity = LogSeverity.Disable
};
//settings.CefCommandLineArgs.Add("high-dpi-support", "1");
//settings.CefCommandLineArgs.Add("force-device-scale-factor", "1");
settings.CefCommandLineArgs.Add("disable-gpu-compositing");
Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);
}
(I've also tried to move these calls to App.xaml.cs Main method which I've created as advised in CefSharp wiki/GeneralUsage. Those were the first calls in Main. No effect, whatever I put as commandline args.)
In the same BrowserView.xaml.cs file in an event handler for an event fired when control's been loaded:
var chromiumWebBrowser = new ChromiumWebBrowser();
wBrowser.Content = chromiumWebBrowser;
// call to Window.Show() here
chromiumWebBrowser.LoadUrl(url);
In conclusion, none of the actions above made the window look as it would look if I switched the system settings to DPI 100% and rerun the application (and that look is what I need).

ActiveX Control In WPF Windows Form Host Not Displaying After Becoming Visible

I have a wpf usercontrol that contains an activex control housed in a windowsformhost.
I'm using an MVVM pattern that says
ViewModel1 is mapped to a Pure WPF View and
ViewModel2 is mapped to wpf content and the above usercontrol
If ViewModel2 is "Hidden" and then becomes Visible then the Activex control inside it doesn't show (Specifically I'm talking about the VLC activex control).
I've tested in a non MVVM pattern with a button and the usercontrol. The usercontrol is hidden until you press the button and the same thing happens but if I create a method in the usercontrol to re attach the activex control to the windowsformhost then it reappears. If I call this method from a viewmodel then it still remains blank. Does anyone know how I can get this to show again?
EDIT - I've just discovered it's because I have transparency on in my wpf application. It seems it's not possable to do what I want with windowsformshost and transparency enabled.
As there are no obvious answers I'll share my experience. When transparency is turned on in the wpf window then the windows form host doesn't refresh when changing from Hidden to Visable. I have found no way to make this work unless it is hosted in a new window with "Allowstransparency=false".
How are you setting up your active x control? The following Typically works for me in WPF if you are just needing it to attach to a grid. No user control required.:
//Active X Control initializer
private Ax addAxObject<Ax>(Grid container)
where Ax : System.Windows.Forms.Control, new()
{
Ax ax = new Ax();
var hoster = new System.Windows.Forms.Integration.WindowsFormsHost();
hoster.Child = (System.Windows.Forms.Control)ax;
container.Children.Add(hoster);
return ax;
}
private MyActiveXControl myActiveXControl;
public Grid InitializeActiveX(Grid grid)
{
myActiveXControl = addAxObject<myActiveXControl>(grid);
return grid;
}
Then all you do is is add it to your grid in your main window like so:
public MainWindow()
{
InitializeComponent();
//initialize Active X control
gridMain = InitializeActiveX(gridMain);
}
It shows up just fine for me. (Obviously not in the designer since it is programatically created)

Cannot show up WPF application when setting MainWindow manually and composing application (MEF)

I got my hands om MEF for a week now and I am trying to build up a WPF application that loads imported controls from MEF.
I created a WPF application project and removed the default window and application start up URI. Then I handled the application startup event to compose the application:
public partial class App : Application, IPartImportsSatisfiedNotification
{
{...}
private void App_Startup(object sender, StartupEventArgs e)
{
this.Compose();
}
public void Compose()
{
try
{
globalCatalog.Catalogs.Add(new DirectoryCatalog(extensionsDirectoryPath));
CompositionContainer container = new CompositionContainer(globalCatalog);
container.ComposeParts(this);
}
catch (Exception ex)
{
// Do something
}
}
{...}
}
Actually, when debugging and watching objects after imports are satisfied, everything has hierarchically composed fine like I wanted. But when I try to show up the MainWindow of the application an exception is thrown on MainWindow.Show() call:
"Specified element is already the logical child of another element. Disconnect it first."
Though my code in OnImportsSatisfied method seems fine as it is working when not using MEF mecanism:
public void OnImportsSatisfied()
{
Window mainWindow = new Window();
mainWindow.Content = this.importedControl;
this.MainWindow = mainWindow;
this.MainWindow.Show();
}
I insist on the fact that this works perfectly when not importing controls with MEF. What is surprising is that this code does not work too:
Window mainWindow = new Window();
//mainWindow.Content = this.importedControl;
this.MainWindow = mainWindow;
this.MainWindow.Show();
So I suspect that ComposeParts is doing a bit more than what it says as it is the only member acting on my actual application instance.
Hope someone can help me (Glenn?).
Thanks.
Edit:
I discovered that when I remove the IPartImportsSatisfiedNotification interface from my parts, no exception is thrown and the window shows up. But of course the window is empty as I need this OnImportsSatisfied method to set the DataContext of the window to its associated imported view model.
The sample applications of the WPF Application Framework (WAF) show how to use MEF within a WPF application.
I finally discovered that I was importing my WPF user controls by using the default ImportAttribute constructor, which in fact will make a shared instance of the class if the creation policy is not specified during export. And as many of my controls were implementing the same interface and I was binding them in my views, I was actually trying to add this shared user control instance to different visual elements, which is not permited by WPF (and so the exception).
I marked my imports using the RequiredCreationPolicy set to NonShared and everything got back in order! That was all about learning MEF...

Dynamic Application-level resources are not dynamic when hosted in ElementHost

I'm hosting a WPF UserControl in a WinForms container. Now, I want to be able to theme/skin the UserControl. To do this, I've got several resource dictionaries that define the "skins." When my app starts up I create a "new System.Windows.Application()" so that Application.Current exists. To change the theme the old skin is removed and a new skin is merged into the Application level resource dictionary at runtime. However, this does not change any of the dyanamically referenced resources in the UserControl. I tried this in a straight WPF application and it worked just fine. Am I missing something, or is it not possible to do this at all? By the way, if I add a skin into the application resources before the UserControl is initialized it will work but I cannot change the skin after that.
To repo this in the most basic way:
Create a new WinForms application. Add a WPF UserControl to the app. This is simple enough:
<UserControl ...>
<Grid>
<Button
Background="{DynamicResource ButtonBG}"/>
</Grid>
</UserControl>
Create two ResourceDictionaries, White.xaml and Black.xaml (or whatever) that have a SolidColorBrush with the key ButtonBG with respective color. In Form1.cs, add two Buttons and an ElementHost. Set the child of the ElementHost to an instance of the UserControl we just created. Wire up the buttons to events that swap the skin:
private void White_Click(object sender, EventArgs e)
{
Application.Current.Resources.MergedDictionaries[0] =
(ResourceDictionary)Application.LoadComponent(
new Uri(#"\WpfThemes;component\White.xaml", UriKind.Relative)));
}
private void Black_Click(object sender, EventArgs e)
{
Application.Current.Resources.MergedDictionaries[0] =
(ResourceDictionary)Application.LoadComponent(
new Uri(#"\WpfThemes;component\Black.xaml", UriKind.Relative)));
}
In Program.cs, ensure that Application.Current exists and set the initial skin:
[STAThread]
static void Main()
{
new System.Windows.Application();
Application.Current.Resources.MergedDictionaries[0] =
(ResourceDictionary)Application.LoadComponent(
new Uri(#"\WpfThemes;component\White.xaml", UriKind.Relative)));
...
}
Now, when the White button is clicked I would expect the button in the UserControl to turn white and when the Black button is clicked I would expect the button to turn black. This does not happen, however.
Does anyone know why? Is there a solution?
Edit: Idea: Perhaps, if there's a way to force re-evaluation of DynamicResources when the theme changes, that would work.
Thanks,
Dusty
I think this may be an overlooked issue in the WPF framework.
From what I can tell via Reflector, it appears that when the Application resource dictionary is catastrophically changed (a change that will likely have wide ranging effects like adding, removing, or replacing a skin), there is code that loops over all of the Windows in the application and forces them to re-evaluate their DynamicResources. However, other elements that I would consider top-level in WPF like ElementHosts do not get the same treatment. This leads to the behavior that I'm experiencing.
My workaround to this issue is to manually go through all of my ElementHosts individually and add, remove, or replace the skin ResourceDictionary file. It's not perfect, but it gets the job done.
Dr. WPF came to my rescue when I was trying to do something similar. He shows how to create the Application object in WinForms. Now you can reference everything as StaticResource just like in a WPF application.
http://drwpf.com/blog/2007/10/05/managing-application-resources-when-wpf-is-hosted/
Another workaround would be to create a dummy window and specify the content of the elementhost as content.
If you look into the Application and check how it handles changes of resourcedictionaries, you see that it only notifies windows..
The only thing you should remind is to never show the window (-> exception), and to close it when disposing the elementhost, so the application can shutdown properly.

How can one host Flash content in a WPF application and use transparency?

How can I go about hosting flash content inside a WPF form and still use transparency/alpha on my WPF window? Hosting a WinForms flash controls does not allow this.
Unless the control you use to display the Flash content is built in WPF, you will run in to these "airspace" issues. Every display technology from Win32 to WinForms used HWNDs "under the hood", but WPF uses DirectX. The Window Manager in Windows however, still only understands HWNDs, so WPF apps have one top-level HWND-based window, and everything under that is done in DirectX (actually things like context menus and tooltips also have top-level HWNDs as well). Adam Nathan has a very good description of WPF interop in this article.
Although I haven't done it, you can probably use the WebBrowser control found in WPF 3.5 sp1 to wrap your Flash content within WPF. I'm not sure how the transparency will be affected though.
Can you use Expression to convert the flash content to XAML? I believe that there are tools in there or off to the side that do this.
Just have been struggling with same problem of how to upload & Make WPF transparent with ability of displaying Flash, because if you enable on your MainWindow "Allow transparency" Flash will not show once the application will run.
1) I used WebBrowser Control to play Flash(.swf) files. They are on my PC, however it can play from internet or wherever you have hosted them. Don't forget to name your WebBrowser Control to get to it in C#.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
MyHelper.ExtendFrame(this, new Thickness(-1));
this.MyBrowser.Navigate(#"C:\Happy\Download\flash\PlayWithMEGame.swf");
}
2) Now for transparency. I have set in WPF 'false' to "Allow Transparency" and set "Window Style" to 'None'. After that I have used information from HERE and HERE and created a following code that produced desired effect of allowing transparency on MainWindow and running Flash at same time, here is my code:
public class MyHelper
{
public static bool ExtendFrame(Window window, Thickness margin)
{
IntPtr hwnd = new WindowInteropHelper(window).Handle;
window.Background = Brushes.Transparent;
HwndSource.FromHwnd(hwnd).CompositionTarget.BackgroundColor = Colors.Transparent;
MARGINS margins = new MARGINS(margin);
DwmExtendFrameIntoClientArea(hwnd, ref margins);
return true;
}
[DllImport("dwmapi.dll", PreserveSig = false)]
static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);
}
struct MARGINS
{
public MARGINS(Thickness t)
{
Left = (int)t.Left;
Right = (int)t.Right;
Top = (int)t.Top;
Bottom = (int)t.Bottom;
}
public int Left;
public int Right;
public int Top;
public int Bottom;
}
And called it from Window_Loaded() + you need 'below' line for 'DllImport' to work.
using System.Runtime.InteropServices;
using System.Windows.Interop;

Resources