I have a question about creating excel button and adding vba code function on it. I have created a button and module code but don't know how to make relation between them. Can anyone show me how?
my code for Button:
Excel.Shape btn = xlWorkSheet5.Shapes.AddOLEObject("Forms.CommandButton.1", Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, 300, 10, 150, 22);
Excel.OLEObject sheetBtn = (Excel.OLEObject)xlWorkSheet5.OLEObjects(btn.Name);
sheetBtn.Object.GetType().InvokeMember("Caption", System.Reflection.BindingFlags.SetProperty, null, sheetBtn.Object, new object[] { "Calculate Bus Load" });
and code for module:
String sCode = "Sub main()\r\n" +
" MsgBox \"Hello world\"\r\n" +
"end Sub";
VBA.VBComponent oModule = xlWorkBook.VBProject.VBComponents.Add(VBA.vbext_ComponentType.vbext_ct_StdModule);
oModule.Name = "Module1";
oModule.CodeModule.AddFromString(sCode);
xlWorkBook.VBProject.VBComponents.Item(1).CodeModule.AddFromString(sCode);
I have searched in Internet but didn't find anything usefull, so i cleared my mind and focused once more with c# help and I found an answer how to do it properly.
My Code:
String updateBT "...// macro code for button";
VBA.VBComponent oModule1 = xlWorkBook.VBProject.VBComponents.Add(VBA.vbext_ComponentType.vbext_ct_StdModule);
oModule1.Name = "Update";
oModule1.CodeModule.AddFromString(updateBT);
Excel.Shape btn2 = xlWorkSheet1.Shapes.AddFormControl(Excel.XlFormControl.xlButtonControl, 150, 5, 150, 22);
btn2.Name = "Update";
btn2.OnAction = "... // name of your macro code";
btn2.OLEFormat.Object.Caption = "... // Button name";
As far as I understand, you need the intermediate call to code/macro module from the button when you click on the button. So the code gets triggered and does what you want it to do.
In usual manner, for e.g.
we add a button in Excel Sheet
choose on_click event
add a code like call mySub
You need to do that within C#.
Please adjust for your module and control names. Here is a sample.
//Within your above code add,
sheetBtn.Click += new MSForms.CommandButtonEvents_ClickEventHandler(sheetBtn_Click);
}
//button click event triggers
void sheetBtn_Click()
{
call subMain
// try a test using : MessageBox.Show("button test!");
}
** PLEASE TRY THIS TUTORIAL OUT It has pretty much what you need.
As per the subject on just invoking a sheet sub or module sub written in Excel from C#, you may use run macro method.
//instead of this.application, you call refer to the Excel app object
this.Application.Run("yourMacroName",missing,missing........)
Reference:
Related
I keep looking for examples on how to add a radio button to an Excel worksheet programmatically but cannot get a straight answer. I've tried using Microsoft.Office.Interop.Excel and Microsoft.Office.Tools.Excel but neither have worked. The system I am working on already has Microsoft.Office.Interop.Excel as a reference so unless there are any objections to using this assembly, this would be my preference.
//propertyWorkSheet is a Microsoft.Office.Interop.Excel worksheet
Microsoft.Office.Tools.Excel.Application xlApp = new Excel.Application();
Microsoft.Office.Tools.Excel.Worksheet worksheet = (Microsoft.Office.Tools.Excel.Worksheet)propertyWorksheet;
Microsoft.Office.Tools.Excel.Range selection = worksheet.get_Range("A12:A12", "A12:A12");
Microsoft.Office.Tools.Excel.Controls.Button button = new Microsoft.Office.Tools.Excel.Controls.Button();
worksheet.Controls.AddControl(button, selection, "Button");
Did some more digging and got to this with my code and it worked.
Microsoft.Office.Interop.Excel.Buttons buttons = propertyWorksheet.Buttons(System.Reflection.Missing.Value) as Microsoft.Office.Interop.Excel.Buttons;
Microsoft.Office.Interop.Excel.Button button = buttons.Add(33, 33, 33, 33);
button.Caption = "Test BUTTON";
Let's create a ListObject and attach an event on it as msdn explain here: https://msdn.microsoft.com/en-us/library/eyfs6478.aspx
The code
In an application-level addin, code will look like above:
Worksheet worksheet = Globals.Factory.GetVstoObject(Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets[1]);
Excel.Range cell = worksheet.Range["$A$1:$D$4"];
ListObject list1 = worksheet.Controls.AddListObject(cell, "list1");
list1.Selected += list1_SelectedDeselected;
list1.Deselected += list1_SelectedDeselected;
With something like that to see the triggered events :
private void list1_SelectedDeselected(Excel.Range Target)
{
Worksheet worksheet = Globals.Factory.GetVstoObject(Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets[1]);
Excel.Range cell = worksheet.Range["$A$6"];
if (cell.Value2 == "foo")
{
cell.Value2 = "bar";
}
else
{
cell.Value2 = "foo";
}
}
Strange behavior
If you run this code within a add-in in excel, you'll see this and that
If you cut/paste this table within the same worksheet, every attached events will be triggered.
But if you cut/paste into another sheet, the events will not be attached anymore to the ListObject.
Are there a reason I don't know for this unpredictable behavior?
You need to use another VSTO object to subscribe to the events anew. The GetVstoObject should be used to get a new Worksheet object.
Alrighty guys I have rather a brain mangler for you; I'm trying develop a relatively simple add-in for excel that should read in data from Sheet A in a workbook, create a new or update sheet B to contain a simplified version of said data in sheet A at the press of a button. Below is some example code I'm working on:
Application.SendKeys("{ENTER}"); // Exit edit mode
Excel.Workbook wb = this.Application.ActiveWorkbook;
Excel.Worksheet sheetA = null;
Excel.Worksheet sheetB = null;
foreach (Excel.Worksheet sheet in wb.Worksheets) {
// Assume origin sheet we want to move from is same name as book name
if (sheet.Name == wb.Name)
sheetA = sheet;
// Sheet to move to will be called review. Clean if it exists.
else if (sheet.Name == "Review")
{
sheetB = sheet;
sheetB.Cells.ClearContents();
}
}
// If origin sheet cannot be found, assume it's the first sheet
if (sheetA == null)
sheetA = (Excel.Worksheet)wb.Worksheets[1];
// Add the review sheet after the origin sheet if it doesn't exist
if (sheetB == null)
{
sheetB = wb.Worksheets.Add(After: sheetA);
sheetB.Name = "Review";
}
// Simply copy across the value of the first cell
sheetB.Range["A1"].Value2 = sheetA.Range["A1"].Value2;
Now the outcomes of this code seem to be radically different depending on whether anything is in "edit mode" (cells are being edited) or not. If not, all is well as you'd expect, a new sheet is created in the correct position and the cell populated.
If a cell is being edited though, the edited cell is moved to another worksheet in the workbook. If there is no other sheet to move to a COMException with HRESULT: 0x800A03EC is thrown (error unknown).
This error shows up A LOT and it's really frustrating with it essentially telling you "be damned if I know", so any ideas would be appreciated. The most common thing seems to be "worksheet doesn't exist" which would be the case here, but I can't tell why it wants to move the edited cell in the first place?
Solution found. Simulating a return key stroke (first line of my example) does exit edit mode, but a delay is required for the application to process it. My implementation worked out as:
Application.SendKeys("{ENTER}"); // Exit edit mode
Timer timer = new Timer(100); // 100ms delay
timer.AutoReset = false; // Stop the timer looping and re-executing
timer.Elapsed += new ElapsedEventHandler((Sender, ent) =>
{
// Code you want to execute outside of edit mode
});
timer.Start(); // Start her up!
Hope that helps some lost wandering soul!
I've trawled the web and according to the documentation there doesn't appear to be a method to move a NamedRange: http://msdn.microsoft.com/en-us/library/microsoft.office.tools.excel.namedrange_methods(v=vs.80).aspx
I have the following code that copies cell data down a couple of rows:
activeSheet.Range[leftColumn + startRow, rightColumn + endRow].Copy();
//activeSheet.Range[leftColumn + startRow, rightColumn + endRow].Delete();
Range newRange = activeSheet.get_Range(leftColumn + (startRow + RowsToMoveDown.Count), rightColumn + (endRow + RowsToMoveDown.Count));
newRange.PasteSpecial(XlPasteType.xlPasteAll, XlPasteSpecialOperation.xlPasteSpecialOperationNone, Missing.Value, Missing.Value);
The NamedRange is the cells I'm copying and pasting, it does move the cell values down a couple of rows but because its a Copy it leaves the data in the above cells and the Delete method causes an Exception. However the real problem is after I move the cells the NamedRange I created:
rnArea = activeSheet.Range[leftColumn + startRow , rightColumn + (MyData.Values.Length + startRow)];
Name name = activeBook.Names.Add(uniqueName, rnArea);
Still refers to the original Cells Range (the location before I the moved the cells down).
How can I programmatically move a NamedRange in C# VSTO 4.0?
Ideally I wont have to move the cells before I put them in a Range but if this is the only solution, then I'll have to go with it.
EDIT:
After reading Doug Glancy's comment about trying the VBA like syntax in VSTO C# I came up with the following:
for (int i = 0; i < activeWorkbook.Names.Count; i++)
{
name = activeWorkbook.Names.Item(i + 1);
Debug.Write(name.Name.ToString());
System.Diagnostics.Debug.Write(name.RefersTo.ToString() + Environment.NewLine);
//prints out "Sheet1!$A$1:$A$25"
name.RefersTo = "Sheet1!$A$2:$A$26";
System.Diagnostics.Debug.Write(name.RefersTo.ToString());
//prints out "Sheet1!$A$2:$A$26"
}
But when I run this code and change the NamedRange RefersTo value, the result is that the NamedRange goes missing from the Excel NamedRange DropDownList?!?!
You can "move" a named range by adding it again with a different address. For example, in VBA:
Sub MoveNamedRange()
ActiveSheet.Names.Add Name:="test", RefersTo:="=$A$1"
Debug.Print ActiveSheet.Range("test").Address
ActiveSheet.Names.Add Name:="test", RefersTo:="=$A$2"
Debug.Print ActiveSheet.Range("test").Address
End Sub
This compiles and runs and yields the following in the immediate window:
$A$1
$A$2
EDIT - C is hard! But I managed to cobble this together in VS 2010 C#. It's from a Workbook project, but would also work in an addin I believe. I don't think I needed all the Type.Missing's in VS 2010, but I'm pretty sure I've read they are needed in earlier versions:
private void Sheet1_Startup(object sender, System.EventArgs e)
{
Globals.Sheet1.Names.Add("test", Globals.Sheet1.Range["A1"], System.Type.Missing, System.Type.Missing, System.Type.Missing, System.Type.Missing, System.Type.Missing, System.Type.Missing, System.Type.Missing, System.Type.Missing, System.Type.Missing);
MessageBox.Show(Globals.Sheet1.Range["test"].Address);
Globals.Sheet1.Names.Add("test", Globals.Sheet1.Range["A2"], System.Type.Missing, System.Type.Missing, System.Type.Missing, System.Type.Missing, System.Type.Missing, System.Type.Missing, System.Type.Missing, System.Type.Missing, System.Type.Missing);
MessageBox.Show(Globals.Sheet1.Range["test"].Address);
}
I created a new Excel file with C#.
When I open the document all the worksheets are align right to left.
How can I align the worksheet/workbook/window to display left to right grammatically?
Sub leftToRight()
Application.DefaultSheetDirection = xlLTR
ActiveSheet.DisplayRightToLeft = False
End Sub
You can also change the setting through Tools->Options->International. Note that you need to set/unset the Checkbox "View current sheet right-to-left" to change currently open sheets.
Edit: Sorry I accidentally interpreted your question as VBA.
Here is a c# Solution:
Excel.Application xlApp = new Excel.Application();
xlApp.Visible = true;
xlApp.Workbooks.Add(System.Type.Missing);
Excel.Worksheet active = (Excel.Worksheet)xlApp.ActiveSheet;
xlApp.DefaultSheetDirection = (int)Excel.Constants.xlLTR; //or xlRTL
active.DisplayRightToLeft = false;
I would like to introduce my implementation of this feature after i used marg concept and changed it to the right syntax for me:
public void SetWorksheetDirection(Application excel, bool isRTL)
{
Worksheet active = (Worksheet)excel.ActiveSheet;
if (isRTL)
excel.DefaultSheetDirection = (int)XlDirection.xlToRight;
else
excel.DefaultSheetDirection = (int)XlDirection.xlToLeft;
active.DisplayRightToLeft = isRTL;
}
Do this one time to change the default direction:
Alt+F11 to open the VBA editor
Ctrl+G to open the Immediate window
in the Immediate window type Application.DefaultSheetDirection = xlLTR and press Enter
Alt+Q to close the VBA editor
create a new workbook to test it
If you just wanted to organize some of the columns, you could use a line like this:
oWS.Range["A1:B2000"].HorizontalAlignment = Microsoft.Office.Interop.Excel.Constants.xlLeft;