Project A contains a WPF Window (Data Entry Form) with a stack panel of commands that launch various reports. The menu is a dynamically built list from a database. What I’m attempting to do is launch the corresponding WPF window based on the CommandText associated with the menu choice. I’d like to create a single function that accepts the name of the WPF Window (CommandText) and launches a new instance of the window by name.
I’ve found examples of how to launch methods on classes, but can’t seem to find a method that works with a window. I know it can be done with a switch and just map all the windows, but there are 60-70 and I was trying to avoid bloat.
I’m failed repeatedly trying to use the TypeOf and Activator.CreateInstance. Suggestions? Is this even possible?
Activator works fine for me. What error do you have? Try if below code will work for you
private void Button_Click(object sender, RoutedEventArgs e)
{
Window wnd = (Window)CreateWindow("WpfApplication1.Window2");
wnd.Show();
}
public object CreateWindow(string fullClassName)
{
Assembly asm = this.GetType().Assembly;
object wnd = asm.CreateInstance(fullClassName);
if (wnd == null)
{
throw new TypeLoadException("Unable to create window: " + fullClassName);
}
return wnd;
}
You might try this:
string windowClass = "CreateWindow.MyWindow";
Type type = Assembly.GetExecutingAssembly().GetType(windowClass);
ObjectHandle handle = Activator.CreateInstance(null, windowClass);
MethodInfo method = type.GetMethod("Show");
method.Invoke(handle.Unwrap(), null);
The code above assumes that your window is called "CreateWindow.MyWindow" (with namespace prefix) and that the type "CreateWindow.MyWindow" is in the currently executing assembly.
Related
Looking for help on this project, I am new and might not know all terms so please bare with me.
Project contains multiple winforms panels. Start one is my dashboard then I have others that do specific functions/features. What I need to be able to do is call up the separate winforms from an outside bat file that comes from a third party software (I have no control over them).
So what I would like to do is this programname.exe runs the dashboard programname.exe -a runs a different winform with in my program. I am not sure what this is called to do this.
Any help would be great even if a place to go and look it up.
namespace Versi_Send_Email
{
public partial class DashBoard : Form
{
public DashBoard()
{
InitializeComponent();
}
private void DashBoard_Load(object sender, EventArgs e)
{
INIFile inif = new INIFile(#"c:\test\mailsettings.ini");
sitetxtbox.Text = inif.Read("Properties", "site");
emailtotxtbox.Text = inif.Read("Properties", "personto");
cctotxtbox.Text = inif.Read("Properties", "ccto");
bcctextbox.Text = inif.Read("Properties", "bcto");
}
This could be a switch statement or a simple if-else statement. In WinForms application and in the main form loaded event, you can read the command line arguments like this
string[] args = Environment.GetCommandLineArgs();
foreach(string arg in args){
if (arg=="EmployeeForm")
{
EmployeeForm.ShowDialog()
else if (arg=="Department")
Department.ShowDialog()
}
//after all arguments are read, you can simply kill the application and main window will never appear
Application.Current.Shutdown();
return;
VS now comes with an interactive window, but unlike running the raw CSI.EXE Roslyn process, Visual Studio adds IntelliSense and a few other features such as being able to load in the current project.
I want to write a VS plug-in that tracks all text editor changes in this window. Is this possible? What I'm looking for is something akin to PreviewKeyDown/PreviewTextInput WPF events. Can I get those on the C# interactive window and, if so, how?
Here's how far I got so far:
var dte = Shell.Instance.GetComponent<DTE>();
foreach (Window window in dte.MainWindow.Collection)
{
if (window.Kind.ToUpper().Contains("TOOL"))
{
if (window.Caption == "C# Interactive")
{
WpfWindow wpfWindow = (WpfWindow)HwndSource.FromHwnd((IntPtr) window.HWnd).RootVisual;
for (int i = 0; i < VTH.GetChildrenCount(wpfWindow); ++i)
{
// now what?
}
}
}
}
Here is some code that will get an IWpfTextViewHost reference on the C# interactive Window. From there, you can have access to all text services from Visual Studio: Text lines, Text buffer, etc. (or you can hook directly on WPF's controls, which I don't recommend)
// get global UI shell service from a service provider
var shell = (IVsUIShell)ServiceProvider.GetService(typeof(SVsUIShell));
// try to find the C# interactive Window frame from it's package Id
// with different Guids, it could also work for other interactive Windows (F#, VB, etc.)
var CSharpVsInteractiveWindowPackageId = new Guid("{ca8cc5c7-0231-406a-95cd-aa5ed6ac0190}");
// you can use a flag here to force open it
var flags = __VSFINDTOOLWIN.FTW_fFindFirst;
shell.FindToolWindow((uint)flags, ref CSharpVsInteractiveWindowPackageId, out IVsWindowFrame frame);
// available?
if (frame != null)
{
// get its view (it's a WindowPane)
frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocView, out object dv);
// this pane implements IVsInteractiveWindow (you need to add the Microsoft.VisualStudio.VsInteractiveWindow nuget package)
var iw = (IVsInteractiveWindow)dv;
// now get the wpf view host
// using an extension method from Microsoft.VisualStudio.VsInteractiveWindowExtensions class
IWpfTextViewHost host = iw.InteractiveWindow.GetTextViewHost();
// you can get lines with this
var lines = host.TextView.TextViewLines;
// and subscribe to events in text with this
host.TextView.TextBuffer.Changed += TextBuffer_Changed;
}
private void TextBuffer_Changed(object sender, TextContentChangedEventArgs e)
{
// text has changed
}
Note "Microsoft.VisualStudio.VsInteractiveWindow" assembly is not specifically documented but the source is open: http://sourceroslyn.io/#Microsoft.VisualStudio.VsInteractiveWindow
I have loaded a windows form from a .dll and it is displaying correctly and I can execute methods created in that .dll.
Note the text label in the Form1 window. In my main window I click debug then select an option that should change the text label to another string. However the string refuses to change. I have checked that the method in Form1 is firing, and that the label.text has been changed yet the display never changes.
NOTE: This also happens with other controls I have tested (textbox/listbox etc).
public void Command(string cmd, string param1, string param2, string param3)
{
if (cmd == "TEST")
{
this.label1.Text = "This should now change";
MessageBox.Show("DONE");
}
}
The MessageBox shows as expected, the label.text has changed, and all events fire correctly (I created a click event for the label which works), it just seems that the label is not actually updating. I have tried using Refresh on the label also.
One other question if possible, :) Is there a particular way that I can create a callback on the main form that I can supply to Form1? Some kind of delegate I presume?
I load from the dll this way
try
{
Type interfaceType = typeof(IPlugin);
// Fetch all the types that implement the interface IPlugin and are a class
Type[] types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(p => interfaceType.IsAssignableFrom(p) && p.IsClass)
.ToArray();
foreach (Type type in types)
{
// Create a new instance of all found types
PluginLists.All.Add((IPlugin)Activator.CreateInstance(type));
Console.WriteLine("Plugin loaded: {0}", type.ToString());
}
Console.WriteLine("Plugins loaded: {0}", PluginLists.All.Count);
}
And, each dll implements a method which starts up the form
public void Plugin_Start(DockPanel _dockPanel)
{
//
var miscForm = new frmMiscellaneousTest();
miscForm.Show(_dockPanel, DockState.DockRight);
}
Thank you for any help given, it is much appreciated.
I was using the below code (commented out now) to show the form, but I had totally forgotten that I was already in the form and did not need to instantiate it again, :(
public void Plugin_Start()
{
//
this.Show();
//var MiscForm = new frmMiscellaneousTest();
//MiscForm.Show();
}
So I've got two tool windows in my visual studio extension (package) and I'd like to open up the second window via a button on the first window.
I expected this to be explained here: "How to: Open a Tool Window Programmatically", but it wasn't.
You should use either Package.FindToolWindow or IVsUIShell.FindToolWindow to find or create a tool window.
If used from your own package (or if you have a reference to the package, just put it there instead of this):
private void OpenFromPackage()
{
ToolWindowPane window = this.FindToolWindow(typeof(MyToolWindow), 0, true); // True means: crate if not found. 0 means there is only 1 instance of this tool window
if (null == window || null == window.Frame)
throw new NotSupportedException("MyToolWindow not found");
IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame;
ErrorHandler.ThrowOnFailure(windowFrame.Show());
}
If you can't do it from your package, or don't have a reference to it, use IVSUIShell:
private void OpenWithIVsUIShell()
{
IVsUIShell vsUIShell = (IVsUIShell)Package.GetGlobalService(typeof(SVsUIShell));
Guid guid = typeof(MyToolWindow).GUID;
IVsWindowFrame windowFrame;
int result = vsUIShell.FindToolWindow((uint)__VSFINDTOOLWIN.FTW_fFindFirst, ref guid, out windowFrame); // Find MyToolWindow
if (result != VSConstants.S_OK)
result = vsUIShell.FindToolWindow((uint)__VSFINDTOOLWIN.FTW_fForceCreate, ref guid, out windowFrame); // Crate MyToolWindow if not found
if (result == VSConstants.S_OK) // Show MyToolWindow
ErrorHandler.ThrowOnFailure(windowFrame.Show());
}
When you create a new package with toolwindow support, you get a single toolwindow and a command that displays it. This command is handled in the package class with the ShowToolWindow method.
Examining that, you'll see that the base package object has a FindToolWindow method that you can use to find (and create if needed) any toolwindow you have implemented in your package. That FindToolWindow method is just a nice wrapper around the IVsUIShell.FindToolWindow method, which is what ultimately gets invoked when displaying any toolwindow.
So instead of using the old EnvDTE automation interface, I would recommend using the lower level services built into the actual package object.
Here's how I solved it, the following code is the code-behind method of the button on the first window:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var dte = Package.GetGlobalService(typeof(DTE)) as DTE;
if (dte == null) return;
var window = dte.Windows.Item("{WindowGUID}");
window.Visible = true;
}
You should find the "WindowGUID" in the Guids class and above the class of the ToolWindow.
I have a WPF window that I am launching from inside of a winform app. I only want to allow once instance of that WPF window to be open at a time, and not warn that user if they try to open it again.
I am having a problem however trying to search for that WPF window being open because the window is being launched from a winform. What I normaly do is when searching for a winform, I search for any instances of that winform existing in the Application.Current.OpenForms, and when in WPF I search for Application.Current.Windows
The problem I have is that System.Windows.Application.Current is null when launched from inside of a winform, so I can't search for the WPF window that way. Is there any better way of searching for an existing instance of an open window?
My Code:
if (System.Windows.Application.Current != null)
{
foreach (System.Windows.Window win in System.Windows.Application.Current.Windows)
{
if (win is frmCaseWpf)
{
MessageBox.Show("You may have only one active case open at a time.", "Open Case",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
win.WindowState = System.Windows.WindowState.Normal;
win.Focus();
win.Activate();
return;
}
}
}
Instead of searching the static application objects, you could instead just track this within your window, with a single static variable. Just keep a variable in the window:
private static frmCaseWpf openWindow = null; // Assuming your class name is frmCaseWpf
When you create a window, either in the initialize routines, or OnLoaded, depending on how you want it to work..:
partial class frmCaseWpf {
public frmCaseWpf {
this.OnLoaded += frmCaseWpf_OnLoaded;
}
private void frmCaseWpf_OnLoaded(object sender, RoutedEventArgs e)
{
if (this.openWindow != null)
{
// Show message box, active this.openWindow, close this
}
this.openWindow = this;
}
}
If you want this window to be reusable, make sure to set this.openWindow = null; when you close the window, as well.
Here's something that's working for me.
private About aboutWin;
private void AboutOpenClicked(object sender, RoutedEventArgs e)
{
if(aboutWin == null)
{
aboutWin = new About();
aboutWin.Closed += (a, b) => aboutWin = null;
aboutWin.Show();
}
else
{
aboutWin.Show();
}
}
It would be better make the frmCaseWpf class a singleton. That way you can't create another instance
Rather than try to search for a Window instance, many people use a session- (or system-) wide "Mutex" or a Mutual Exclusion lock. I was going to rewrite one for you, but I found a good codeproject article demonstrating the technique. It's not complex and very simple.
http://www.codeproject.com/KB/cs/SingleInstanceAppMutex.aspx?msg=2908697
Sneak peek:
[STAThread]
static void Main()
{
bool onlyInstance = false;
Mutex mutex = new Mutex(true, "UniqueApplicationName", out onlyInstance);
if (!onlyInstance) {
return;
}
Application.Run(new MainForm);
GC.KeepAlive(mutex);
}
Hope this helps.
(edit: of course you'll have to modify this slightly for your particular use-case, but it demos the general idea)
I am not really a 'proper' programmer, however I have achieved this in a WPF application (not from a winforms one) by using the following:
Dim wdwDetails As New detailsNew()
Private Sub openNewDetails(ByVal recordID As String)
wdwDetails.Owner = Me
wdwDetails.recordID = recordID
wdwDetails.WindowStartupLocation = Windows.WindowStartupLocation.CenterOwner
wdwDetails.Show()
End Sub
Essentially because I am creating the window object outside of the sub that opens it, there will only be a single window. Any new call to the window open sub will use the same object. But I guess that is what Thomas is referring to also.
Like I said, not sure if this will help you or not though.
You can use XXXwindown.isLoad to check if window is loaded before you create a new window:
if ( !ChildWindow.IsLoaded)
{
childWindow= new ChildWindow();
childWindow.Show();
}