I have created an Excel Addin project in C#. Now the solution contains a file ThisAddin.cs, which has a class ThisAddin. Later I have added an item called Form to the same solution. In Form, when I click on a button, for that button click event i want to call a method inside ThisAddin.cs file.
namespace ExcelAddIn
{
public partial class ThisAddIn
{
public void RefreshExcelData()
{
}
}
}
Now in MyForm.cs, while trying to create an object for ThisAddin class there is a compilation error that Thisaddin class doesn't have a constructor that takes 0 arguments.
private void btnUploadTestCases_Click(object sender, EventArgs e)
{
ThisAddIn objrefresh = new ThisAddin();
}
What am I missing here?
You are approaching the problem from the wrong direction. When you click the button, you don't want to create a new add-in, what you really want is to access the add-in instance which is created for you by VSTO when Excel starts up, which is accessible via Globals.ThisAddIn.
Change your code in the Form to the following:
private void btnUploadTestCases_Click(object sender, EventArgs e)
{
var addIn = Globals.ThisAddIn;
addIn.RefreshExcelData();
}
... and it should work a charm.
That being said, is there a good reason for this a method to be on ThisAddIn? In general, ThisAddIn should be used to wire up and tear down the add-in when Excel starts up / shuts down, and I would recommend to put as little logic in there as possible.
Use this code:
Globals.ThisAddIn.Application.StatusBar = "Referesh clicked!!!."; Globals.ThisAddIn.RefreshExcelData();
Just make sure your function remains public.
Related
I am working on a project where I have a Visual Studio C# Excel Add-in project (VSTO). From the VSTO code I am adding a Ribbon with a button to Excel (Office.IRibbonExtensibility).
From the so added button in Excel I can make callback calls to methods in the C# code (in the VSTO add-in).
I am looking (and not finding) for the way to call VBA (sub of function) from that button to VBA code that is in the Excel file.
In other words, from that described button I know how to call code that is in C# but don't know how to call VBA code that is in the Excel file itself.
I spent quite some time searching for information and testing some blind ideas but no success either finding anything or getting some good results from my tests.
I would appreciate a kick start in the right direction.
Not sure if this the right place and way to add this later (2/10/20 #17:08 GMT -7) note (after getting the answers below). I have created a small demo project and uploaded it on github. There is a video (mp4) file in the project too to show how it works.
https://github.com/MNemteanu/ExcelVSTOAddInDemo
This is possible.
You have to note that VBA code is stored in particular workbook whilst VSTO add-in is loaded within application, despite of the active workbook. And none of both knows about each other, unless developer does.
In order to implement such interaction you have to know following:
1. Macro holding workbook's name;
2. Macro name.
Knowing this you will be able to apply solution posted in 3d comment.
Below is an example.
Prerequisites:
1. I have prepared macro-enabled workbook "VBA.xlsm";
2. This workbook has a macro called "Foo" in regular module.
Implementation:
1. Create new VSTO add-in;
2. Add Ribbon (Visual Designer) called "Ribbon1" and setup it to have "Custom" ControlIdType (to be a separate tab called "Test");
3. Add a button named "callVBA" to that ribbon which will check the name of the book and try to run workbook's macro.
I didn't add any code to ThisAddIn.cs class. The only code I used - is in the button click event handler in the Ribbon class:
public partial class Ribbon1
{
private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
{
}
private void callVBA_Click(object sender, RibbonControlEventArgs e)
{
if (Globals.ThisAddIn.Application.ActiveWorkbook.Name == "VBA.xlsm")
{
Globals.ThisAddIn.Application.Workbooks["VBA.xlsm"].Application.Run("Foo");
}
}
}
That's pretty easy, but requires that you have prepared prerequisites.
How it works:
Update 1
Here is more complex approach which checks open workbooks and enables/disables specific button based on whether there is a workbook with needed macro. It also checks newly opened and handles recently closed workbooks with only one Workbook_Activate event.
If you don't do any check - you may get a System.Runtime.InteropServices.COMException with a
Message=Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus.
public partial class Ribbon1
{
private bool vbaMacroFound;
private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
{
CheckButtons();
Globals.ThisAddIn.Application.WorkbookActivate += new Excel.AppEvents_WorkbookActivateEventHandler(Workbook_Activate);
}
private void callVBA_Click(object sender, RibbonControlEventArgs e)
{
if (vbaMacroFound)
{
Globals.ThisAddIn.Application.Workbooks["VBA.xlsm"].Application.Run("Foo");
}
}
private void Workbook_Activate(Excel.Workbook Wb)
{
CheckButtons();
}
private void CheckButtons()
{
vbaMacroFound = false;
this.callVBA.Enabled = false;
this.callVBA.ScreenTip = "There is no specified macro in none of active workbooks";
foreach (Excel.Workbook book in Globals.ThisAddIn.Application.Workbooks)
{
if (book.Name.Equals("VBA.xlsm"))
{
this.callVBA.Enabled = true;
this.callVBA.ScreenTip = "Call the sub from VBA";
vbaMacroFound = true;
}
}
}
}
I'm setting up a tool for a randomized generator for some friends. I'm working with Windows Forms for the first time and am having trouble with the Button_Click event handler. The problem is this:
The project is set up with project.scripts.subset stylization. Form1.cs is a partial class, and I'm working within the confines of the event handlers. Button1_Click(object sender, EventArgs e) Does not seem to recognize the methods of other classes, despite having the proper using set up. I'm trying to determine if there's a means of fixing this, or determining what I am doing wrong.
Unfortunately, I haven't tried too much due to inexperience in using buttons. Originally, I attempted to make the handlers in the Form1.Designer.cs and switched when I realized pressing the buttons in the [Design] generates a class to handle that information for you.
using System;
using System.Windows.Forms;
using project.code.location1;
namespace project
{
partial class Form1: Form
{
public Form1()
{
...
}
void Button1_Click(object sender, EventArgs e)
{
GenerateA();
}
...
}
}
namespace project.code.location1
{
class foo
{
public void GenerateA()
{
...
}
}
}
The expected result is for GenerateA() in class Foo to be usable by the event handler of Button1_Click(), but for some reason it's continually throwing an error message stating that GenerateA() does not exist. In a Windows Application, it does not throw this error message in the system when the same function is called elsewhere.
The way you are using GenerateA() it needs to be a static method in a static class and you need to use either foo.GenerareA() or add foo to your using clause.
I'm pretty new to programming with Class Libraries and I think my question is kind of complicated.
I'm working on my own exception logger. This exception logger has static routines and functions so I don't have to declare it in each of my other classes. (Is this btw a good idea? :/ ) Whenever something goes wrong, a routine is called in the exception logger class. This routine determines what the error message should be based on the number I passed to it.
My goal is as follow:
The exception logger should trigger an event in the GUI. The event in the GUI then shows a custom made message box with the error message in it. I know the possibility of declaring the exception logger in the GUI with WithEvents, but the there is no purpose of having everything static in the exception logger class.
EDIT 1: I'm looking for a way to trigger an event in the GUI project from a ExceptionLogger Class in a Class Library in the same solution without declaring the Class ExceptionLogger in every Class in my GUI project.
EDIT 2: Let me give an example. I have a Class Library named Base. This Class Library contains a Class named ExceptionLogger. This Class in its turn has a Static Routine called Log.
So I have a .dll named Base as a Reference in my main Project called GUI. GUI has a Form MainWindow and 2 Classes named Product and Customer.
What I want is that, for example, the class Customer runs into an Error (by using Try...Catch) and in the Catch part Base.Log(1). The Log routine in ExceptionLogger will look up what the error message has to be and will fire an event in the GUI's MainWindow showing a User Made MessageBox.
The real question is, how do I fire this event from Log into the MainWindow?
EDIT 3: The User Made MessageBox in EDIT 2 is rather a Panel that overlays the MainWindow, not a MessageBox window that opens.
I have found the solution on my own. Any remarks are welcome. Already tnx to the ppl who commented or posted an answer.
This is an example of what I was looking for and it's working.
This is my DLL (namespace Base):
public class ExceptionHandler
{
public static event EventHandler ShowErrorMessage;
public static void test(EventArgs e)
{
ShowErrorMessage("me", e);
}
}
This is the code in my MainWindow:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Base.ExceptionHandler.ShowErrorMessage += ExceptionHandler_ShowErrorMessage;
}
void ExceptionHandler_ShowErrorMessage(object sender, EventArgs e)
{
MessageBox.Show((string)sender);
}
private void Form1_Load(object sender, EventArgs e)
{
Base.ExceptionHandler.test(new EventArgs());
}
}
please help me to find a solution for calling a method automatically when ever a form is loaded. I want to write a piece of code in every form for invoking a license validation for each form. To avoid I placed that code in the program.cs as a static method and now I want to call the method without re writing my form's codes. Please help me on this issue.
Create base class LicensedForm which will provide this functionality:
public class LicensedForm : Form
{
protected override void OnLoad(EventArgs e)
{
// invoking a license validation here
base.OnLoad(e);
}
}
Inherit other forms from this base class instead of Form
public class MainForm : LicensedForm
{
//...
}
You need your forms to handle the Load event. Then run your license-check method in the eventhandlers.
You need to make sure that your method is both public and static, to be able to access it.
private void MainForm_Load(object sender, EventArgs e)
{
Program.CheckLicense();
}
ThisAddIn class created with new Outlook VSTO C# project has a Application property that you can use to among other things get access to Outlook folders and items. The problem is that you can easily use it when you're inside of ThisAddIn class but there's no easy access to it from other classes in the project. This is because it's an instance property.
I want to find the best way of having access to the same functionality this property provides in my other classes so I come up with two possible solutions but I don't know which one (if any) of them is good.
Lets assume I want to get default inbox folder. Inside ThisAddIn class I would simply do something like this:
this.Application.Session.GetDefaultFolder(Outlook.olFolderInbox);
Now how to do the same outside this class?
1. Static property
First, I could add a static property to ThisAddIn class and set it to the value I want to expose in other classes.
public partial class ThisAddIn
{
public Outlook.Application OutlookApp;
void ThisAddIn_Startup(object sender, EventArgs e)
{
// init static variable value here
OutlookApp = this.Application
// initialize the rest of addin here
}
void InternalStartup()
{
this.Startup += this.ThisAddIn_Startup;
}
}
This way in any of my other classes I could do something like this:
ThisAddIn.OutlookApp.Session.GetDefaultFolder(Outlook.olFolderInbox);
2. Create new Application object
Second thing that I could do is to init Application object in my other class before I use it. But I'm not sure if creating new object of that type doesn't create a new instance of Outlook.
class MyOtherClass
{
public void MyMethod()
{
var app = new Outlook.Application();
var folder = app.Session.GetDefaultFolder(Outlook.olFolderInbox);
}
}
Does anyone have any suggestions which approach is better, of if you have different solutions for this problem I'd apprieciate that as well.
Since you can have single Instance of ThisAddIn you can have a static variable to access Application form outside... FYI when you add Outlook-AddIn VSTO project,instance of ThisAddIn will be available as static member in static class Globals
It is actually bad practice to place static variables in the ThisAddIn in order to reference from around your code.
According to this answer https://stackoverflow.com/a/46493968/2068626, the Outlook Application object is a singleton so it would be preferred to do your own second suggestion
var app = new Outlook.Application();
Since this is a Outlook VSTO add-in there is zero risk of starting Outlook since it by definition will be running. If you use this method from within another Office application you would start Outlook only if Outlook is not currently running.
For completeness sake, all other shared models should use a form of Dependency Injection so there is no strong coupling in your code.
Using these two approaches it would also be easier (possible) for you to refactor your code and other classes out into a separate library for easier reuse across your VSTO projects or even a Nuget Package.