Converting dll with WinForm application to WPF - wpf

I need to convert existing plugin for CAD systems from WinForm based UI to WPF. The plugin is a basic dll library with Main method used by main application as an entry point to call this plugin. Also it includes a couple of mandatory methods to load/unload the library with plugin.
Long story short. How it looks today (WinForm based):
public class MyPluginClass : System.Windows.Forms.Form
{
private static MyPluginClass thePlugin;
public MyPluginClass()
{
InitializeComponent();
// Make the displayed window a child of the main application window
Reparent(this);
}
private void InitializeComponent()
{
//UI initialization logic
}
// The main entry point. Called when this library is loaded
public static void Main()
{
thePlugin = new MyPluginClass();
thePlugin.Show();
}
}
Once I load this dll, the parent app executes the Main methods and I get the plugin form loaded and displayed.
Now I'm trying to use WPF instead of WinForm (and I have zero experience in WPF, so apologize for silly questions if any).
First thing I tried is to apply the same logic I used with WinForms - just extend Window class:
public class MyPluginClass : Window
{
private static MyPluginClass thePlugin;
.....
public static void Main()
{
thePlugin = new MyPluginClass();
thePlugin.Show();
}
}
...and I didn't find the way to load required XAML file with my window layout. While trying to find solution, I read several posts saying that maybe it's not a good approach to create derived class based on Window. Though, examples I found so far, describe the standalone application (.exe), while in my case I'm trying to invoke WPF UI while being inside the dll file called by parent application.
Is there a way to specify the required XAML file in this case (when I derive my class from Window class) or I'm going completely wrong way?

Related

Caliburn.Micro - why uses UserControl instead of Window

My question is exactly like in the title.
I'm starting with Caliburn.Micro for MVVM approach (which also is new for me) and in every tutorial the first step is to remove the default MainWindow.xaml file and create a new UserControl file. Why is that? UserControl does not even accept a Title. Isn't it possible to build application using normal Windows? I already tried that, but with every launch I get error "Cannot find view for ViewModel", although both MainView.xaml and MainViewModel.cs are present. When I created a pair of USerControl and ViewModel for it, everything started to work as expected. So again, why Windows don't work?
It wouldn't really be a problem, but I'm thinking that some additions like Modern UI themes for WPF might not work without a window. I'm not sure of that.
Probably one solution would be to display a defined UserControl View inside of a Window, but it's just a workaround.
You could create your own custom shell window by creating a custom WindowManager:
public class CustomWindowManager : WindowManager
{
protected override Window CreateWindow(object rootModel, bool isDialog, object context, IDictionary<string, object> settings)
{
Window window = new Window();
window.Title = "custom...";
return window;
}
}
...that you register in your bootstrapper:
public class HelloBootstrapper : BootstrapperBase
{
...
protected override void Configure()
{
_container.Singleton<IWindowManager, CustomWindowManager>();
...
}
}

Windows Narrator reads the names of all the controls in the window (even hidden ones)

I need to make my application visually impaired friendly... and I am facing this problem: Windows Narrator reads all the controls names in the window despite that some of them are hidden.
I have another app that I used WinForms to write it, and there it works fine.
After looking in the UI Spy I saw that WinForms app is not exposing hidden controls and WPF is exposing all the controls in the window.
Can it be that it's a bug in WPF?
I was having the same problem.
Based on Alexis's answer, I wrote the code bellow. It works for me.
public class MyAutoComplete : RadAutoCompleteBox
{
public MyAutoComplete ()
{
//init stuff here
}
protected override AutomationPeer OnCreateAutomationPeer()
{
return new MyAutomationPeer(this);
}
}
internal class MyAutomationPeer : RadAutoCompleteBoxAutomationPeer
{
public MyAutomationPeer(FrameworkElement owner)
: base(owner)
{
}
protected override List<AutomationPeer> GetChildrenCore()
{
return new List<AutomationPeer>();
}
}
If your controls are already in the visual tree, this behavior is the normal one, because UI Automation tree based on the Visual tree. So if you want to prevent of reading unnecessary elements using screen readers, you have to load them on demand.
You can also override the OnCreateAutomationPeer method in controls that contain visible and hidden elements to return your own AutomationPeer. Then you can override the GetChildrenCore method and return modified children collection. To update automation children tree, you need to call the AutomationPeer.ResetChildrenCache() method and the AutomationPeer.RaiseAutomationEvent(AutomationEvents.StructureChanged) one.

WPF window doesn't implement ISynchronizeInvoke.InvokeRequired ERROR

I have this problem that My WPF App in its MainWindow:
public partial class MainWindow : Window, ICallbackNotify
this " ICallbackNotify " is to communicate with MapInfo COM
and to assure The interactivity between UI & MapInfo
public interface ICallbackNotify : ISynchronizeInvoke
{
// Method called by MapInfoCallback class when user chooses custom OLE menuitem
void OnMenuItemClick(uint id);
// Method called by MapInfoCallback class when the status bar text changes
void OnStatusBarTextChanged(string text);
// Method called by MapInfoCallback class when window changes
void OnWindowContentsChanged(uint windowId);
}
The problem is, this code is working just fine in Windows From but when i put it into WPF there is an ERROR msg :
Error 1 'WpfApplication3.MainWindow' does not implement interface member 'System.ComponentModel.ISynchronizeInvoke.InvokeRequired' c:\users\dragon\documents\visual studio 2010\Projects\WpfApplication3\WpfApplication3\MainWindow.xaml.cs 25 26 WpfApplication3
and i cant figure out why or how to solve this problem
Under the hood Windows Forms and WPF are very different.
ISynchronizeInvoke is a Windows Form concept built upon Win32 API that doesn't apply to WPF. In WPF, we use the Dispatcher for communicating between threads.
From your question it sounds like what you want to do is host a WinForms COM control inside a WPF application. In order to bridge these two technologies, Microsoft has a WindowsFormHost control that you can use to contain your component. This blog post has a pretty good write up on that. Checkout this MSDN example: https://msdn.microsoft.com/en-us/library/vstudio/ms751761(v=vs.100).aspx
Update:
It sounds like the problem you are having isn't at runtime but during compilation?? Let slow down and be clear:
In Windows Forms, all user controls and windows implement ISynchronizeInvoke. WPF isn't built on the traditional Win32 WinProc message pump, so they do not implement this interface. If you copy and paste your INotifyCallBack interface and implementation "as is" into the MainWindow.xaml.cs, you will get a compilation error because the base interface ISynchronizeInvoke is not implemented in that class. To bypass the compilation error you will have to implement the ISynchronizeInvoke signature:
public partial class MainWindow : Window, ISynchronizeInvoke
{
public IAsyncResult BeginInvoke(Delegate method, object[] args)
{
throw new NotImplementedException();
}
public object EndInvoke(IAsyncResult result)
{
throw new NotImplementedException();
}
public object Invoke(Delegate method, object[] args)
{
throw new NotImplementedException();
}
public bool InvokeRequired
{
throw new NotImplementedException();
}
}
But guess what? If you do this, it's up to you to provide the proper mapping between ISynchronizeInvoke and the WPF Dispatcher. So don't do this! The above code might compile but it likely won't work correctly (or at all).
Instead, use the Windows Form Integration capabilities provided by Microsoft to host your control inside a WindowsFormHost control. As you need to implement custom code in your INotifyCallback, you'll need to put that code into a custom windows forms user control.
All said, you need to follow the direction provided by the article listed above:
Reference System.Windows.Forms.dll and WindowsFormsIntegration.dll
Add the appropriate namespaces to your MainWindow.xaml
Add the WindowsFormsHost as a container into your MainWindow.xaml, and then put your custom control inside of that.
So instead of putting your ICallbackNotify implementation on the WPF MainWindow, put it in a standard Windows Form UserControl:
namespace YourProject {
using System.ComponentModel;
using System.Windows.Forms;
public class MyCustomMapControl : UserControl, ICallbackNotify
{
// your custom code goes here
}
}

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...

WPF & WinForms Integration and Application Class

I am planning to create a WPF application with a main window which would launch various WinForms. Some of the WinForms use the System.Windows.Forms.Application class (DoEvents, Application.Path, etc). Do you think that there will be a problem in doing this?
Can I still use System.Windows.Forms.Application.DoEvents() from a WinForm that is launched from a WPF application?
The main problem will the ability to instantiate the Windows Forms window and set it's owner to that of the WPF window. The Winforms will want a IWin32Window which a WPF window isn't. To get around this, you need to make a custom class.
I found this code on Mark Rendle's blog (I've copied it here as I had to use the Google Cache to access the page).
LINK - WARNING: May not work
class Shim : IWin32Window
{
public Shim(System.Windows.Window owner)
{
// Create a WindowInteropHelper for the WPF Window
interopHelper = new WindowInteropHelper(owner);
}
private WindowInteropHelper interopHelper;
#region IWin32Window Members
public IntPtr Handle
{
get
{
// Return the surrogate handle
return interopHelper.Handle;
}
}
#endregion
}
and it's method of use:
namespace System.Windows.Forms
{
public static class WPFInteropExtensions
{
public static DialogResult ShowDialog(
this System.Windows.Forms.Form form,
System.Windows.Window owner)
{
Shim shim = new Shim(owner);
return form.ShowDialog(shim);
}
}
}
I haven't tested this code, but reading around the internet, it appears that you can host Winforms windows inside of a WPF app.
I just found this link on MSDN that has a very detailed description of how to interop a Win32 control/window in a WPF application.
Hope these help you out.
I've been doing this sometimes and didn't encounter any problem.
However i don't really recommend it, you should prefer WPF when you are in a WPF Application.
for exemple if you want application path use this :
System.Reflection.Assembly.GetExecutingAssembly().Location

Resources