i've wrote a API in C# which will be used in vb6 & vbscript and assume my API details is as below:
1. there is a form in the API but it is not exposed to COM
2. there is a method called ShowForm() with code "form1.Show()". This method ShowForm() is exposed to COM so that i can call this method from vb6/vbscript.
So, my problem is:
When i call ShowForm() method in vb6 under a button click event, the form created in C# API shows up but if i instantiate the same class and call the same method in vbscript, the form dont show up.
However, if i change the code in ShowForm(),
- from "form1.Show()" to "form1.ShowDialog()", it will show in both vb6/vbscript. But, it is showed as a modal form where what i want is modeless
OR
- from "form1.Show()" to "Application.Run(form1)". It will shows up in vbscript but not in vb6 (it actually shows up in vb6, but it crash after i close the form in C#)
What can i do to make it works fine in vb6 and vbscript? Your advice is very much appreciated
PS: its not script syntax problem or COM problem. it should be something to do with form.show()/ShowDialog()/Application.Run() or message loop thingy which is something i dont really understand
My code would be more or less something like this.
//C# code
public void ShowForm()
{
m_frm.tempWebBrowser.Navigate("http://stackoverflow.com", "_self", Encoding.Default.GetBytes(""), "Content-Type: application/x-www-form-urlencoded");
m_frm.FormBorderStyle = FormBorderStyle.Sizable;
m_frm.ShowInTaskbar = true;
m_frm.WindowState = FormWindowState.Maximized;
m_frm.tempWebBrowser.Visible = true;
m_frm.Show();
}
//VB6 code, vbscript also using exactly the same code in the event
Private Sub Command1_Click()
Dim tempClass
Set tempClass = CreateObject("myClass.API")
tempClass.ShowForm
End Sub
The form is not shown exactly because of what you suggested...there is no message pump running on the thread. If you are trying to fire-and-forget the C# window from VBScript, I suggest you launch it in a seperate process. It would have the same effect as you've described. If you instead were trying to launch it and wait for the user to interact with it (say you are prompting them for info) then showing to form modally is appropriate.
Related
I have a Windows Desktop App with an auto-update method implemented. I want to show a custom Form (because I need to change the button texts) asking the user if he or she wants to download the new version or not when a new update is detected and I want to "block" all input actions in the Desktop App until the user has made his selection.
After reading Form.ShowDialog() documentation and several topics here saying "ShowDialog() is not making my windows modal" and several answers replying "You need to properly set the owner" I still don't understand how to set this owner. Of course if I make two forms and the first one shows the second, I can "block" the first one doing:
secondForm.ShowDialog(firstForm);
But I don't know how to make that the firstForm blocks all the application to prevent the user using a deprecated version of it.
I tried several approaches like getting the current id process (or trying to get it) and convert it to IWin32Window. But nothing seemed to work.
If you need it, I add here the code I'm using:
FormAsk formAsk = new FormAsk (param1, param2);
formAsk.StartPosition = FormStartPosition.CenterParent;
formAsk.TopLevel = true;
formAsk.TopMost = true;
formAsk.DialogResult = formAsk .ShowDialog();
if(formAsk.DialogResult.Equals(DialogResult.OK))
{
// do stuff
}
else
{
// do other stuff
}
I've also seen lots of solution implementing:
myForm.ShowDialog(this);
But VS start's crying because the types are not compatible. I'm using a MVVM pattern so I can't just set a Form as an owner because I don't have a main form. Just views in .xaml and views controllers in c#.
Edit: I would like to add few comments I learned after facing this issue that may help to understand better my situation.
The main method of the program executes the following:
[STAThread]
static void Main()
{
//stuff
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
try
{
//stuff
STAApplicationContext context = new STAApplicationContext();
Application.Run(context);
}
catch (Exception exc)
{
MessageBox.Show(exc.Message, Localization.Strings.error_popup_title);
Application.Exit();
}
}
}
And this context is the one that generates the service layer and views manager of the application. If I display the Forms in the service layer, showDialog() method can not "block" the input in the views but if I display them from the views (generated and handled by the view manager) i can. There's a communication between views and service, because actions triggered in the views have as consequence a service method call, but in this case the communication I want is the opposite: the service calling the methods in the view controllers to display the Forms by showDialog().
You need to pass an instance of the IWin32Window interface to the ShowDialog method.
IntPtr myWindowHandle = IntPtr(parent.Handle);
IWin32Window w = Control.FromHandle(myWindowHandle);
Do your Stuff in your first Form and whatever button you press to create your second Form just go like this. (Pseudo Code)
button1_click()
{
Form2 = new Form()
Form2.Owner = this;
}
and now from your Form2 you can talk to your Owner with this.Owner.Visible = false for example.
Thats how you make the Owner if thats what you asked for.
Thanks those who tried to help with your replies. However, although your answers probably will work in other circumstances, mine where a bit different.
Finally I achieved to solve it, I needed to do the handle with Forms in a higher level of abstraction. The information managed was retrieved from an asynchronous task so I couldn't use there a showDialog method and block the MainWindow of the application. Instead I did several threads, wait them and eventually show dialogs when I needed. It's not the best approach, but given the context is the only thing I could do.
I have a library that I originally created in Java, but I want to rewrite it (and update it) in C#. I've found IKVM, and I might have to go that route. But my first choice would be to rewrite the library in C#.
The library is a teaching tool reminiscent of turtle graphics or Karel the Robot. So I need to be able to open a non-modal window while still sending commands from the Main method.
To mimic the original, I would like to use code similar to what is shown below to start the program since that would reinforce the typical way that objects are declared and instantiated. As the name implies, MyCustomForm would be a subclass of Form.
MyCustomForm myform = new MyCustomForm(...);
I've searched through the site, but haven't found any other question similar to mine.
Is this possible in C#?
Yes, there are several ways. The easiest would be to call Application.DoEvents periodically. Here's a short example of how you might write it.
static class Program {
static bool formClosed = false;
[STAThread]
static void Main() {
MyCustomForm form = new MyCustomForm();
form.Show();
form.FormClosed += Form_FormClosed;
while(!formClosed) {
Application.DoEvents();
Thread.Sleep(10);
}
}
private static void Form_FormClosed(object sender, FormClosedEventArgs e) {
formClosed = true; }
}
There exists much better solutions than this example, though. The term you want to Google for is "C# game loop" despite that your application is not a game.
No, it is not possible.
Application.Run() starts the event pump which allows your application to respond to events. This includes events that come from the user (e.g. clicks, drags, maximize, close) and events that come from the O/S or your own program (such a request to repaint the window, respond to timers, shut down when the O/S is being shut own). All windows forms applications require this sort of event pump in order to provide typical functionality.
Yes, just use ShowDialog() on your form, simple like that, then when your form closes call Application.Exit(). There is no need for Application.DoEvents();
In my case I'm using this to allow user to login again with another user.
So I can close my main form e then show user login form again
I created a COM Visible DLL in C# that should show a Form after some inputs from the User in the host application (unmanaged). It works fine with ShowDialog(), but ideally the Form should keep running even after the DLL finishes. Because the Form need some Data a separated Project with Main(string[] args) is not an option.
How can I accomplish this? I tried something like that but it didn't worked.
public class FormManager : ApplicationContext
{
FormMain frmMain;
public FormManager()
:base(new FormMain())
{
frmMain = (FormMain)this.MainForm;
frmMain.Closed += new EventHandler(OnFormClosed);
}
public void SetData(object o1, object o2)
{
if (frmMain != null)
{
frmMain.SetData(o1, o2);
frmMain.Show();
}
}
private void OnFormClosed(object sender, EventArgs e)
{
ExitThread();
}
}
I have no idea where Application.Run should be inserted.
I, too, am a little unclear as to what exactly you are trying to do here... but normally, if you are placing the Application.Run somewhere in that code, it would be in place of this line:
frmMain.Show();
Of course, by using Application.Run you will be freezing this code (the thread that calls Application.Run) until the form in question closes... So maybe that doesn't really accomplish what you want (it is, indeed, unclear).
Edit After Clarification of Question
Here's the thing about COM in .NET that was not true about previous iterations of Microsoft languages. When you call an assembly in .NET via COM (OLE) the calling assembly subsumes the COM exposed code into its runtime. In other words, when you look in the Task Manager, you won't see both of your assemblies running! You'll only see the one that did the calling. Thus, when you close the main assembly, you close any running code attached to it, including your COM code.
There is one way around this, but it's not simple. In short, you would need to:
Launch your second process (you could, for instance, use a Process.Start())
Use the first process to look inside the ROT (Running Objects Table) and locate the second assembly
Communicate freely via COM (OLE) and pass your data
At this point, the two assemblies are running in separate runtimes, which will allow you to produce forms in the second assembly that will not close when the first assembly closes. That, as I understand it, is what you're looking for.
If you want to try this route, do a little Googling for the ROT and try some sample code. If you have questions about that let me know!
I have an intermittent problem where I cannot create an instance of the System.Windows.Forms.Form class in my C# library. I log exceptions in my program, but unfortunately nothing was recorded for this problem. It might not be relevant, but my library, called via COM (i.e. there is no Main method).
So I threw a test exception, and it was caught and logged successfully -- so I'm thinking that maybe something screwy is going on in the message loop?
Update 1
The Form is created using the following code in a Thread which has ApartmentState.STA set.
someForm = new Form();
someForm.Visible = false;
someForm.Text = "Hello world!";
It's just a guess, since you've provided no code and little context, but might you be missing the STAThread attribute on your entry point method (Main)? This is necessary for WinForms apps that make use of COM.
I am having a problem that is strange to me but hopefully is not so strange to someone else. : ) Some background: I am working on a simple IM client that allows the user to broadcast messages to multiple recipients. The goal is to create a chat form for each of the recipients containing the text of the broadcast message, then show that form only if the recipient responds to the broadcast-er. However, when the application receives a response then attempts to locate the form for that particular chat session (using Application.OpenForms) it cannot find it UNLESS I .Show at the time it is created. I would like to avoid having to show this form when it is created because this means that the user will see a flash on the screen. The form doesn't seem to really be created until I show it, but it would seem there has to be a way to do this without showing first. Can anyone assist?
I can provide code snippets if needed, I didn't in this post because this feels more like a conceptual misunderstanding on my part than a bug in the code. Thanks in advance!
Instead of using the form as a base class, do it the other way, create a class that can reference a form. That way, you'll keep the class informed of the content, and reflect it on the form (if it's initialized), not the other way around.
You shouldn't rely on Forms as a basis of your objects. Using Application.OpenForms should be unnecessary.
public class Contact
{
string displayname = String.Empty;
List<Message> history = new List<Message>();
MessageForm theform = new MessageForm(this);
public void OnEvent(Message msg)
{
if(msg.Sender != me && !theform.Visible)
theform.Show();
}
public void Tell(string message)
{
}
}
etc
Keep your contacts in some sort of list, and things should be relatively simple.
(Be aware that windows forms aren't thread-safe, and will throw an exception if you try to alter any properties of any of the controls from a different thread than main)
windows form has methods like Hide(),Show() and Activate(). use these method for your problem.
Why not store a reference to the form with the chat session and use that to call .Show() when you need to display the form:
session.form.Show();
You can then create the form without showing it and you don't have the overhead of calling Application.OpenForms each time you want to reference it.
I know this is stating the obvious but OpenForms won't find a form that hasn't been shown because it's not open.
As the form handle does not get created until the form is shown you can assign it manually like so:
mf = new MainForm();
/* Need to assign a handle to MainForm instance manually
as handle does not get created until form is shown */
IntPtr handle = mf.Handle;