I use interop.Word to create a Word document programmatically.
In the document I have a particular range which I would like to insert text to.
When I google it I see that the way to do this is :
range.Text=" Whatever...";
but I have no "Text" property for the range object.
Any ideas?
For the orignal question - this is just an intellisense bug, there is such property in the Range class.
For the problem from comments that
Range range=wordApp.ActiveDocument.TablesOfFigures[i].Range;
range.Text=" Whatever...";
replaces the ToF instead of prepending it with text. If you just want to set a header of the table, you can use Caption:
wordApp.ActiveDocument.TablesOfFigures[i].Caption = "Header text";
If however you need some text preceeding the ToF - check out this thread which is discussing similar case, but for the list instead of Table of Figures.
Another way to set caption is to select range you need and call InsertCaption:
wordApp.ActiveDocument.TablesOfFigures[i].Range.Select();
wordApp.Selection.InsertCaption("Whatever");
Note that InsertCaption accepts various args of various types, make sure to try different.
If you want to insert text at a range position, you can use Range.InsertBefore.
Range range=wordApp.ActiveDocument.TablesOfFigures[i].Range;
range.InsertBefore("My Text here. ");
Related
I want to be able to place 2 paragraphs after text in a MSWord document I am working on. It is all written in a custom compiler setup as an addin in word. Unfortunately I am a C# beginner and I cannot make heads or tails of it anymore.
Will Microsoft.Office.Interop.Word.Paragraph.Next(ref object count); be able to add even 1 paragraph if added in some way after text? E.g.
Somehow declaring Paragraph2 as 2 x Microsoft.Office.Interop.Word.Paragraph.Next(ref object count); and would be used as follows
string text = "Hello, World!";
BC.SetThisField(text + Paragraph2);
Resulting in (Pilcrows to represent what it would look like with paragraph characters showing):
Hello, World!¶
¶
The link(below) to Microsoft's documentation on this has simply led me down a road of researching ref, object and their definition of count which is apparently 1. I have been unsuccessful in using even the default of what is supposed to be.
https://learn.microsoft.com/en-us/dotnet/api/microsoft.office.interop.word.paragraph.next?view=word-pia#Microsoft_Office_Interop_Word_Paragraph_Next_System_Object__
I have thought, according to similar questions asked on stackoverflow that I should use Paragraphs.Add(Object) Method instead, although I would still be stuck either way.
If I understand the question correctly, you want to insert text, followed by two paragraph marks? The simplest C# for that would be:
string text = "Hello, World!\n\n";
BC.SetThisField(text);
\n or \r generates a paragraph mark (ANSI 13) in Word.
Another possibility is to concatenate char(13).
There's also InsertParagraphAfter, but that requires an additional
step and a target Range object.
The Paragraphs.Add() will insert a single paragraph, but in order
to insert more than one it would have to be run in a loop and would
also require a Range object as the target.
Microsoft.Office.Interop.Word.Paragraph.Next(ref object count); will not insert paragraphs and will return an error if the code tries to perform an action with a paragraph that does not exist. (If code tried to assign text to the paragraph, for example.)
I'm getting data from a web API that returns text, and formatting information. The formatting data only includes the type of formatting (bold, italic, etc.) and the range of that formatting. The main problem with this is, that two ranges can "collide" (for example the first 3 characters of a word are bold and italic but the last 3 characters are only italic). Example response
{
"text" : "This is an example text",
"inlineStyles" : [
{
"offsetFromStart" : 5,
"length" : 10,
"type" : "bold"
}
{
"offsetFromStart" : 10,
"length" : 10,
"type" : "italic"
}
]
}
I already tried doing this with a simple TextBlock and failed. And I also tried this with a RichTextBox but when I added a Span I couldn't insert it into its original position. I also tought about formatting each character with its own span or run but that would be very ugly and in general just a bad solution. (My main concern is speed..)
var tb = new RichTextBox();
var para = new Paragraph();
para.Inlines.Add("This is an example text") // Text parsed from the response
var startingPointer1 = para.ContentStart.GetPositionAtOffset(5);
var sp1 = new Span(startingPointer1, startingPointer1.GetPositionAtOffset(10));
sp1.FontWeight = FontWeights.Bold;
var startingPointer2 = para.ContentStart.GetPositionAtOffset(10);
var sp2 = new Span(startingPointer2 , startingPointer2 .GetPositionAtOffset(10));
sp2.FontStyle= FontStyles.Italic;
para.Inlines.Add(sp1);
para.Inlines.Add(sp2);
tb.Document.Blocks.Add(para);
This code appends it to the end and when combining multiple inline elements like in my example it doesn't work at all (because of the first problem.)
Example result:
I don't think you can overlap Runs/Spans like this, you'll have to find all the breaking points in your text and format each text range separately. It's similar to HTML, where
<bold>some<italic> bold italic</bold> and other </italic> text.
is not valid. In your case, you'll have a bold from (5,10), bolditalic from (11, 15) etc.
It's probably useful to find some kind of Range class with methods to combine ranges, split, find overlaps, etc. A while ago I started with this.
EDIT: I don't exactly have an idea how to implement all this (last time I did something similar was almost 10 years ago), but you can try something like this:
Create a List<Range<int>>. Initially it contains a single Range(0, length of text).
Load the first style, create a new Range with start/end offset. Overlap (or whatever method is appropriate) this range with the range in the list. This should give you 3 ranges, something like (0, start of style), (start of style, end of style), (end of style, end of text). Remove old range from the list and add new ones.
Load the next, find overlaps, with the ranges in the list, delete the ones that are overlapped and add new ranges.
This should give you a list of nonoverlapping ranges.
Now, for the styles. You can create a kind of stylesheet class. This class can use the FontWeights, FontStyles and other enums, defined in System.Windows. Modify a list, so that it contains, for example, List<Tuple<int, Stylesheet>>. To calculate overlaps just use the first param in the Tuple.
Before you remove old ranges from the list, combine the styles.
This should give you a list of nonoverlapped regions, with the appropriate styles. Create TextRanges, apply styles
Other idea that might work:
Again, create a stylesheet. Initially it should be normal weighy, normal style, default font size etc.
Find the next offset from the input (the first one that is larger than the current), create a TextRange and apply a style.
Find the next offset from the input, modify current (and only) style and apply.
If I remember correctly, inserting style definition in the text also counts as characters, so you might need to adjust offsets when you insert style tags in the final text. Also, I believe it is doable just using TextBlock.
As I said, I don't know if this works like described, but this might give you and idea.
My current solution is that I go through every character one by one and scan through the ranges detecting if the current character is in any of them and then assigning a span to the character. This is not ideal at all, but it gets the job done. I'll try to implement an actual algorithm for this later. Until then, if you have any information that could help, please comment.
If anyone needs sample code of my current implementation I'd happily share it with you. (Even though it's not really efficient at all)
I am looking to have a validation in an excel sheet which is generated using C# such that it allows either date value or the text "train" in the excel cell. I found XlDVType.xlValidateDate for validating date in a cell and XlDVType.xlValidateList to allow text values. But I want a combination of both, user should either be able to enter date or a particular text in the cell. Can this be accomplished using XlDVType.xlValidateCustom ??
any help on this would be greatly appreciated.
I want a combination of both, user should either be able to enter date
or a particular text in the cell.
The following code snippet sets the desired validation for cell A1 in your worksheet:
Excel.Range range = xlWorkSheet.Range["A1"];
range.ClearFormats();
range.Validation.Delete();
range.Validation.Add(Excel.XlDVType.xlValidateCustom,
Formula1: "=OR(EXACT(LEFT(CELL(\"format\",A1)),\"D\"),EXACT(A1,\"train\"))");
The most complicated part is the actual validation formula. It uses the following functions:
EXACT(cell,string) compares the contents of a cell with a literal string and returns TRUE only if they are exactly the same.
CELL("format",cell) returns a code containing the format of the indicated cell, where the code for a date always starts with a D. See Office help for CELL for an explanation and a list of all possible codes. This snippet only looks at the first character, which has to be a D in order for the contents to be formatted as some kind of date.
LEFT(text,count) returns the leftmost count characters in text, with count defaulting to 1. In this case, it returns TRUE if the contents are some kind of date.
OR(logical1, logical2) returns logical1 OR logical2, so TRUE if the contents are either a date, or exactly equal to the string "train".
This solution is not yet ideal, because A1 is hardcoded into the formula. There is a way around that using INDIRECT, described here. You might need that, for example if you want to apply the validation to a whole range of cells in one go. In that case, the hardcoded A1 needs to be replaced by INDIRECT("RC",FALSE), which is the mechanism to indicate the current shell. You can read it as a short-cut for INDIRECT("R"&ROW()&"C"&COLUMN(),FALSE).
With that adjustment, he last line of code then looks like this (tested in Excel, but not from C#):
range.Validation.Add(Excel.XlDVType.xlValidateCustom,
Formula1: "=OR(EXACT(LEFT(CELL(\"format\",INDIRECT(\"RC\",FALSE))),\"D\"),EXACT(INDIRECT(\"RC\",FALSE),\"train\"))"
Update
As the OP points out, there appears to be a problem with this approach if a date has first been entered into the cell, and then is changed to an incorrect string or number value -- see the comments below. Using the CELL function therefore does not seem to be the best approach.
However, if the cell format is explicitly set to the (more flexible) Text format, it is possible to impose the validation with the following lines:
range.Validation.Delete();
range.NumberFormat = "Text";
range.Validation.Add(Excel.XlDVType.xlValidateCustom,
Formula1: =OR(ISNUMBER(DATEVALUE(INDIRECT(\"RC\",FALSE))),EXACT(INDIRECT(\"RC\",FALSE),\"train\"));
Again tested in Excel, but not from C#.
Is there anyway to reconcile the two ? Ie when i set the text of a richtextform from a string, a given characters index in the string does not match the position of it in the textbox.
Make sure the WordWrap property is False.
On extremely long lines you're going to run into RightMargin. It is not infinite, the maximum right margin depends on the font size.
It seems to be okay, with this my sample text:
"Provide details and share your research. Avoid statements based solely on opinion; only make statements you can back up with an appropriate reference, or personal experiences"
Using the code:
richTextBox1.Text.IndexOf("back up");
textBox1.Text.IndexOf("back up");
Both have results of: 112
It seems you are using the Rtf property of the RichTextBox that contains extra tags for its formatting?
I've set MaskedTextBox's Mask to: "Fl\air H\al ###.## , something here: ####.##"
When user inputs the value final text looks something like this:
Flair Hal 987.67 , something here: 1234.12
What will be the best way to extract 976.67 and 1234.12 from the MaskedTextBox's Text. I am looking for a List which will have all the values of the mask (976.67, 1234.12).
There can be any number of masks in the mask string and the mask can be any valid mask.
I am thinking of first removing '\' from the Mask and then in a for loop keep comparing the Mask with the Text and detect changes and add them to the List. But this doesnt sound good to me and i think there probably is a better way of doing it.
There are four values of the mask in your example: 987, 67, 1234, 12. The fact that blocks separated by a . are treated as one is your own logic, so I think you will just have to write code to get the information yourself.
Have a look at the MaskedTextProvider property of the MaskedTextBox, and its EditPositions property. The EditPositions give you the positions within the Text that the user could enter.
Well i found out that there is no good way of doing it. As adrianbanks said i have to code myself to get this information.
I have written my own usercontrol which uses combination of labels and maskedtexboxes to get the input.
I use curly braces to indicate where i wan the masked textbox and the user control puts one masked textbox per pair of curly braces.
"Flair Hal {###.##} , something here: {####.##}"
Then I can use the values collection which has the values for the masks.
I would think that you would be to use TextMaskFormat to remove the literals and prompt characters from the .Text property. That way you'd only get the numbers (and spaces).
Use a regular expression. The following regex will work for your example, but you may have to tweak it depending on your actual mask:
\d*[.]\d{2}