Create Revit Plugin With Windows Form - c#

I've been trying to create a plugin for Revit 2017 with Visual Studio 2015 with windows Form.
Unfortunately I've not found any documentation online for doing so (if you have links, i'll be happy to give them a look)
I've built a simple form using a Listbox and a select button
The Listbox shows all the doors in a Revit project
The select button selects all the selected doors from the Listbox and selects them in the Revit project (that's a lot of selects ...)
It's a test solution, to see how it all works.
WeWillSee class is the class implementing the main RevitAPI function Execute:
using System;
using Autodesk.Revit.UI;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
namespace Test2 {
[Transaction(TransactionMode.Manual)]
class WeWillSee : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
UIApplication uiapp = commandData.Application;
/*UIDocument uidoc = uiapp.ActiveUIDocument;
Document doc = uidoc.Document;*/
try
{
System.Windows.Forms.Application.EnableVisualStyles();
System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
System.Windows.Forms.Application.Run(new Form(commandData));
//System.Windows.Forms.Form wf = new Form1(uiapp);
}
catch (Exception e)
{
TaskDialog.Show("Error", e.ToString());
return Result.Failed;
}
return Result.Succeeded;
}
}
}
The Form I want to open (the rest in not important):
namespace Test2
{
public partial class Form : System.Windows.Forms.Form
{
private UIApplication uiapp;
private UIDocument uidoc;
private Document doc;
public Form(ExternalCommandData commandData)
{
InitializeComponent();
uiapp = commandData.Application;
uidoc = uiapp.ActiveUIDocument;
doc = uidoc.Document;
}
And finally the Program.cs file (the one causing me problems):
namespace Test2
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1(/*Can't call ExternalCommandData on static class*/));
}
}
}
Thanks for any help you can offer! :)

I don't think you even need the Program.cs class file in your project the way you have it written.

Here is a simple Revit add-in implementing an external command that creates and displays a Windows form on the fly:
http://thebuildingcoder.typepad.com/blog/2012/05/the-schedule-api-and-access-to-schedule-data.html

You don't need to do the Application.Run kind of things (that's only for standalone windows applications). You don't need the Program.cs thing at all.
You can just do as you started to:
Form1 wf = new Form1(uiapp);
if (wf.ShowDialog() == System.Windows.Forms.DialogResult.OK)
return Result.Success

Related

How to fix "the name ... does not exist in the current context error" in a C# Windows Application?

I made a simple C# app some time ago (as a console app), now I'm making a new version as a Windows Application in Sharpdevelop. I'm pretty new to C# and graphical applications and I got this confusing error.
What I want to do: on the UI, I have a button which checks if COM3 serial port is opened and if not, open it, if it's open, close it and write a message about it on a listbox. The problem is that wherever I put the serial handling part, I get errors about it not being in the current context, or get an "Object reference not set to an instance of an object" error.
I created the app with Sharpdevelop's template, so I have code in several files:
Program.cs:
using System;
using System.Windows.Forms;
namespace SerialClient_v2
{
class Program
{
[STAThread]
void Main(string[] args)
{
SerialHandle SH=new SerialHandle();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm(SH));
}
}
}
Mainform.cs:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.IO.Ports;
namespace SerialClient_v2
{
public partial class MainForm : Form
{
private SerialHandle _sh;
public MainForm(SerialHandle sh)
{
InitializeComponent();
_sh = sh;
}
void ConnectBtnClick(object sender, EventArgs e)
{
try{
if(_sh._serialPort.IsOpen){
listBox1.Items.Add(DictionaryClass.strDisconnecting);
//Program._serialPort.Close();
}
else{
//Program._serialPort.Open();
listBox1.Items.Add(DictionaryClass.strConnecting);
}
}
catch (Exception ex)
{
listBox1.Items.Add("Error opening/writing to serial port :: " + ex.Message);
}
}
}
}
SerialHandle.cs: (this is my file, the stuff which used to be in the Main function in the console app comes here. DictionaryClass is just a translation helping class to easily switch between two languages)
using System;
using System.IO.Ports;
namespace SerialClient_v2
{
public class SerialHandle
{
bool SerialComm;
public SerialPort _serialPort;
public SerialHandle()
{
SerialPort _serialPort = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);
_serialPort.Handshake = Handshake.None;
_serialPort.ReadTimeout = 1000;
_serialPort.Open();
}
}
public static class DictionaryClass{
//DICTIONARY:
public static string strConnecting="Initializing serial communication!";
public static string strDisconnecting="Closing serial port";
}
}
The problem is that SH._serialPort.IsOpen can't be checked as it's not in the context. SH is created in the Main function, so I don't get why it's not seen. Sorry for the beginner question.
The simplest solution is to pass the SH object instance to the main form:
In SerialHandle.cs (use _sh._serialPort.IsOpen):
private SerialHandle _sh;
public MainForm(SerialHandle sh)
{
_sh = sh;
}
In Program.cs:
Application.Run(new MainForm(SH));
Simply move the initialization statement SerialHandle SH=new SerialHandle(); into MainForm.cs class

Visio Interop Application events causing undesired behaviour

I'm trying to use Visio Application events. When instantiating a new Application object, and setting any event (i.e. BeforeDocumentClose), this appears to result in unable to restore the Visio window after minimizing it.
I'm using VS/C# 2013, Windows Forms, Visio 2013 (on Windows 7). Though my main code project is huge implementing exchange between various office applications using Add-Ins, the following simple code reproduces the same issue. It is a Windows Forms project (with added Reference to Microsoft.Office.Interop.Visio).
using Visio = Microsoft.Office.Interop.Visio;
Visio.Application app;
bool initialised = false;
private void visioButton_Click(object sender, EventArgs e)
{
init();
app.Documents.Add("c:\\test.vst"); // creates new document from template
}
void init()
{
if (!initialised)
{
// only initialise once
app = new Visio.Application();
app.BeforeDocumentClose += app_BeforeDocumentClose;
initialised = true;
}
}
void app_BeforeDocumentClose(Visio.Document doc)
{
}
Issue #1: This is the main issue. Creating one or more Visio Documents, the Visio Window is not maximized after being minimized. No Exceptions thrown as far as I can see. Windows just does it's audible error 'ping'.
Issue #2: This is a secondary issue. Creating two or more Visio Documents, hovering over the Windows Taskbar, the preview windows show the waiting cursor instead of normal document preview.
Conditions: Issue #1 only occurs when using an event on the Application. Document, Page/Shape events don't cause any problem. All events are captured fine. Issue #2 always occurs, but this is less important for me.
I've been searching for this issue for a while, but can't find anything related to it, so any help is greatly appreciated.
I am not quite sure what is causing Visio to not respond to restore, but you can try the approach with "AddAdvise" instead:
[ComVisible(true)]
public partial class Form1 : Form, Visio.IVisEventProc
{
public Form1()
{
InitializeComponent();
}
Visio.Application app;
bool initialised = false;
private void button1_Click(object sender, EventArgs e)
{
init();
app.Documents.Add("C:\\test.vst"); // creates new document from template
}
void init()
{
if (!initialised)
{
// only initialise once
app = new Visio.Application();
// app.BeforeDocumentClose += app_BeforeDocumentClose;
app.EventList.AddAdvise(DocCloseEventCode, this, null, null);
initialised = true;
Application.DoEvents();
}
}
const short DocCloseEventCode = unchecked((short)Visio.VisEventCodes.visEvtDoc + (short)Visio.VisEventCodes.visEvtDel);
object Visio.IVisEventProc.VisEventProc(short eventCode, object source, int eventID, int eventSeqNum, object subject,object moreInfo)
{
if (eventCode == DocCloseEventCode)
app_BeforeDocumentClose(subject as Visio.Document);
return null;
}
void app_BeforeDocumentClose(Visio.Document doc)
{
}
}
To provide the completed solution for multiple events using Nikolay's advice, here is the completed code including both events and (de)initialisation of Visio Application, and without using templates. (Note that the Message boxes may turn up in the background, behind the Visio window.)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Visio = Microsoft.Office.Interop.Visio;
namespace VisioInteropTest
{
[ComVisible(true)]
public partial class TestForm : Form, Visio.IVisEventProc
{
Visio.Application app;
bool initialised = false;
// all AddAdvise events:
// https://msdn.microsoft.com/en-us/library/office/ff768620.aspx
const short appCloseEventCode = (short)(Visio.VisEventCodes.visEvtApp | Visio.VisEventCodes.visEvtBeforeQuit);
const short docCloseEventCode = (short)(Visio.VisEventCodes.visEvtDoc | Visio.VisEventCodes.visEvtDel);
public TestForm()
{
InitializeComponent();
}
private void visioButton_Click(object sender, EventArgs e)
{
if (init())
{
app.Documents.Add("");
}
}
bool init()
{
if (!initialised)
{
app = new Visio.Application();
app.EventList.AddAdvise(appCloseEventCode, this, null, null);
app.EventList.AddAdvise(docCloseEventCode, this, null, null);
initialised = true;
}
return initialised;
}
object Visio.IVisEventProc.VisEventProc(short eventCode, object source, int eventID, int eventSeqNum, object subject, object moreInfo)
{
switch (eventCode)
{
case appCloseEventCode: app_BeforeAppClose((Visio.Application)subject); break;
case docCloseEventCode: app_BeforeDocumentClose((Visio.Document)subject); break;
}
return null;
}
void app_BeforeAppClose(Visio.Application app)
{
initialised = false;
MessageBox.Show("App closed");
}
void app_BeforeDocumentClose(Visio.Document doc)
{
MessageBox.Show("Doc closed");
}
}
}

Start WPF Application in Console Application

Is it possible to start WPF Application in Console mode?
public partial class App : Application
{
public App()
{
InitializeComponent();
}
}
<Application x:Class="WPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Application>
[STAThread]
static void Main(string[] args)
{
if (args.Length > 0)
{
switch (args[0].ToLower())
{
case "/g": RunApplication(); break;
}
}
}
private static void RunApplication()
{
var application = new System.Windows.Application();
application.Run(new App());
}
It will show Argument type 'WPF.app' is not assignable to parameter type 'System.Windows.Window'.
Any solution to work around it??
Any different between
1.public partial class App : Application
2.public partial class App : Window
You could declare a Window and then start your app this way:
var application = new System.Windows.Application();
application.Run(new Window());
EDIT:
You seem a bit confused, so let me explain:
Say you have a program:
using System;
namespace ConsoleApplication
{
class Program
{
[STAThread]
static void Main(string[] args)
{
RunApplication();
}
private static void RunApplication()
{
var application = new System.Windows.Application();
application.Run();
}
}
}
This will run a WPF application with no Window.
If, on the other hand, you pass a Window into application.Run(), you will get a WPF window. App should not derive from Window, since it should derive from Application.
Application.Run method either takes no arguments or a Window. It does not take Application. Therefore, if you want to start a previously created Application, as you have over there, you should do something like this:
private static void RunApplication()
{
var application = new App();
application.Run(); // add Window if you want a window.
}
Lastly, if you want to just use application.Run() and not have to pass a specific Window, just declare a starting Window in your Application XAML using StartupUri:
<Application x:Class="WPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="SomeWindow.xaml">
</Application>

Error 'System.Diagnostics.Process' does not contain a definition for 'GetProcesses' in visual studio 2008 c#

How to kill process in visual studio 2008 c#? I tried the below code
using System;
using System.Windows.Forms;
using System.Diagnostics;
namespace GR
{
public partial class Form1 : Form
{
public Login loginform;
public EntryForm entryform;
public Form1()
{
//userName = "";
InitializeComponent();
//CheckForInternetConnection();
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.StartsWith("Bin_"))
{
clsProcess.Kill();
}
}
loginform=new Login();
entryform = new EntryForm();
Controls.Add(loginform);
Controls.Add(entryform);
loginform.Show();
entryform.Hide();
loginform.entryForm = entryform;
entryform.loginForm = loginform;
}
}
}
I am getting this error
'System.Diagnostics.Process' does not contain a definition for
'GetProcesses'.
Any suggestion ?
The ompact Framework doesn't support it, which is why you get the error. The fallback is to use the Toolhelp APIs, like the SDF does. Take a look at the ProcessEntry class in the SDF. Specifically the GetProcesses() method and the subsequent Kill() method.

Changing a picture box visibility from C# code

So I tried to create a new form and reference it...the compiler didn't mind this but it clearly wasn't changing the visibility of my picturebox. this is how I was calling my method found in my form, FROM my c# script.
Form1 updateForm = new Form1();
updateForm.setLights();
It called the method, and seemed like it worked! Until I read a post about instancing forms, and how by creating a "new" instance of Form1, that anything referenced by my updateForm would not change what I would see on my Form1.
So what I need to do is to call the function in setLights() which is in my Form1, and get it to change the visibility of my image on that form, from my C# code. Please see below (i understand the issue of the instancing problem mentioned above, but I left it in so that hopefully it will give better insight into what I am "trying" to do :) ALSO, please keep in mind that setLightCall() is running in a separate thread. Thanks in advance!
This code is also in my main c# script, and is the main function that I use to call my threads
static void Main(string[] args)
{
Thread FormThread = new Thread(FormCall);
FormThread.Start();
Thread setLightThread = new Thread(setLightCall);
setLightThread.Start();
log4net.Config.XmlConfigurator.Configure();
StartModbusSerialRtuSlave();
}
This code is in my main C# script
public void setLightCall(Form1 parent)
{
Form1 updateForm = new Form1();
while(true)
{
updateForm.setLights();
}
}
The below code is in my form1
public void setLights()
{
Input1GreenLight.Visible = false;
}
Here is an example of what I think you are wanting to try. Note the use of Invoking and delegates to be able to access the PictureBox's Visible method. I had to add the System.Windows.Forms Namespace to the Console Application to be able to access the instance of the Form that was created in the FormThread Method, this is assuming that you only have 1 Form in your FormCollection.
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Windows.Forms;
namespace ConsoleApplication59
{
class Program
{
static void Main(string[] args)
{
Thread FormThread = new Thread(FormCall);
FormThread.Start();
Thread.Sleep(2000); //Sleep to allow form to be created
Thread setLightThread = new Thread(setLightCall);
setLightThread.Start(Application.OpenForms[0]); //We can get by with this because just one form
Console.ReadLine();
}
public static void setLightCall(object parent)
{
Form1 updateForm = (Form1)parent;
while (true)
{
updateForm.Invoke(updateForm.setLights, new object[] { false });
}
}
public static void FormCall()
{
Application.Run(new Form1());
}
}
}
Form1
public partial class Form1 : Form
{
public delegate void Lights(bool state);
public Lights setLights;
public Form1()
{
InitializeComponent();
setLights = new Lights(setLightsDelegate);
}
public void setLightsDelegate(bool state)
{
Input1GreenLight.Visible = state;
}
}

Categories