VSTO Word Content Control - adding multiple RichTextContentControls at run time - c#

I'm creating a Word add-in which allows the user to select various text in a Word document and click a button on the ribbon which will wrap that text with a Content Control (rich text). Eventually these content controls will then be mapped to XML.
The code so far is like this:
public partial class Ribbon1
{
private RichTextContentControl titleRichTextControl;
private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
{
}
private void addTitle_Click(object sender, RibbonControlEventArgs e)
{
AddRichTextControlAtSelection();
}
private void AddRichTextControlAtSelection()
{
word.Document currentDocument = Globals.ThisAddIn.Application.ActiveDocument;
currentDocument.ActiveWindow.Selection.Range.Select();
Document extendedDocument = Globals.Factory.GetVstoObject(currentDocument);
titleRichTextControl = extendedDocument.Controls.AddRichTextContentControl("titleRichTextControl");
titleRichTextControl.PlaceholderText = "Enter the title";
titleRichTextControl.Title = "Title";
titleRichTextControl.Tag = "title";
}
}
All this is fine and it works for the first time the button is clicked. However, if there is more than one 'title' (in this case) that needs adding, and the user presses the button a second time, it throws the error:
The control cannot be added because a control with the name titleRichTextControl already exists in the Controls collection.
It's clear why it complains, but i can't think of the correct way to go to allow multiple clicks of the button to generate multiple content controls of the same type (rich text content control) and the same name ("title" for example).
Can anybody point me in the right direction please.

OK this was how i did it in the end:
private void addTitle_Click(object sender, RibbonControlEventArgs e)
{
AddRichTextControlAtSelection();
}
int count = 0;
private void AddRichTextControlAtSelection()
{
word.Document currentDocument = Globals.ThisAddIn.Application.ActiveDocument;
Document extendedDocument = Globals.Factory.GetVstoObject(currentDocument);
if (currentDocument.ContentControls.Count > 0)
{
currentDocument.ActiveWindow.Selection.Range.HighlightColorIndex = word.WdColorIndex.wdYellow;
currentDocument.ActiveWindow.Selection.Range.Select();
richTextControls = new List<RichTextContentControl>();
foreach (word.ContentControl nativeControl in currentDocument.ContentControls)
{
if (nativeControl.Type == Microsoft.Office.Interop.Word.WdContentControlType.wdContentControlRichText)
{
count++;
RichTextContentControl tempControl = extendedDocument.Controls.AddRichTextContentControl("VSTORichTextControl" + count.ToString());
richTextControls.Add(tempControl);
tempControl.Title = "Title";
tempControl.Tag = "title";
break;
}
}
}
else
{
RichTextContentControl VSTORichTextControl;
VSTORichTextControl = extendedDocument.Controls.AddRichTextContentControl("VSTORichTextControl");
VSTORichTextControl.PlaceholderText = "Enter the DM title";
VSTORichTextControl.Title = "Title";
VSTORichTextControl.Tag = "title";
}
}

Related

C# Object Panels

The problem I'm having is where I can successfully use the RoomID of the Room object I've created, but not the panel. Here is the function where I set the label to the name of the room. (This is my Form1.cs)
public partial class Form1 : Form
{
Room ThisRoom = new Room();
public Form1()
{
InitializeComponent();
this.Text = "Aquinas College Master Controller";
}
private void roomDesignerToolStripMenuItem_Click(object sender, EventArgs e)
{
new RoomDesigner().Show();
}
private void button1_Click(object sender, EventArgs e)
{
roomsToolStripMenuItem.DropDownItems.Clear();
foreach (var Room in Global.Aquinas.Aquinas)
{
ToolStripMenuItem NewItem = new ToolStripMenuItem(Room.RoomID);
NewItem.Name = Room.RoomID;
NewItem.Click += new EventHandler(ItemClick);
roomsToolStripMenuItem.DropDownItems.Add(NewItem);
}
}
void ItemClick(object sender, EventArgs e)
{
ToolStripItem item = (ToolStripItem)sender;
label2.Text = item.Name;
foreach (var Room in Global.Aquinas.Aquinas)
{
if (Room.RoomID == item.Name)
{
ThisRoom = Room;
break;
}
}
Panel RoomPanel = ThisRoom.Panel;
RoomPanel.Size = new Size(607, 304);
RoomPanel.Location = new Point(144, 27);
RoomPanel.BackColor = Color.White;
this.Controls.Add(RoomPanel);
}
}
This is some of my code for the room object. (This is in Room.cs)
public class Room
{
private List<CtrlComputer> _Computers;
public List<CtrlComputer> Computers
{
get { return _Computers; }
set { _Computers = value; }
}
private string _RoomID;
public Room()
{
_Computers=new List<CtrlComputer>();
}
public string RoomID
{
get
{
return _RoomID;
}
set
{
_RoomID = value;
}
}
public Panel Panel
{
get
{
return _Panel;
}
set
{
_Panel = value;
}
}
private Panel _Panel;
}
And here is where I register the panel which I have just put my designs on, to a new room. (This is some of my RoomDesigner.cs)
public partial class RoomDesigner : Form
{
Room NewRoom = new Room();
Panel RoomDesignerPanel = new Panel();
public RoomDesigner()
{
InitializeComponent();
RoomDesignerPanel.Size = new Size(607, 304);
RoomDesignerPanel.Location = new Point(144, 27);
RoomDesignerPanel.BackColor = Color.White;
this.Controls.Add(RoomDesignerPanel);
textBox1.ForeColor = SystemColors.GrayText;
textBox1.Text = "Enter Computer ID Here";
this.textBox1.Leave += new System.EventHandler(this.textBox1_Leave);
this.textBox1.Enter += new System.EventHandler(this.textBox1_Enter);
textBox2.ForeColor = SystemColors.GrayText;
textBox2.Text = "Enter Room ID Here";
this.textBox2.Leave += new System.EventHandler(this.textBox2_Leave);
this.textBox2.Enter += new System.EventHandler(this.textBox2_Enter);
}
private void textBox1_Leave(object sender, EventArgs e)
{
if (textBox1.Text.Length == 0)
{
textBox1.Text = "Enter Computer ID Here";
textBox1.ForeColor = SystemColors.GrayText;
}
}
private void textBox1_Enter(object sender, EventArgs e)
{
if (textBox1.Text == "Enter Computer ID Here")
{
textBox1.Text = "";
textBox1.ForeColor = SystemColors.WindowText;
}
}
private void textBox2_Leave(object sender, EventArgs e)
{
if (textBox2.Text.Length == 0)
{
textBox2.Text = "Enter Room ID Here";
textBox2.ForeColor = SystemColors.GrayText;
}
}
private void textBox2_Enter(object sender, EventArgs e)
{
if (textBox2.Text == "Enter Room ID Here")
{
textBox2.Text = "";
textBox2.ForeColor = SystemColors.WindowText;
}
}
private void button1_Click(object sender, EventArgs e)
{
CtrlComputer NewComputer = new CtrlComputer();
NewComputer.ComputerID = textBox1.Text;
NewComputer.Text = NewComputer.ComputerID;
NewComputer.Parent = RoomDesignerPanel;
RoomDesignerPanel.Controls.Add(NewComputer);
NewRoom.Panel = RoomDesignerPanel;
NewRoom.Add(NewComputer);
}
When I try to reload that panel, the RoomID of that object is returned fine, yet the panel is not. Any ideas?
Edit: Sorry for the lack of clarity in my post, first posts aren't always easy.
The panel is created in the designer, but I tried private Panel Panel1 = new Panel(); with no luck. My current problem is that in Form1.cs, the program will throw "Cannot access a disposed object", but I followed the guide here https://msdn.microsoft.com/en-us/library/82785s1h(v=vs.110).aspx .
What you are looking to do is swap between a collection of Panel objects (kind of like your own version of TabPages). To do this you need to create each of the Panel objects (You do this in RoomDesigner, which is fine, but there is a catch: see * below). With that collection in hand, when you want to show it on the form, you need to remove the panel1 the designer code created (Controls.Remove(panel1)) and insert your new one (Controls.Add(RoomPanel)).
Since Controls utilize system resources, make sure to Dispose any control you will no longer use. Here is an example method:
//Initial case where no room has been displayed yet and panel1 is still valid
private Panel CurrentPanel = null;
public void SwapPanel(Panel p)
{
//If no panel has been placed yet, get rid of the default one
if (panel1 != null)
{
//we should never need the designer panel again, so dispose it
panel1.Dispose();
this.Controls.Remove(panel1);
}
else
{
//we may return to this panel later, so don't dispose it
this.Controls.Remove(CurrentPanel);
}
CurrentPanel = p;
this.Controls.Add(p);
}
When you finally close out and no longer need your Panels stored in your Room collection, make sure to Dispose them. (However, if your whole program is exiting at that point then it doesn't matter, but do it anyway as good practice)
*When you close any RoomDesigner form (or any form), all controls it contains in 'Controls' will be disposed, even if you are using them elsewhere. To prevent this, remove the controls you want to preserve from the RoomDesigner Controls collection before it is closed.

Opening in RichTextBox rather than Tab name

// add a module tab
private void add_mod_Click(object sender, EventArgs e)
{
int TabCount = 0;
int? index = searchIndex(mod_add_textbox.Text);
if (index == null)
{
RichTextBox new_rich = new RichTextBox();
new_rich.Dock = DockStyle.Fill;
TabPage NewPage = new TabPage();
TabCount += 1;
string DocumentText = mod_add_textbox.Text;
NewPage.Name = DocumentText;
NewPage.Text = DocumentText;
NewPage.Controls.Add(new_rich);
mod_tab.TabPages.Add(NewPage);
}
else
{
mod_tab.SelectedIndex = Convert.ToInt32(index);
}
}
private async void btn_file_note_Click(object sender, EventArgs e)
{
using(OpenFileDialog ofd = new OpenFileDialog() { Filter="Text Documents|*.txt", ValidateNames = true, Multiselect = false })
{
if(ofd.ShowDialog() == DialogResult.OK)
{
using (StreamReader sr = new StreamReader(ofd.FileName))
{
mod_tab.SelectedTab.Text = await sr.ReadToEndAsync();
}
}
}
}
The problem I am getting is that when I try to open a document it is opening it into the tab name rather than the rich text box inside the tab. I have changed the "mod_tab.SelectedTab" part to the name of the rich text box within the tab however I want it so whichever tab the user has selected it opens in there. Any suggestions? thank you.
You assigned the value to the Text property of selected tab. Instead you should assign the value to Text property of RichTextBox or use Load method of RichTextBox to load content. for example:
this.richTextBox1.Text = ....
Also when you create the tab and RichTextBox dynamically like you ar doing in your code, you can find it this way:
//It means: Find all RichTextBox control which are children of mod_tab.SelectedTab
//And return first of them.
var rtb = this.mod_tab.SelectedTab.Controls.OfType<RichTextBox>().FirstOrDefault();
rtb.Text = ...
Also this way:
//It means get the first child control of mod_tab.SelectedTab
//And convert it to RichTextBox.
var rtb = this.mod_tab.SelectedTab.Controls[0] as RichTextBox;
rtb.Text = ...

How add menuitem in word in c#

I have tried this code for add new menu item in word document on right click. But there is one problem
Error: 'this.Application' this line give an error of the statement is not in current context. What is the solution like any package is required for this line or any another solution.
Word.Application application;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
application = this.Application;
application.WindowBeforeRightClick +=
new Word.ApplicationEvents4_WindowBeforeRightClickEventHandler(application_WindowBeforeRightClick);
application.CustomizationContext = application.ActiveDocument;
Office.CommandBar commandBar = application.CommandBars["Text"];
Office.CommandBarButton button = (Office.CommandBarButton)commandBar.Controls.Add(
Office.MsoControlType.msoControlButton);
button.accName = "My Custom Button";
button.Caption = "My Custom Button";
}
public void application_WindowBeforeRightClick(Word.Selection selection, ref bool Cancel)
{
if (selection != null && !String.IsNullOrEmpty(selection.Text))
{
string selectionText = selection.Text;
if (selectionText.Contains("C#"))
SetCommandVisibility("My Custom Button", false);
else
SetCommandVisibility("My Custom Button", true);
}
}
private void SetCommandVisibility(string name, bool visible)
{
application.CustomizationContext = application.ActiveDocument;
Office.CommandBar commandBar = application.CommandBars["Text"];
commandBar.Controls[name].Visible = visible;
}

Button with image and not visible text properties

Can I create a button with both .Image and .Text properties simultaniously, in such way, that text is not visible on form, and is created just for identifying what button should do at the moment?
Using TextAlign and TextImageRelation properties doesn't help. Text is always visible, just a position changes.
private System.Windows.Forms.Button bRenameCourse;
this.bRenameCourse.BackColor = System.Drawing.SystemColors.ButtonFace;
this.bRenameCourse.Image = ((System.Drawing.Image)(resources.GetObject("bRenameCourse.Image")));
this.bRenameCourse.Location = new System.Drawing.Point(966, 6);
this.bRenameCourse.Name = "bRenameCourse";
I want this text "Rename" to be not visible on button
this.bRenameCourse.Text = "Rename";
this.bRenameCourse.Size = new System.Drawing.Size(64, 60);
this.bRenameCourse.TabIndex = 10;
this.bRenameCourse.UseVisualStyleBackColor = false;
this.bRenameCourse.Click += new System.EventHandler(this.bRenameCourse_Click);
Here is why do I want it works :
private void bRenameCourse_Click(object sender, EventArgs e)
{
if (bRenameCourse.Text.Equals("Rename"))
{
//DO SMTHNG
bRenameCourse.Text = "OK";
}
else if (bRenameCourse.Text.Equals("OK"))
{
//DO SMTHNG
bRenameCourse.Text = "Rename";
}
}
I can avoid this using some flags, but I'd like to know if it's possible in general.
Don't use the .Text property of the button to store information.You can use the .Tag property
ie
this.bRenameCourse.Tag = "Rename";
And in the Event
private void bRenameCourse_Click(object sender, EventArgs e)
{
if (bRenameCourse.Tag.Equals("Rename"))
{
//DO SMTHNG
bRenameCourse.Tag = "OK";
}
else if (bRenameCourse.Tag.Equals("OK"))
{
//DO SMTHNG
bRenameCourse.Tag = "Rename";
}
}
Just set the .Text property to ""(blank or empty)

WPF how do I create a textbox dynamically and find the textbox on a button click?

I am creating a TextBox and a Button dynamically using the following code:
Button btnClickMe = new Button();
btnClickMe.Content = "Click Me";
btnClickMe.Name = "btnClickMe";
btnClickMe.Click += new RoutedEventHandler(this.CallMeClick);
someStackPanel.Childern.Add(btnClickMe);
TextBox txtNumber = new TextBox();
txtNumber.Name = "txtNumber";
txtNumber.Text = "1776";
someStackPanel.Childern.Add(txtNumber);
I hook up to a click event to the Click Me button. The click me button even is fired correctly. However I cannot find the TextBox I entered dynamically.
Here is my click me event:
protected void ClickMeClick(object sender, RoutedEventArgs e)
{
// Find the phone number
TextBox txtNumber = this.someStackPanel.FindName("txtNumber") as TextBox;
if (txtNumber != null)
{
string message = string.Format("The number is {0}", txtNumber.Text);
MessageBox.Show(message);
}
else
{
MessageBox.Show("Textbox is null");
}
}
How can I find the TextBox txtNumber?
Josh G had the clue that fixed this code: use RegisterName().
Three benefits here:
Doesn't use a member variable to save the reference to the dynamically created TextBox.
Compiles.
Complete code.
using System;
using System.Windows;
using System.Windows.Controls;
namespace AddControlsDynamically
{
public partial class Window1 : Window
{
public void Window_Loaded(object sender, RoutedEventArgs e)
{
GenerateControls();
}
public void GenerateControls()
{
Button btnClickMe = new Button();
btnClickMe.Content = "Click Me";
btnClickMe.Name = "btnClickMe";
btnClickMe.Click += new RoutedEventHandler(this.CallMeClick);
someStackPanel.Children.Add(btnClickMe);
TextBox txtNumber = new TextBox();
txtNumber.Name = "txtNumber";
txtNumber.Text = "1776";
someStackPanel.Children.Add(txtNumber);
someStackPanel.RegisterName(txtNumber.Name, txtNumber);
}
protected void CallMeClick(object sender, RoutedEventArgs e)
{
TextBox txtNumber = (TextBox) this.someStackPanel.FindName("txtNumber");
string message = string.Format("The number is {0}", txtNumber.Text);
MessageBox.Show(message);
}
}
}
Another method is to set the associated TextBox as Button Tag when instanciating them.
btnClickMe.Tag = txtNumber;
This way you can retrieve it back in event handler.
protected void ClickMeClick(object sender, RoutedEventArgs e)
{
Button btnClickMe = sender as Button;
if (btnClickMe != null)
{
TextBox txtNumber = btnClickMe.Tag as TextBox;
// ...
}
}
You can get your original click handler to work by registering the name of the text box:
someStackPanel.RegisterName(txtNumber.Name, txtNumber);
This will then allow you to call FindName on the StackPanel and find the TextBox.
If you want to do a comprehensive search through the visual tree of controls, you can use the VisualTreeHelper class.
Use the following code to iterate through all of the visual children of a control:
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parentObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if (child is TextBox)
// Do something
}
If you want to search down into the tree, you will want to perform this loop recursively, like so:
public delegate void TextBoxOperation(TextBox box);
public bool SearchChildren(DependencyObject parent, TextBoxOperation op)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
TextBox box = child as TextBox;
if (box != null)
{
op.Invoke(box);
return true;
}
bool found = SearchChildren(child, op);
if (found)
return true;
}
}
Is there any way you can make the TextBox control a field in your class instead of a variable inside your generator method
public class MyWindow : Window
{
private TextBox txtNumber;
public void Window_Loaded()
{
GenerateControls();
}
public void GenerateControls()
{
Button btnClickMe = new Button();
btnClickMe.Content = "Click Me";
btnClickMe.Name = "btnClickMe";
btnClickMe.Click += new RoutedEventHandler(this.CallMeClick);
someStackPanel.Childern.Add(btnClickMe);
txtNumber = new TextBox();
txtNumber.Name = "txtNumber";
txtNumber.Text = "1776";
someStackPanel.Childern.Add(txtNumber);
}
protected void ClickMeClick(object sender, RoutedEventArgs e)
{
// Find the phone number
string message = string.Format("The number is {0}", txtNumber.Text);
MessageBox.Show(message);
}
}

Categories

Resources