I'm inserting variable text from a *.html file into a Word document and have to adapt the font(name and size) of the inserted text to the rest of the document.
I have a working solution but I don't like the way I did it, so I'm searching another way to get the standard font name and size from Word application.
Another problem is that NameLocal can be in different languages. So I also need another way to find the Headers. I already tried Style.Type but it has always value "1"
My code so far:
foreach (Word.Style style in Globals.ThisAddIn.Application.ActiveDocument.Styles)
{
if (style.NameLocal.Equals("Normal")) // find correct style object
{
float size = style.Font.Size;
string font = style.Font.Name;
foreach (Word.Paragraph paragraph in Globals.ThisAddIn.Application.ActiveDocument.Paragraphs)
{
if (paragraph.Range.get_Style().NameLocal.Contains("Heading")) // find all headers
{
paragraph.Range.Font.Size = size;
paragraph.Range.Font.Name = font;
}
}
break;
}
}
The reason why I'm not simply changing the style is so the headers are still marked as headers.
I'm pretty clueless atm
For built-in styles, the Word object model provides the enumeration WdBuiltinStyle. Using this instead of a string value (the local name of a style) makes specifying a style language-independent. In addition, the built-in styles will always be present in a document so there's no need to loop the Styles collection of a document to get a particular style.
So, for example:
Word.Document doc = Globals.ThisAddin.Application.ActiveDocument;
Word.Style style = doc.Styles[Word.WdBuildinStyle.wdStyleNormal];
float size = style.Size;
string font = style.Font.Name;
foreach (Word.Paragraph paragraph in doc)
{
if (paragraph.Range.get_Style() = Word.WdBuildinStyle.wdStyleHeading1)
{
paragraph.Range.Font.Size = size;
paragraph.Range.Font.Name = font;
}
}
Related
I would like to add rich text to a TextBox on a PowerPoint Slide.
I am using .Net 4.7.2 with Microsoft.Office.Interop.PowerPoint
Overview
// Initialization of the Application
public PresentationGenerator () {
pptApplication = new Application ();
}
// Creating a new document based on a teplate.
Presentation pptPresentation = generator.pptApplication.Presentations.Open (templatePath, MsoTriState.msoFalse, MsoTriState.msoTrue, showWindow);
// Setting a slide and editing the text works perfectly fine with this
Slide currentSlide;
currentSlide = pptPresentation.Slides[1];
currentSlide.Shapes.Title.TextFrame.TextRange.Text = "Wonderful Title";
currentSlide.Shapes[3].TextFrame.TextRange.Text = "Great TextBox";
This is an mwe of my setup to set text on slides.
I would however like to add text to one of my shapes using a loop and setting the layout depending on a property.
Imagine the following Array
customParagraphs = [
{
text:"example heading",
type:"title"
},{
text:"example normal text",
type:"text"
}]
I can loop over this list and add the text to the end of the TextRange2 using .insertAfter(text) and try setting the font-size for the text portion that i added.
TextRange2 textrange = currentSlide.Shapes[3].TextFrame2.TextRange
foreach(var paragraph in customParagraphs){
TextRange2 paragraphRange = textrange.Paragraphs.insertAfter(paragraph.text)
if(paragraph.type == "title"){
paragraphRange.Font.Size = 24.0F;
}
}
This will successfully add the text and change the font-size, if type is title. However it will change the font-size for the whole text-range!
The reference returned by .insertAfter() seems to refer to the instance of TextRange2 and not my newly added paragraph.
My Questions
Is there a way to change the font-size and other attributes of a line, paragraph or word inside a TextRange or TextRange2 element?
Is there a better way to add text to a TextRange or TextRange2 element than .insertAfter that preferably returns a reference to only the text i added?
Is it possible to highlight a part of a text without selecting this part of the text preferably with a different color in Textbox or Rich TextBox? In fact, I mean, a part of the text is highlighted by another color differing from the color assigned for text selection. To clarify, I have attached an image showing this behavior. (The image is from a website, not WPF).
The bold and dark green part is a text which is just highlighted, and the gray region is a selected part.
Using the RichTextBox element allows for more styling options which, to my knowledge, aren't available for the regular TextBox element.
Here is an approach that I have created:
// Generate example content
FlowDocument doc = new FlowDocument();
Run runStart = new Run("This is an example of ");
Run runHighlight = new Run("text highlighting in WPF");
Run runEnd = new Run(" using the RichTextBox element.");
// Apply highlight style
runHighlight.FontWeight = FontWeights.Bold;
runHighlight.Background = Brushes.LightGreen;
// Create paragraph
Paragraph paragraph = new Paragraph();
paragraph.Inlines.Add(runStart);
paragraph.Inlines.Add(runHighlight);
paragraph.Inlines.Add(runEnd);
// Add the paragraph to the FlowDocument
doc.Blocks.Add(paragraph);
// Apply to RichTextBox
YourRichTextBoxHere.Document = doc;
View Screenshot
I found this article to be helpful.
Highlight Searched Text in WPF ListView
While the article is about highlighting searched text in a ListView, I have easily adapted it in my own code to work with pretty much any control.
Starting with the control you pass in, it will recursively look for TextBlocks and will find the text you want, extract it as an inline, and will change it's Background / Foreground properties.
You can easily adapt the code to be a behavior if your want.
Here is an example:
private void HighlightText(object controlToHighlight, string textToHighlight)
{
if (controlToHighlight == null) return;
if (controlToHighlight is TextBlock tb)
{
var regex = new Regex("(" + textToHighlight + ")", RegexOptions.IgnoreCase);
if (textToHighlight.Length == 0)
{
var str = tb.Text;
tb.Inlines.Clear();
tb.Inlines.Add(str);
return;
}
var substrings = regex.Split(tb.Text);
tb.Inlines.Clear();
foreach (var item in substrings)
{
if (regex.Match(item).Success)
{
var run = new Run(item)
{
Background = (SolidColorBrush) new BrushConverter().ConvertFrom("#FFFFF45E")
};
tb.Inlines.Add(run);
}
else
{
tb.Inlines.Add(item);
}
}
}
else
{
if (!(controlToHighlight is DependencyObject dependencyObject)) return;
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObject); i++)
{
HighlightText(VisualTreeHelper.GetChild(dependencyObject, i), textToHighlight);
}
}
}
I hope this is helpful!
So, I know there are multiple thread on how to do the conversion between the aforementioned systems. And I know they're not 1-to-1. However, I'm hoping there's a way to be able to get things to work.
The fonts specifically in question are just examples, as I'm sure others have the same issue, Segoe UI just happens to be my default font. What's not working though, is when I select Segoe UI Semibold Italic or some other in-between font.
Here's my conversion code:
// Font family
FontFamilyConverter ffc = new FontFamilyConverter();
TextContent.FontFamily = (System.Windows.Media.FontFamily)
ffc.ConvertFromString(fontDialog.Font.Name);
// Font size
TextContent.FontSize = fontDialog.Font.Size;
// Bold?
TextContent.FontWeight = (fontDialog.Font.Bold ? FontWeights.Bold : FontWeights.Normal);
// Italic?
TextContent.FontStyle = (fontDialog.Font.Italic ? FontStyles.Italic : FontStyles.Normal);
// Underline and strikethrough?
TextContent.TextDecorations = new TextDecorationCollection();
if (fontDialog.Font.Strikeout) {
TextContent.TextDecorations.Add(TextDecorations.Strikethrough);
}
if (fontDialog.Font.Underline) {
TextContent.TextDecorations.Add(TextDecorations.Underline);
}
// Color
TextContent.Foreground = new SolidColorBrush(
System.Windows.Media.Color.FromArgb(fontDialog.Color.A,
fontDialog.Color.R,
fontDialog.Color.G,
fontDialog.Color.B)
);
From using the debugger, I know that the Italic property is being properly set, but the font isn't coming through as Semibold Italic it's just coming through as Semibold. If (when in the debugger) I change the FontFamily to "Segoe UI Semibold Italic" then it works.
Is there something I'm missing to be able to get all the styles to come across correctly?
Thanks.
Note: I know size isn't working correctly. Just haven't fixed it yet
Here is what I ended up with:
After the dialog returns OK:
FontFamilyConverter ffc = new FontFamilyConverter();
TextContent.FontFamily = (System.Windows.Media.FontFamily) ffc.ConvertFromString(getFontName(fontDialog.Font));
A helper method:
private List<string> limitFontList(List<string> fontList, string word) {
// Create a new list
var newFontList = new List<string>();
// Go through each element in the list
foreach (var fontFamily in fontList) {
// If the elment contains the word
if (fontFamily.ToLower().Contains(word.ToLower())) {
// Add it to the new list
newFontList.Add(fontFamily);
}
}
// Return the new list if anything was put in it, otherwise the original list.
return newFontList.Count > 0 ? newFontList : fontList;
}
getFontName:
private string getFontName(Font font) {
// Holds the font we want to return. This will be the original name if
// a better one cannot be found
string fontWanted = font.FontFamily.Name;
// Create a new Media.FontFamily
var family = new System.Windows.Media.FontFamily(fontWanted);
/// Get the base font name
string baseFont = ""; // Holds the name
/* FamilyNames.Values will holds the base name, but it's in a collection
** and the easiest way to get it is to use a foreach. To the best of my
** knowledge, there is only ever one value in Values.
** E.g. If the font set is Segoe UI SemiBold Italc, gets Segoe UI.
*/
foreach(var baseF in family.FamilyNames.Values){
baseFont = baseF;
}
// If the baseFont is what we were passed in, then just return
if(baseFont == fontWanted) {
return fontWanted;
}
// Get the typeface by extracting the basefont from the name.
// Trim removes any preceeeding spaces.
string fontTypeface = fontWanted.Substring(baseFont.Length).Trim();
// Will hold all of the font names to be checked.
var fontNames = new List<string>();
// Go through all of the available typefaces, and add them to the list
foreach (var typeface in family.FamilyTypefaces) {
foreach(var fn in typeface.AdjustedFaceNames) {
fontNames.Add(baseFont + " " + fn.Value);
}
}
// Limit the list to elements which contain the specified typeface
fontNames = limitFontList(fontNames, fontTypeface);
// If the font is bold, and the original name doesn't have bold in it (semibold?)
if(!baseFont.ToLower().Contains("bold") && font.Bold) {
fontNames = limitFontList(fontNames, "bold");
}
// In a similar manner for italics
if (!baseFont.ToLower().Contains("italic") && font.Italic) {
fontNames = limitFontList(fontNames, "italic");
}
// If we have only one result left
if(fontNames.Count == 1) {
return fontNames[0];
}
// Otherwise, we can't accurately determine what the long name is,
// So hope whatever the short name is will work.
return fontWanted;
}
I am trying to replace the temporary text in a word document with new text from a list. It works if the text is not in a shape, but once it tries to find the text in a textbox it throws an error. Here is what I have so far:
public void FindReplace(List<repvals> replaceVals, string docLocation, int listLen)
{
//Opens a new Word application
var app = new Microsoft.Office.Interop.Word.Application();
//Opens the .docx
var doc = app.Documents.Open(docLocation, true, false);
//Selects the document
var range = doc.Range();
for (int i = 0; i < listLen; i++)
{
//Finds the parameter, then replaces
range.Find.Execute(FindText: Convert.ToString(replaceVals[i].tempVal), Replace: WdReplace.wdReplaceAll, ReplaceWith: Convert.ToString(replaceVals[i].Boxes));
var shapes = doc.Shapes;
//Finds text within textboxes, then changes them
foreach (Microsoft.Office.Interop.Word.Shape shape in shapes)
{
var initialText = shape.TextFrame.TextRange.Text;
var resultingText = initialText.Replace(Convert.ToString(replaceVals[i].tempVal), Convert.ToString(replaceVals[i].Boxes));
shape.TextFrame.TextRange.Text = resultingText;
}
}
//prints document
doc.Save();
doc.Close();
//fully closes Word
Marshal.ReleaseComObject(app);
}
The problem occurs when it hits
var initialText = shape.TextFrame.TextRange.Text;
And throws an error saying: "This object does not support attached text."
The text in the shapes are nothing special. (e.g. tDATE, tNAME, etc.)
Any ideas?
I found the answer. Turns out my code was fine, however the document I was using (which I didn't write), had another shape on the second to last page to form a place to sign your name. I replaced that with an underscore, ran the code, and everything changed perfectly.
For those who also experience this problem, try checking how many shapes your foreach loop has counted:
http://i.imgur.com/1yNrL4p.png
Thank you Andrew and varocarbas for the help
*"In 2003 the AltText default for a standard textbox WAS the contained text BUT since you can change the Alt Text to NOT match it was never a good idea to read it this way. In 2010 the default for Alt Text is blank
If the textbox is named "Text Box 2" (substitute the correct name if not)
MsgBox ActiveDocument.Shapes("Text Box 2").TextFrame.TextRange should work."*
--
John SR Wilson
http://answers.microsoft.com/en-us/office/forum/office_2010-customize/shapesalternativetext-is-blank-for-the-docx/7671c746-2c2b-41d9-b7de-389a766587a7?page=2&msgId=31041d67-e62b-4ce0-b283-57fd6a4ff6b2
I'm using the following code to make my treenodes bold:
Font font = new Font(tvQuestionSequence.Font, FontStyle.Bold);
foreach (QuestionnaireBuilder_Category cat in categories)
{
TreeNode node = new TreeNode();
node.Text = cat.Description;
node.Name = cat.Id.ToString();
node.NodeFont = font;
tvQuestionSequence.Nodes.Add(node);
}
But the text of the bold nodes is not displayed correctly. The last letter(s) are not shown. How come? And how to solve this problem?
I found this Post when searching through the web because I am facing the exact same problem.
However, appending a white space to the end of the node was not an option, and I found an alternative way that seems to fix the issue.
After setting my node font Bold, all I need to do is reset the node text with the same value.
Here is the Code Sample:
Font boldFont = new Font(treeview.Font, FontStyle.Bold);
node.NodeFont = boldFont;
node.Text = node.Text;
It seems that the node is redrawn after changing the text, which is exactly what I wanted in the first place.
I've found that this is a Windows issue. A workaround for this problem is this:
In the form constructor set the font of the treeview to bold. When adding nodes which must not be bold, change the font to regular:
// Constructor of your form
public Form()
{
InitializeComponent();
Font font = new Font(tvQuestionSequence.Font, FontStyle.Bold);
tvQuestionSequence.Font = font;
}
// Add regular nodes (not bold)
Font font = new Font(tvQuestionSequence.Font, FontStyle.Regular);
TreeNode treeNode = new TreeNode();
treeNode.Text = "Foo";
treeNode.NodeFont = font;
TreeNode parent = tvQuestionSequence.Nodes.Find("parent", true);
parent.Nodes.Add(treeNode);
Simply use treeView.BeginUpdate() before you bold the node then treeView.EndUpdate() after you've bolded the node.
This is a known Windows bug.
The simple solution is just to append an extra space character at the end of your strings. The space character will not be visible, but it will increase the number of pixels needed to draw the string, so the entire string will be visible.
This is all not helping for me.
What DID the trick is making the font a little bigger and bold at DESIGN time.
(In the Properties window)
So make sure you define the treeview with big enough font, then later you can add nodes with smaller font. They will fit.
I do agree with the solution provided. I just want to add to it to shed a little more light on what the problem is.
The treeview has its own font which determines the width of items at the root level. That compensates for the fact that there is only an item height property available and no item width property.
The solution to your problem is to determine what the font of your root node should be, then set the tree to that same font. You can do that at design time also.
Hope that helps someone.
A workaround for this problem is this:
Set the defaul font of treeview to bold in the properties.
And chnage to not bold when you need.
I do the following, I set the DrawNode Event to call, it sets the node to bold and removes the highlighted colour.
You can set any colour you like using the first parameter of the e.Graphics.FillRectangle function.
private void SetNodeBoldWhenSelected(object sender, DrawTreeNodeEventArgs e)
{
if (e.Node == null) return;
var font = e.Node.NodeFont ?? e.Node.TreeView.Font;
if (e.Node.IsSelected)
{
font = new Font(font, FontStyle.Bold);
}
var bounds = new Rectangle( e.Bounds.X,e.Bounds.Y,e.Bounds.Width+20,e.Bounds.Height);
e.Graphics.FillRectangle(SystemBrushes.ControlDarkDark, bounds);
TextRenderer.DrawText(e.Graphics, e.Node.Text, font, bounds, SystemColors.HighlightText, TextFormatFlags.GlyphOverhangPadding);
}
Now when I select a node I get 20 pixels more space, for my font, this works well, one can calculate the "real" size needed but there is no specification stating it needs to do this but you can use Graphics.MeasureString if you feel you need to do that.
Very easy and works fine
treeView1.SelectedNode.NodeFont = new System.Drawing.Font(treeView1.SelectedNode.TreeView.Font, treeView1.SelectedNode.TreeView.Font.Style | FontStyle.Bold);
this.treeView1.SelectedNode.Text += string.Empty;
I realize this is an old thread and it may have been answered. I just ran across this problem as I'm learning to use TreeViews. What worked for me was changing the font size for the entire TreeView to the same size, or bigger than the font of the level you want to bold. The default font size is 8.something. I changed mine to 10, which was the size I wanted my nodes, and the truncating was gone.
What worked for me: Hooking into the load event in the Control's constructor and tweaking the node as explained in BlunT's answer.
public MyControl()
{
InitializeComponent();
_head = new TreeNode();
this.Load += (s, e) =>
{
trvParts.Nodes.Clear();
_head.NodeFont = new Font(trvParts.Font, FontStyle.Bold);
trvParts.Nodes.Add(_head);
_head.Text = "Node Name";
};
}
It's in vb.Net however the solution to re-enter the value of the TEXT field gets around this nicely. As in:
With myNode
Dim myText As String = .Text 'capture the text
.NodeFont = New Font(<name of your treeview>.Font, FontStyle.Bold)
.Text = myText 'reset the text to the original value
End With
Based on MSDN Library, try change your code to:
Font font = new Font(tvQuestionSequence.Font, FontStyle.Bold);
foreach (QuestionnaireBuilder_Category cat in categories)
{
TreeNode node = new TreeNode();
node.Text = cat.Description;
node.Name = cat.Id.ToString();
node.NodeFont = font;
tvQuestionSequence.BeginUpdate(); //added newline here <--
tvQuestionSequence.Nodes.Add(node);
tvQuestionSequence.EndUpdate(); //added newline here <--
}
It work for me