I am trying to add a group of textboxes to a list. I keep getting an error cannot convert from 'System.Windows.Forms.TextBox' to 'Microsoft.Office.Interop.Excel.TextBox'. Not sure why I am getting this error. Do I need to add a cast?
Here is my code. The textboxes I want to add are in getValues. I haven't worked a lot with windows forms so this error is a mystery for me.
class DiagnosticsExcelFile: ConfigForm
{
string fileName = "";
private ConfigForm configForm;
Excel.Application xlApp = null;
Excel.Workbook xlWorkBook = null;
Excel.Worksheet xlWorkSheet = null;
List<TextBox> list = new List<TextBox>();
public DiagnosticsExcelFile(ConfigForm config)
{
fileName = "Z:\\UCA\\Diagnostics.xlsx";
this.configForm = config;
this.list.Add(this.configForm.HighOffSetX); // get error here
}
private void getValues()
{
this.configForm.HighOffSetX.Text = "5.0";
this.configForm.HighOffSetY.Text = "5.0";
this.configForm.HighOffSetZ.Text = "5.0";
}
}
Define your list like:
List<System.Windows.Forms.TextBox> list = new List<System.Windows.Forms.TextBox>();
Currently your List is referring to TextBox from Microsoft.Office.Interop.Excel.TextBox.
Both Microsoft.Office.Interop.Excel= and System.Windows.Forms has a class TextBox, since you haven't shown your using directives, I assume you have
using Microsoft.Office.Interop.Excel;
and you don't have a using directive for System.Windows.Forms, therefore your code is considering your List to contain TextBoxes from Office Interop. (By the way, if you have both references, then you will get an error for ambiguous reference to TextBox)
By explicitly defining System.Windows.Forms.TextBox, your error will go away.
using TextBox = System.Windows.Forms.TextBox;
I usually find this approach the most efficient.
It doesn't require many code changes and allows to keep lines short.
You're not specifying a full namespace, but you probably have:
using System.Windows.Forms;
using Microsoft.Office.Interop.Excel;
in your code file.
The compiler can't tell which TextBox you mean. You can fully qualify each type name, or alias one of the usings.
using xl = Microsoft.Office.Interop.Excel;
Then you would refer to the Excel Textbox as xl.TextBox. Specifying just TextBox would give you the System.Windows.Forms one that you expect.
You are hiding the TextBox type from System.Windows.Forms.TextBox (maybe with a using TextBox = Microsoft.Office.Interop.Excel.TextBox).
Try declaring your list with:
List<System.Windows.Forms.TextBox> list = new List<System.Windows.Forms.TextBox>();
Your definition is correct. The problem occurs when adding new members to the list. Try the following:
list.Add(HighOffSetX);
You dont need the whole "this" stuff at this point.
Related
I have a problem that I can't solve. I need to pass a value from one Selenium test to another. And the writing part works just fine. I saves the value to an excel file. The problem appears when I try to read it. I have a class which should be able to read the cell, but it won't.
Excel.cs
using System.IO;
using OfficeOpenXml;
using excel = Microsoft.Office.Interop.Excel;
namespace Core
{
public sealed class Excel
{
#region Private Properties
private static string InputPath { get; } = ConfigSettingProvider.InputSourcePath;
private static string OutputPath { get; } = ConfigSettingProvider.OutputSourcePath;
#endregion Private Properties
public static string ReadCell (int sheet, int x, int y) // (sheet, row, column)
{
FileInfo existingFile = new FileInfo("..\\..\\..\\Property\\Input\\data1.xlsx");
using (ExcelPackage package = new ExcelPackage(existingFile))
{
return package.Workbook.Worksheets[sheet].Cells[x, y].Value.ToString();
}
}
}
}
And then in Test Fixture class I am trying to call ReadCell method:
var cellPolicyNo = Excel.ReadCell(1, 1, 1);
But it simply won't even enter to read it. Immediately the exception is thrown: "The type initializer for 'Core.Excel' threw an exception". at Core.Excel.ReadCell(Int32 sheet, Int32 x, Int32 y)
What am I doing wrong? What should I change?
Thank you
You might want to take a look at this thread to see if it helps with answering your question.
How to read single Excel cell value
This is the more common way of reading from an excel file that is more reliable. There are also ways of doing it through OLEDB but that is for reading the entire excel file. Hopefully this helps as it is similar to code I have used in the past to read data from excel files.
It appears to be a static constructor problem, see here:
https://stackoverflow.com/questions/4398334/the-type-initializer-for-myclass-threw-an-exception#:~:text=The%20type%20initializer%20for%20%27CSMessageUtility.,-CSDetails%27%20threw%20an&text=means%20that%20the%20static%20constructor,static%20members%20of%20that%20class.
Btw, it might perhaps help to try a different toolkit, namely EPPlus (the last free version is 4.5.3.3). I've tried to read cells also with Excel Interop but it didn't work out so well. With EPPlus you can use LINQ on the range objects of Excel.
I am aware that there are already a few threads on this already created and closed (like 2-dimensional Integer array to DataGridView)
My problem is that since I have only been working with console applications, I am not aware of what I need to do in order to apply the code.
Up till now I have drag and dropped a new dataGridView and chose program.cs (where my main is) as a source. Now when I apply the code from the aforementioned link in program.cs, visualstudio is saying that dataGridView1 "does not exist in the current context". When I try to declare it beforehand, I get that the type/namespace can't be found. Any ideas?
With regard to the errors you got: All you need to do is add the namespace: using System.Windows.Forms; and also a reference to it! This works just fine for Console applications. Of course it will never show up but other than that you can make use of its abilities..
But the real question is: What do you want to achieve and why do you stick to a console application?
This is not to say that you may not have a good reason! For example it is sometimes necessary to run a service application without a display screen. This could still create output from DataGridViews or from Chart controls..
But it always helps to understand the situation fully when we answer questions here..
Here is an example that creates and fills a DataGridView DGV and then saves an image of the data to a png file.
For this to work you also need to add System.Drawing. As for Windows.Forms you need to add both the using clause and the reference:
(I have only a German VS version here; instead of the 'Aktuell' (ie 'Current') group you should search the references in the 'Framework'! The result is the same - I chose the other one becasue it is not so big on the screenshot..)
Once the references are in place..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms; // <--- add namespace AND reference!!
using System.Drawing; // <--- add namespace AND reference!!
..this simple console application will compile and run:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataGridView DGV = new DataGridView();
List<string> test = new List<string>()
{ "Anna", "Bertha", "Carol", "Doreen", "Erica", "Fran", "Gisa" };
DGV.Columns.Add("No", "Number");
DGV.Columns.Add("Name", "Name");
DGV.Columns.Add("Age", "Age");
DGV.Columns["Name"].DefaultCellStyle.Font =
new Font(DGV.Font, FontStyle.Bold);
for (int i = 0; i < test.Count; i++) DGV.Rows.Add(new[]
{ (i + 1)+ "", test[i], i + 21 +""}); // cheap string array
DGV.ScrollBars = ScrollBars.None;
DGV.AllowUserToAddRows = false;
DGV.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
DGV.RowHeadersVisible = false;
var width = DGV.Columns.GetColumnsWidth(DataGridViewElementStates.None);
DGV.ClientSize = new Size(width,
DGV.ColumnHeadersHeight + DGV.RowCount * (DGV.Rows[0].Height) );
Bitmap bmp = new Bitmap(DGV.ClientSize.Width, DGV.ClientSize.Height);
DGV.DrawToBitmap(bmp, DGV.ClientRectangle);
bmp.Save("D:\\testDGV.png", System.Drawing.Imaging.ImageFormat.Png);
bmp.Dispose();
}
}
}
This seems like a super simple issue to resolve. The type or namespace 'Sheet' is simply not there, so I get the red underline with the above message.
I have included:
using NPOI.HSSF.UserModel;
using NPOI.HPSF;
using NPOI.POIFS.FileSystem;
using NPOI.SS.UserModel;//(i think it should be in here)
Code that has error:
HSSFWorkbook hssfworkbook;
hssfworkbook = new HSSFWorkbook();
Sheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); //this causes the error
Oh and further down, same error here. 'Row' not defined.
Row row = sheet1.CreateRow(i);
Using MS Visual Studio 2013. (C#)
Thanks.
you need to typecast the ISheet interface returned by the CreateSheet method to HSSFSheet. Namespace should be NPOI.SS.UserModel:
HSSFWorkbook hssfworkbook= new HSSFWorkbook ();
HSSFSheet sheet1 = (HSSFSheet)hssfworkbook.CreateSheet("Sheet1");
var row = sheet1.CreateRow(0);
Is there a way to get the objects (queries, forms, reports, macros, etc.) from an .accdb file? I am not looking for storing data, I want to examine the structure and design of those objects.
Edit: To make the question clear. I want to access those objects from C#, so that I can do automatic checking.
The following C# console app lists all of the controls in a specified Form:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace comAutoTest
{
class Program
{
static void Main(string[] args)
{
// this code requires the following COM reference in the project:
// Microsoft Access 14.0 Object Library
//
var objAccess = new Microsoft.Office.Interop.Access.Application();
objAccess.Visible = false;
objAccess.OpenCurrentDatabase(#"C:\Users\Public\Database1.accdb");
string formName = "MembersForm";
Console.WriteLine(String.Format("The form [{0}] contains the following controls:", formName));
objAccess.DoCmd.OpenForm(formName, Microsoft.Office.Interop.Access.AcFormView.acDesign);
Microsoft.Office.Interop.Access.Form frm = objAccess.Forms[formName];
foreach (Microsoft.Office.Interop.Access.Control ctl in frm.Controls)
{
Console.WriteLine();
Console.WriteLine(String.Format(" [{0}]", ctl.Name));
Console.WriteLine(String.Format(" {0}", ctl.GetType()));
}
objAccess.DoCmd.Close(Microsoft.Office.Interop.Access.AcObjectType.acForm, formName);
objAccess.CloseCurrentDatabase();
objAccess.Quit();
Console.WriteLine();
Console.WriteLine("Done.");
}
}
}
The output is:
The form [MembersForm] contains the following controls:
[LastName]
Microsoft.Office.Interop.Access.TextBoxClass
[Label0]
Microsoft.Office.Interop.Access.LabelClass
[MemberDonationsSubform]
Microsoft.Office.Interop.Access.SubFormClass
[MemberDonationsSubform Label]
Microsoft.Office.Interop.Access.LabelClass
[Command3]
Microsoft.Office.Interop.Access.CommandButtonClass
Done.
Edit: For Relationships, do something like this
Microsoft.Office.Interop.Access.Dao.Database cdb = objAccess.CurrentDb();
foreach (Microsoft.Office.Interop.Access.Dao.Relation rel in cdb.Relations)
{
Console.WriteLine(rel.Name);
}
There is a feature in Access that is called "Database Document". You can access it through the Database Tools ribbon panel. When you run the tool fol all objects it will generate a report called Objects Definition. You can print it, but I recommend you export it to a file of some sort (e.g. Excel, or Text). That way it will be easier to analyse.
Discussion on OOoForum.org
In python, using pyuno, I can do it like this:
table = self.model.createInstance("com.sun.star.text.TextTable")
This doesn't seem to work in C#. Here is my test code (I realize I probably don't need all those using statements, but I am adapting someone else's code):
using System;
using unoidl.com.sun.star.lang;
using unoidl.com.sun.star.uno;
using unoidl.com.sun.star.bridge;
using unoidl.com.sun.star.frame;
using unoidl.com.sun.star.document;
using unoidl.com.sun.star.text;
using unoidl.com.sun.star.container;
using unoidl.com.sun.star.util;
using unoidl.com.sun.star.table;
using unoidl.com.sun.star.beans;
namespace FromScratch
{
class MainClass
{
public static void Main(string[] args)
{
XComponentContext componentContext =
uno.util.Bootstrap.bootstrap();
XMultiServiceFactory multiServiceFactory = (XMultiServiceFactory)
componentContext.getServiceManager();
XTextDocument document;
XComponentLoader loader = (XComponentLoader)
multiServiceFactory.createInstance
("com.sun.star.frame.Desktop");
document = (XTextDocument) loader.loadComponentFromURL
("private:factory/swriter", "_blank", 0,
new PropertyValue[0]);
XText text = document.getText();
XTextCursor cursor = text.createTextCursor();
XTextTable table = (XTextTable)
multiServiceFactory.createInstance
("com.sun.star.text.TextTable");
table.initialize(2, 2);
text.insertTextContent(cursor, table, false);
}
}
}
Most of it seems to work fine, but when it gets to this line:
table.initialize(2, 2);
I get a runtime error:
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object
at FromScratch.MainClass.Main (System.String[] args) [0x00063] in /home/matthew/Desktop/OpenOfficeSample/FromScratch/Main.cs:37
Apparently, this line:
XTextTable table = (XTextTable)
multiServiceFactory.createInstance
("com.sun.star.text.TextTable");
doesn't actually set table to anything.
What is going on here?
Solution (from OOoForum.org):
You must get the text table from the document multiservice factory, not from the multiservice factory of the service manager. You can do this by casting your document (a Model) to XMultiServiceFactory and calling its createInstance method.
XTextTable table = (XTextTable)
((XMultiServiceFactory)document).createInstance
("com.sun.star.text.TextTable");
See DevGuide.