Update: I have tried using other Excel.XlAutoFillType, sadly it did not work.
The types are (here)
ORIGINAL QUESTION:
I'm new to VSTO and now writing an VSTO add-in for Excel(2013 or higher version). The Autofill method is Range.AutoFill(Range, XlAutoFillType) from Microsoft.Office.Interop.Excel (See Documentation from MS).
I have a date (like 25.02.2020) and want the excel to autofill the rest of the column with the dates after it (ex. A1:25.02.2020; A2:26.02.2020; A3:27.02.2020; ... A6:01.03.2020; ...).
As I tried manually to do it in excel, it worked, as long as I selected two or more cells as examples.
Image see: https://i.imgur.com/L8Enqrr.png (As StackOverflow doesn't allow me to post photos)
However, when I tried to use the autofill method, it failed and only added the years. (B2 was provided as an example for excel)
image see: https://i.imgur.com/smWpo0Y.png
Here is my code:
// Datetime dates[] = new dates[3] {dt1, dt2, dt3}
// Int[] dateLength = new Int[3] {0, (int)Math.Ceiling(dates[1].Subtract(dates[0]).TotalDays), ((int)Math.Ceiling(dates[2].Subtract(dates[1]).TotalDays) + (int)Math.Ceiling(dates[1].Subtract(dates[0]).TotalDays))}
for (int i = 0, j = 0; i < dates.Length; i++, j++)
{
Excel.Worksheet activeWst = Globals.ThisAddIn.Application.ActiveSheet;
Excel.Range tRange = activeWst.Range[activeWst.Cells[dateLength[i] + 2 + j, 2], activeWst.Cells[dateLength[i] + 2 + j, 2]];
tRange.Value = dates[i].ToString("dd/MM/yyyy");
if (i < dates.Length - 1) {
tRange.AutoFill(activeWst.Range[activeWst.Cells[dateLength[i] + 2 + j, 2], activeWst.Cells[dateLength[i + 1] + j + 1, 2]]);
}
}
Thanks.
I have found the answer myself. As the date value in every cell is stored as String, the Autofill won't work.
tRange.Value = dates[i].ToString("dd/MM/yyyy"); //The problem
The only thing is to write the dates in the cells directly and change the entire column's number format to what you like.
//Fixed code
tRange.EntireColumn.NumberFormat = "dd-MM-yyyy";
tRange.Value = dates[i].Date;
And BTW, it works with the default XlAutoFillType(Excel.XlAutoFillType.xlFillDefault).
tRange.AutoFill(activeWst.Range[activeWst.Cells[dateLength[i] + 2 + j, 2], activeWst.Cells[dateLength[i + 1] + j + 1, 2]], Excel.XlAutoFillType.xlFillDefault);
I appreciate everyone's help. Thank you a lot.
Related
I am trying to pick string values from different Excel worksheets and write it into Word. However, each text line will have different font size, bold and underline.
I managed to pull all values into Word with StringBuilder, however I am not able to format each line way I wanted, therefore trying to make other solution without StringBuilder.
Please see code below. Unfortunately, the end result has only the last row of Excel sheet value.
Anyone has idea, what I can do here:
for (int c = 0; c < aList.Count; c++)
{
Worksheet excelWorksheet = excelWorkbook.Worksheets[aList[c]];
Microsoft.Office.Interop.Excel.Range lastRow = excelWorksheet.Cells.SpecialCells(XlCellType.xlCellTypeLastCell);
int lastUsedRow = lastRow.Row;
currentItemNumber = "Item " + (c + 1).ToString();
docRange.Text = currentItemNumber;
docRange.Font.Bold = 1;
docRange.Font.Size = 12;
docRange.Font.Underline = (WdUnderline)1;
docWholeRange.Text = docRange.Text;
for (int d = 0; d < lastUsedRow; d++)
{
if (d == 0)
{
currentRowValue = excelWorksheet.Range["A" + (d + 1)].Value2 + "\t" + excelWorksheet.Range["D"+(d+1)].Value2;
docRange.Text = currentRowValue;
docRange.Font.Bold = 0;
docRange.Font.Size = 12;
docRange.Font.Underline = (WdUnderline)0;
docWholeRange.Text = docWholeRange.Text + docRange.Text;
}
else
{
currentRowValue = excelWorksheet.Range["A" + (d + 1)].Value2+ excelWorksheet.Range["B" + (d + 1)].Value2+ excelWorksheet.Range["C" + (d + 1)].Value2;
docRange.Text = currentRowValue;
docRange.Font.Bold = 0;
docRange.Font.Size = 10;
docRange.Font.Underline = (WdUnderline)0;
docWholeRange.Text = docWholeRange.Text + docRange.Text;
}
}
wordDocument.Bookmarks.Add(bookmarkPlace, docWholeRange);
}
I tried using StringBuilder, however it is not possible to format each line separately and keep it formatted.
Then tried using Selection object in similar manner as shown in code and then tried to use Selection.TypeText. Instead of being entered at Bookmark place, it was actually starting from the beginning of word file. The size of the letter was way too small.
Managed to get all lines correctly to be written in Word without using StringBuilder.
However, trying to get all text lines between two bookmarks and all samples advising to use Bookmarks.get_Item and this does not exists in Visual Studio 2019 and C#.
Anyone has idea, how to store it into Range?
Tried following:
object start = bm.Range.End;
object end = ebm.Range.Start;
Microsoft.Office.Interop.Word.Range textRange = wordDocument.Range(ref start, ref end);
bm and ebm are two bookmarks.
I am trying to store all text line into range, then iterate line by line and change font size/bold in each text line.
Anyone has better idea how it can be done.
Regards,
As you can see in the picture, ShiftLeaders row is merged, however, from the 4th to 7th row the values are dynamic(If there are more data more rows will be filled). Because of that, I can't be sure what row to assign on setters and merge those two rows. The current way is displaying the table correctly, the problem is with setters and operators rows which can't be merged. Any help is appreciated!
Shift Leaders is ok:
ws.Cells["A3:C3"].Value = "Shift Leaders";
ws.Cells["A3:C3"].Merge = true;
Setters row is not merging:
ws.Cells[largestLeaders + 4, 2].Value = "Setters";
ws.Cells[largestLeaders + 4, 2].Merge = true;
As well as Operators row:
ws.Cells[largestSetters + 4 + largestLeaders + 2, 2].Value = "Operators";
ws.Cells[largestSetters + 4 + largestLeaders + 2, 2].Merge = true;
it looks like you are using single cell instead of cell range. try with range:
int n = largestLeaders + 4;
string range = $"A{n}:C{n}";
ws.Cells[range].Value = "Setters";
ws.Cells[range].Merge = true;
I need to implement this equation:
In c# it is pretty straightforward:
static double LogarithmicSummed(IReadOnlyList<double> values)
{
double outVal = 0;
for (int i = 0; i < values.Count; i++)
{
outVal = outVal + Math.Pow(10, values[i] / 10);
}
return 10 * Math.Log10(outVal);
}
I need to verify the data in an excel sheet where I print out the data programmatically. That is the raw data, my c# calculations on the raw data AND the excel formual that should match my c# calculations. I just do not know how to write the formula in excel.
I know the rows and columns where the data are and they are next to each other. So for example, if I needed the arithmetic average, ie:
I could print out for each row:
// If row = 1, startColumn = A, endColumn = D, noOfColumn = 4. This would print: =SUM(A1:D1)/4
mean[row] = #"=SUM(" + startColumn + row + ":" + endColumn + row + ")/" + noOfColumns;
What would I print out to match the first formula I wrote? Or what would I need to write in Excel?
without VBA:
Put your data in A1 through A10 and in B1 enter:
=10^(A1/10)
and copy down. Then in another cell enter:
=10*LOG10(SUM(B1:B10))
You can avoid the "helper" column (column B) by using:
=10*LOG10(SUMPRODUCT(10^((A1:A10)/10)))
If that's how you enter your formulas, then why not use the literal form of the sum? Ie use a for loop over the columns and fill in:
10*log(10^(A1/10)+10^(A2/10)+10^(A3/10)+10^(A4/10))
I have a following string, with line breaks in a textfile:
Card No. Seq Account 1 Account 2 Account 3 Account 4 Customer Name Expiry Status
0100000184998 1 2500855884500 - - /NIRMAL PRADHAN 1302 Cold
0100000186936 1 - - - /RITA SHRESTHA 1302 Cold
0100000238562 1 2500211214500 - - /HARRY SHARMA 1301 Cold
0100000270755 0 1820823730100 - - /EXPRESS ACCOUNT 9999 Cold
0100000272629 0 1820833290100 - - - /ROMA MAHARJAN 1208 Cold
0100000272637 0 2510171014500 - - /NITIN KUMAR SHRESTHA 1208 Cold
0100000272645 0 1800505550100 - - - /DR HARI BHATTA 1208 Cold
Here,
Card No., Seq has fixed digits.
Account 1, Account 2, Account 3, Account 4 can have fixed digit
number or - or null.
Customer Name can have First Name, Last Name, Middle Name etc.
My desired result is:
array[0][0] = "0100000184998"
array[0][1] = "1"
array[0][2] = "2500855884500"
array[0][3] = " "
array[0][4] = "-"
array[0][6] = "NIRMAL PRADHAN "
array[1][0] = "0100000186936"
array[1][1] = "1"
array[1][3] = " "
array[1][4] = "-"
Here, What I tried is:
var sourceFile = txtProcessingFile.Text;
string contents = System.IO.File.ReadAllText(sourceFile);
var newarr = contents.Split(new char[]{ '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select (x =>
x.Split(new char[]{ ' ' }, StringSplitOptions.RemoveEmptyEntries).ToArray()
).ToArray();
DataTable dt = new DataTable("NewDataTable");
dt.Columns.Add("CardNo");
dt.Columns.Add("SNo");
dt.Columns.Add("Account1");
and so on...
for (int row = 0; row < newarr.Length; row++)
{
for (int col = 0; col < newarr[col].Length; col++)
{
dt.Rows.Add(newarr[row]);
row++;
}
}
This works fine if data field is not empty and Customer name is only the first name or delimited.
But, here what I am trying to get is:
First Name, Middle Name or Last Name must be stored in the same
array element.
Account Number in the array element must left blank if it is blank.
How is it possible to store it correctly on the datatable ?
I suggest that you learn to use the TextFieldParser class. Yes, it's in the Microsoft.VisualBasic namespace, but you can use it from C#. That class lets you easily parse text files that have fixed field widths. See the article How to: Read From Fixed-width Text Files in Visual Basic for an example. Again, the sample is in Visual Basic, but it should be very easy to convert to C#.
If you are willing to make the compromise of not making a difference between - and null values in the account values, you may try this:
var sourceFile = txtProcessingFile.Text;
string[] contents = System.IO.File.ReadAllLines(sourceFile);
DataTable dt = new DataTable("NewDataTable");
dt.Columns.Add("CardNo");
dt.Columns.Add("SNo");
dt.Columns.Add("Account1");
dt.Columns.Add("Account2");
dt.Columns.Add("Account3");
dt.Columns.Add("Account4");
dt.Columns.Add("CustomerName");
dt.Columns.Add("Expiry");
dt.Columns.Add("Status");
for (int row = 2; row < contents.Length; row++)
{
var newRow = dt.NewRow();
var regEx = new Regex(#"([\w]*)");
var matches = regEx.Matches(contents[row].ToString())
.Cast<Match>()
.Where(m => !String.IsNullOrEmpty(m.Value))
.ToList();
var numbers = matches.Where(m => Regex.IsMatch(m.Value, #"^\d+$")).ToList();
var names = matches.Where(m => !Regex.IsMatch(m.Value, #"^\d+$")).ToList();
for (int i = 0; i < numbers.Count() - 1; i++)
{
newRow[i] = numbers.Skip(i).First();
}
newRow[newRow.ItemArray.Length - 2] = numbers.Last();
newRow[newRow.ItemArray.Length - 1] = names.Last();
newRow[newRow.ItemArray.Length - 3] = names.Take(names.Count() - 1).Aggregate<Match, string>("", (a, b) => a += " " + b.Value);
dt.Rows.Add(newRow);
}
To get around the names with a single space in them, you could try splitting on a double-space instead of a single space:
x.Split(new string[]{ " " }
This still won't fix the issue with columns that have no value in them. It appears that your text file has everything in a specific position. Seq is in position 16, Account 1 is in position 20, etc.
Once your lines are stored in newarr, you may just want to use String.Substring() with .Trim() to get the value in each column.
I'm doing a university assignment and I'm having some trouble with my Array.
I am building a console based address book application and I need to use a 2D-Array which has the options of adding, removing, or printing the contents of the Array...
I am deleting the current entries by replacing the values with "XXX" -as is required in the brief. I then just hide the "XXX" values when printing to screen.
But once I have 'Deleted' an entry, when it comes to printing the entire Array, anything listed AFTER the entry I tried to delete, does not show up at all, except after adding a new entry.
Could anyone shed some light on why this may be happening?
In your code you're deleting the whole column, once the if statement catch a true condition, e.g. if the address[1, 1] == DeleteName then your array after the for loop will look like:
A00 XXX A02 ...
A10 XXX A13 ...
A20 XXX A23 ...
A30 XXX A33 ...
... ...
So if you just need to delete an entry you will need two loops like this:
for (int i = 0; i < 5; i++)
{
for(int j =0;j < 5; j++)
{
if (Address[i, j] == DeleteName)
{
Address[i, j] = "XXX";
Console.WriteLine("Contact Deleted");
}
}
}
Also, in your case 3 it looks like you're missing the { } after your else statement
else
{
FName = Address[i, 0];
LName = Address[i, 1];
Number = Address[i, 2];
Road = Address[i, 3];
Town = Address[i, 4];
Console.WriteLine(LName +", " + FName + " - "+ Number + ", " + Road+", " + Town);
}