I am trying to write a Winform application and dock Excel inside it as a control. Everything works until I tried to run an Excel macro (written in VBA) from my c# app. When this macro tried to access ActiveWorkBook, it is Nothing.
Funny thing is, if I comment out the "SetParent(xlHwnd, this.Handle);", the ActiveWorkBook is available. Seems like the window state of Excel application is affecting the current ActiveWorkbook.
Below is my code:
xlApp = newMicrosoft.Office.Interop.Excel.Application();
xlApp.WindowState = XlWindowState.xlMinimized;
xlApp.DisplayFormulaBar = false;
xlApp.ShowWindowsInTaskbar = false;
xlApp.DisplayAlerts = false;
xlApp.DisplayStatusBar = false;
xlApp.Interactive = true;
xlApp.Visible = true;
xlHwnd = (IntPtr)xlApp.Hwnd;
//Windows API call to change the parent of the target window.
SetParent(xlHwnd, this.Handle);
//Wire up the event to keep the window sized to match the control
SetWindowPos(xlHwnd, 0, this.DisplayRectangle.Left, this.DisplayRectangle.Top, this.DisplayRectangle.Width, this.DisplayRectangle.Height, 0X0040 | 0X4);
this.SizeChanged += newEventHandler(Panel1_Resize);
xlWorkbook = xlApp.Workbooks.Add();
vardbAddIn = (AddIn)xlApp.AddIns[3];
if(dbAddIn != null)
{
WorkbookdbWorkbook = xlApp.Workbooks.Open(dbAddIn.FullName);
if(dbWorkbook != null)
{
dbWorkbook.Application.Run("myMacro1");
}
}
According to MSDN you have to use ThisWorkbook instead of ActiveWorkbook in your macro because this is the one that contains the macro.
https://msdn.microsoft.com/en-us/vba/excel-vba/articles/application-thisworkbook-property-excel
Related
I was trying to create an Excel button in a sheet from a C# code, using a C# event hander for the button, so I don't have to involve VBA and text scripts; and I found this great solution from #
Dummy yoyo that works but "only for a while".
The solution is simple: add a shape, get it as an ole object, get the ole as a button and add a click event:
int i = 1;
private void CreateButton()
{
Microsoft.Office.Interop.Excel.Worksheet sheet = #get a sheet from Excel
sheet.Shapes.AddOLEObject("Forms.CommandButton.1", Type.Missing, false, false, Type.Missing, Type.Missing, Type.Missing, 10, 10, 100, 30);
Microsoft.Office.Interop.Excel.OLEObject oleShape = sheet.OLEObjects(1);
Microsoft.Vbe.Interop.Forms.CommandButton button = oleShape.Object;
button.Caption = "Custom Buttom" + i;
button.Click += Button_Click;
i++; //for future use, see below
}
private void Button_Click()
{
MessageBox.Show("It works!");
}
The problem:
Now, the first time I click the button on the sheet, it works! The second time "might" work. The more clicks the less chance of it working. Inevitably, the button "loses" the click event and just does nothing. I can't figure out why, what can be causing this.
I even tried to recreate the button every click with:
private void Button_Click()
{
MessageBox.Show("It Works!");
sheet.Shapes.Item(1).Delete();
CreateButton();
}
I can see the button is being recreated because of the i++ that shows in the button's caption. Even though, some clicks later, even the newly created button simply does nothing when clicked.
What can be going on? How can I solve this?
Edit:
A partial solution was found, as #Hans Passant suggested: if I keep the shape, the ole object and the button as static members outside the method, the garbage collector doesn't kill them. Nevertheless, I would like to save, close and reopen the sheet and still get the events.
Great solutions could be:
Identifying why it happens and solving it
Suggesting another way of creating the button using a c# click handler
Making it possible to save this sheet, close and reopen it with the events still attached.
A few more details:
I am creating a new Excel application from C# code and taking the first sheet of the first workbook.
This is not a VSTO Addin, I don't have access to the Globals.Factory, although I'd love to be able to get an instance of this factory and try Microsoft.Office.Tools.Worksheet methods to add controls.
My project is inevitably a class library consumed by another non-related software
The sheet must be saved and reopened later independently from the source project.
The sheet is populated before adding the button, also via C# code.
Visual Studio debugger is able to step into the button's event handler without any special action
Following way every time I click on this button not getting any issue.
using Excel = Microsoft.Office.Interop.Excel;
Excel.Application xlApp;
Excel.Workbook xlWorkBook;
Excel.Worksheet xlWorkSheet;
object misValue = System.Reflection.Missing.Value;
int i = 1;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
xlApp = new Excel.Application();
xlWorkBook = xlApp.Workbooks.Add(misValue);
xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
//add data
xlWorkSheet.Cells[1, 1] = "";
xlWorkSheet.Cells[1, 2] = "Student1";
xlWorkSheet.Cells[1, 3] = "Student2";
xlWorkSheet.Cells[1, 4] = "Student3";
xlWorkSheet.Cells[2, 1] = "Term1";
xlWorkSheet.Cells[2, 2] = "80";
xlWorkSheet.Cells[2, 3] = "65";
xlWorkSheet.Cells[2, 4] = "45";
xlWorkSheet.Cells[3, 1] = "Term2";
xlWorkSheet.Cells[3, 2] = "78";
xlWorkSheet.Cells[3, 3] = "72";
xlWorkSheet.Cells[3, 4] = "60";
xlApp.Visible = true;
#region
xlWorkSheet.Shapes.AddOLEObject("Forms.CommandButton.1", Type.Missing, false, false, Type.Missing, Type.Missing, Type.Missing, 10, 10, 100, 30);
Microsoft.Office.Interop.Excel.OLEObject oleShape = xlWorkSheet.OLEObjects(1);
Microsoft.Vbe.Interop.Forms.CommandButton button = oleShape.Object;
button.Caption = "Custom Buttom" +i ;
button.Click += Button_Click;
i++;
#endregion
}
private void Button_Click()
{
MessageBox.Show("It works!");
}
but sometime button seems not clickable or not fire click even. see below screenshot.
Message show already, but we do not see it in top of the worksheet. too many time click on the button it may happened. so if you do not see the dialog just open it from taskbar.
Reopen: Controls that are added at run time are not persisted when the document or workbook is saved and closed. The exact behavior is different for host controls and Windows Forms controls. In both cases, you can add code to your solution to re-create the controls when the user reopens the document.
xlApp = new Excel.Application();
string workbookPath = #"c:/Book.xlsx";
xlWorkBook = xlApp.Workbooks.Open(workbookPath,
0, false, 5, "", "", false, Excel.XlPlatform.xlWindows, "",
true, false, 0, true, false, false);
xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
xlApp.Visible = true;
Microsoft.Office.Interop.Excel.OLEObject oleShape = xlWorkSheet.OLEObjects(1);
Microsoft.Vbe.Interop.Forms.CommandButton button = oleShape.Object;
button.Caption = "Custom Buttom" + i;
button.Click += Button_Click;
i++;
NB: Microsoft Excel 2013, project reference Microsoft office 15.0 object Library and VS 2022 work perfectly but Excel 2016 has this issue you are telling about. so I think this issue has come from
Microsoft Office specific version.
if you want to make it count clicks try this..
const btn = document.querySelector('.btn');
btn.onclick = Counter;
const clicks = document.querySelector('.clicks');
clicks.id = document.querySelector('clicks');
var a = 0;
function Counter() {
a += 1;
clicks.innerHTML = a;
}
const reset = document.querySelector('.reset');
reset.onclick = resetCounter;
function resetCounter() {
a = 0;
clicks.innerHTML = a;
}
I am creating charts in PowerPoint. The below code opens two excel applications. One opens in the background that is invisible. The second one opens after the method ends. I need to make sure second excel either never open ideally or I can close it after it opens.
I have tried the below things but none worked.
I have tried forcing GC, Manual ReleaseComObject, Killing Excel process
I have tried separating excel COM objects and forcing GC
private void BtnInsert_Click(object sender, EventArgs e)
{
var Addin = Globals.ThisAddIn;
Microsoft.Office.Interop.PowerPoint.Application activeApplication = Addin.Application;
DocumentWindow activeWindows = activeApplication.ActiveWindow;
Microsoft.Office.Interop.PowerPoint.View activeView = activeWindows.View;
Slide activeSlide = activeView.Slide;
Microsoft.Office.Interop.PowerPoint.Shapes slideShape = activeSlide.Shapes;
Microsoft.Office.Interop.PowerPoint.Shape shape = slideShape.AddChart2(-1, XlChartType.xl3DBarClustered, -1, -1, -1, -1, true);
Microsoft.Office.Interop.PowerPoint.Chart chart = shape.Chart;
//Access the chart data
Microsoft.Office.Interop.PowerPoint.ChartData chartData = chart.ChartData;
chartData.Activate();
//Create instance to Excel workbook to work with chart data
Workbook workbook = chartData.Workbook;
Microsoft.Office.Interop.Excel.Application workbookApplication = workbook.Application;
workbookApplication.Visible = false;
workbookApplication.WindowState = XlWindowState.xlMinimized;
//Accessing the data worksheet for chart
Worksheet worksheet = workbook.Worksheets[1];
// I am adding data here
// This is not required to reproduce this
chartData.BreakLink();
workbook.Close(true);
}
Also, note that this issue does not occur while updating data.
Remove chartData.Activate() and chartData.BreakLink() solves this.
Although online documentation says that chartdata.activate is required before accessing the workbook.
Otherwise, we will get a null reference.
I think the documentation is incorrect or it does not apply to vsto.
I am trying to develop a gtk application on mac using gtk#. I created a tree view just like in the tutorial but the arrows are not rendering properly as seem below, there are no arrows. I can click to open and close but the actual arrows aren't working.
I copy pasted the code from the tutorial and It did the same thing, but here is my code anywhere. I have also verified view.ShowExpanders is set to true
Application.Init();
Window window = new Window("Editor Window");
window.SetPosition(WindowPosition.Center);
window.HeightRequest = 800;
window.WidthRequest = 1200;
TreeView view = new TreeView();
view.WidthRequest = 500;
TreeViewColumn column = new TreeViewColumn();
column.Title = "Heirarchy";
CellRendererText cell = new CellRendererText();
column.PackStart(cell, true);
view.AppendColumn(column);
column.AddAttribute(cell, "text", 0);
TreeStore store = new TreeStore(typeof(string));
TreeIter i = store.AppendValues("Project");
i = store.AppendValues(i,"Room1");
i = store.AppendValues(i, "Item");
store.AppendValues(i, "Attribute");
store.AppendValues(i, "Attribute");
store.AppendValues(i, "Attribute");
view.ShowExpanders = true;
view.Model = store;
view.ShowExpanders = true;
window.Add(view);
window.DeleteEvent += ExitWindow;
window.ShowAll();
Application.Run();
Is there some sort of image asset that doesn't exist on my computer? How would I install that on mac? Thank you for any help you can provide.
As a comment suggested, using brew install adwaita-icon-theme fixed the problem
I am working on an excel sheet in which, with the help of an xll addin, I am trying to update the data. I press Ctrl+Alt+F9 to start the calculations, but the calculation stops at any keypress or cell selection. Is there a way to override this feature, or bug?
This does not happen on Excel 2003.
Very good question!
Not sure will this work in your C# addin, but with Excel 2010 Application object model, you can use Application.CalculationInterruptKey to stop interruption on data calculation when a key is pressed. Not tested but this can be it.
Apply this at beginning of CtrlAltF9:
Dim lKey As Long
lKey = Application.CalculationInterruptKey
Application.CalculationInterruptKey = xlNoKey
Then at end of calculation, reset it to what it was or change it to default xlAnyKey.
Application.CalculationInterruptKey = lKey ' Or xlAnyKey
If you have other event triggered Subs, you may want to add lines about Application.CalculationState such that it won't make changes until Application.CalculationState = xlDone.
Hope this helps.
Why do you need to select a cell during calculation - Is there a reason you can't wait until it is done? Selecting/changing cells during calculation is dangerous as the data might not be correctly calculated yet.
If you are just worried about accidental key presses - this should prevent any accidental key presses
Sub SetUpUserInterface()
Dim Current As Worksheet
For Each Current In Worksheets
Current.Protect , UserInterfaceOnly:=True
'Current.Protect Password:="", UserInterfaceOnly:=True
Next
End Sub
Try turning off the UserControl, EnableEvents and setting the Calculation to Manual:
private static XlCalculation xlCalculation = XlCalculation.xlCalculationAutomatic;
static public void TurnOffApplicationSettings(Excel.Application xlApp)
{
xlApp.ScreenUpdating = false;
xlApp.DisplayAlerts = false;
xlCalculation = xlApp.Calculation; //Record the current Calculation Mode
xlApp.Calculation = XlCalculation.xlCalculationManual;
xlApp.UserControl = false;
xlApp.EnableEvents = false;
}
Then run your long operation and revert the UserControl and EnableEvents to true and the calculation back to Automatic (or more correctly what it was set to previously):
static public void TurnOnApplicationSettings(Excel.Application xlApp)
{
xlApp.ScreenUpdating = true;
xlApp.DisplayAlerts = true;
xlApp.Calculation = xlCalculation;
xlApp.UserControl = true;
xlApp.EnableEvents = true;
}
I am working on the C# vsto Excel application.
Whenever user pastes something in the excel template from another excel sheet,it also pastes Cell format along with the cell data in the excel template. I want to avoid this.
So i googled & i came across term paste special.
Paste special will only paste the contents and will no alter the format of the current sheet.
I want to introduce paste special option in my vsto application.
I have code here,
Application.OnKey("^v", "PasteSpecV");
but its not working...
can any one help me with this ?
Download dll From http://globalmousekeyhook.codeplex.com/
Add Reference MouseKeyboardActivityMonitor.dll
private KeyboardHookListener k_keyListener;
private void ThisWorkbook_Startup(object sender, System.EventArgs e)
{
k_keyListener = new KeyboardHookListener(new AppHooker());
k_keyListener.Enabled = true;
k_keyListener.KeyDown += new KeyEventHandler(k_keyListener_KeyDown);
}
void k_keyListener_KeyDown(object sender, KeyEventArgs e)
{
if (Control.ModifierKeys == Keys.Control)
if (e.KeyCode == Keys.V)
{
Worksheet actSht = ActiveSheet as Worksheet;
Range rng = actSht.Application.Selection as Range;
if (MessageBox.Show("You are about to paste values only. Do you want to continue?", "Paste Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
rng.PasteSpecial(XlPasteType.xlPasteValues, XlPasteSpecialOperation.xlPasteSpecialOperationNone, false, false);
}
e.Handled = true;
}
}
After lots of search and try and error methods,i finally managed to do "Paste Special".
The sheet on which i am working , i have delacred as Static Worksheet in the class called commonData
class CommonData
{
public static Worksheet DATASHEET;
}
after that, i used that worksheet in ThisWorkbook.cs
On the ThisWorkbook Start up, i replaced PASTE(^v) by VBA Function Paste_cell
private void ThisWorkbook_Startup(object sender, System.EventArgs e)
{
// replacing paste by macro function Paste_cell
CommonData.DATASHEET.Application.OnKey("^v", "Paste_cell");
}
Open excel sheet on which you are working, Press ALT + F11 i.e. VBA Macros Editor.
Tools >> Macros >> Create new macro,
It will create Module 1 in the Project Explorer,
Paste the following code in the module1
Sub Paste_cell()
If MsgBox("You are about to Paste only Values and not the format, proceed?", vbQuestion + vbOKCancel, GSAPPNAME) = vbOK Then
On Error Resume Next
ActiveSheet.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
End If
End Sub
Now,if you copy paste any value from any excel sheet,It will only paste the cell data and it will not paste its Format.It will prompt following message in order to alert user.
So the original Format will not change.
Cheers, :-)
I used the following code to copy-paste only values and formatting using PasteSpecial in VSTO 2010
Excel.Worksheet lastSheet = Application.ActiveSheet;
Excel.Workbook wb = Application.Workbooks.Add(Excel.XlWBATemplate.xlWBATWorksheet);
for (int i = this.Worksheets.Count; i >= 1; i--)
{
Excel.Worksheet source = this.Worksheets[i];
source.Activate();
Application.Cells.Select();
Application.Selection.Copy();
Excel.Worksheet sheet = wb.Worksheets.Add();
sheet.Activate();
Application.Selection.PasteSpecial(Excel.XlPasteType.xlPasteValues);
Application.Selection.PasteSpecial(Excel.XlPasteType.xlPasteFormats);
sheet.Name = source.Name;
sheet.Range["A1"].Select();
Clipboard.Clear();
}
lastSheet.Activate();
lastSheet.Range["A1"].Select();
wb.SaveAs(fileName);
wb.Close();
The line Clipboard.Clear() is the same as VBA CutCopyMode = False, to deselect what was selected to copy.