Link to home
Start Free TrialLog in
Avatar of kunal singh
kunal singh

asked on

button on being saved in word VSTO

i have this pieace of code

 private void button2_Click(object sender, RibbonControlEventArgs e)
        {
            Microsoft.Office.Tools.Word.Controls.Button salesButton;
            Document vstodoc = Globals.Factory.GetVstoObject(Globals.ThisAddIn.Application.ActiveDocument);
            Microsoft.Office.Interop.Word.Application objApplication = Globals.ThisAddIn.Application;
            Microsoft.Office.Interop.Word.Selection objSelection = objApplication.Selection;
            Microsoft.Office.Interop.Word.Range objRange = objSelection.Range;
            salesButton = vstodoc.Controls.AddButton(objRange, 20, 20, "salesButton");
            salesButton.Text = "Calculate Total Sales";
        }

Open in new window

when i click on the button a button is inserted inside word doc but when i save it and try to reopen the word document the button is not there

before i save the word doc: 
User generated imageAfter i save the Word Doc: After i save the Word Doc:
User generated image

ASKER CERTIFIED SOLUTION
Avatar of kunal singh
kunal singh

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of kunal singh
kunal singh

ASKER

i found a solution to this problem.
1. all dynamically created host controls are disconnected from their events and they lose their data binding functionality. You can add code to your solution to re-create the host controls when the document is reopened (Dynamic MsControl)

Step 1 create a word-addsin
User generated image
Step 2Add reference to the project for
Microsoft.Office.Tools.Word.v4.0.Utilities.dll

Open in new window

User generated image
Step 3 Add a ribbon to the project
User generated image
  • Now your project should look like this 
User generated image
Step 4go to your ThisAddsIn.cs file
 public partial class ThisAddIn    {

        private void ThisAddIn_Startup(object sender, System.EventArgs e)        {        }         private void ThisAddIn_Shutdown(object sender, System.EventArgs e)        {        }         #region VSTO generated code
        /// <summary>        /// Required method for Designer support - do not modify        /// the contents of this method with the code editor.        /// </summary>        private void InternalStartup()        {            this.Startup += new System.EventHandler(ThisAddIn_Startup);            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);        }       
        #endregion    } 

Open in new window

create a new method in the above class name it WhenRibionBtnIsClicked()
  private Microsoft.Office.Tools.Word.Controls.Button button = null;//decalre a global varible  internal void WhenRibionBtnIsClicked()  {    Document vstoDocument = Globals.Factory.GetVstoObject(this.Application.ActiveDocument);    Word.Selection selection = this.Application.Selection;    if (selection != null && selection.Range != null)    {      string name ="myBtn";      Button button = new Button();      button.Click += new EventHandler(Generatedbtn_Click);      button = vstoDocument.Controls.AddButton(selection.Range, 100, 30, name);    
      button.Text = "I am A Generated Button";      button.Name = name;    
     /*this part is done so that when the document is closed the button state is saved in the document property*/      string startPosition = selection.Range.Start.ToString();      string endPosition = selection.Range.End.ToString();      //create a custom property to save infornmation needed to recreate the button    Microsoft.Office.Core.DocumentProperties properties =(DocumentProperties)Globals.ThisAddIn.Application.ActiveDocument.CustomDocumentProperties; 
//save the start range pos   if (properties.Cast<DocumentProperty>().Where(c => c.Name == "startPosition").Count() == 0)    {      properties.Add("startPosition", false, MsoDocProperties.msoPropertyTypeString, startPosition);    }    else{          properties["startPosition"].Value = startPosition;          Globals.ThisAddIn.Application.ActiveDocument.Saved = false; //important somtimes        }       //save the End range pos   if (properties.Cast<DocumentProperty>().Where(c => c.Name == "endPosition").Count() == 0)   {     properties.Add("endPosition", false, MsoDocProperties.msoPropertyTypeString, endPosition);   }else       {         properties["endPosition"].Value = endPosition;         Globals.ThisAddIn.Application.ActiveDocument.Saved = false; //important somtimes       }   // Store Button Info   if (properties.Cast<DocumentProperty>().Where(c => c.Name == "btnName").Count() == 0)     {      properties.Add("btnName", false, MsoDocProperties.msoPropertyTypeString, name);     } else            {               properties["btnName"].Value = startPosition;               Globals.ThisAddIn.Application.ActiveDocument.Saved = false; //important somtimes            }   if (properties.Cast<DocumentProperty>().Where(c => c.Name == "btnText").Count() == 0)  {    properties.Add("btnText", false, MsoDocProperties.msoPropertyTypeString, "I am A Generated Button");  }else      {        properties["btnText"].Value = "I am A Generated Button";        Globals.ThisAddIn.Application.ActiveDocument.Saved = false; //important somtimes                          }   //you can add more custom properties just like how i added start and end strings    }//end of if selection != null && selection.Range != null
  }//end of WhenRibionBtnIsClicked Method

Open in new window

Step 5Create a button click event for the button that has been generated in the word file
 void Generatedbtn_Click(object sender, EventArgs e) {    MessageBox.Show("I have Been Clicked :-O"); } 

Open in new window

Step 6and now in the Ribbon.cs file get the click event of the button created in the ribbon and point it to the above created class
  private void btnTest_Click(object sender, RibbonControlEventArgs e)        {            Globals.ThisAddIn.WhenRibionBtnIsClicked();        }

Open in new window

so now when you run it you should be able to see the following:
User generated image
however the problem you will face after this is when you save the document this things are removed the so what you must do is re-create this buttons when the word starts and use the properties you have stored to get the button state or other info about the button
Step 7go to the function ThisAddIn_Startup
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{  Word.Application wb;  wb = this.Application;  if (wb.Documents.Count > 0)   {      String queryResult_StartPosition = String.Empty;      String queryResult_EndPosition = String.Empty;      String queryResult_btnName = String.Empty;      String queryResult_btnText = String.Empty;      Microsoft.Office.Core.DocumentProperties properties = (DocumentProperties)Globals.ThisAddIn.Application.ActiveDocument.CustomDocumentProperties;      //get doc info about start pos     if (properties.Cast<DocumentProperty>().Where(c => c.Name == "startPosition").Count() > 0)    {        queryResult_StartPosition = properties["startPosition"].Value;    }    else       {         queryResult_StartPosition = String.Empty;       }       //get doc info about end pos               
    if (properties.Cast<DocumentProperty>().Where(c => c.Name == "endPosition").Count() > 0)  {    queryResult_EndPosition = properties["endPosition"].Value;  }   else       {         queryResult_EndPosition = String.Empty;       }   //get info about btn name    if (properties.Cast<DocumentProperty>().Where(c => c.Name == "btnName").Count() > 0)     {       queryResult_btnName = properties["btnName"].Value;     }      else         {           queryResult_btnName = String.Empty;         }      //get info about btn text if (properties.Cast<DocumentProperty>().Where(c => c.Name == "issue_title").Count() > 0) {   queryResult_btnText = properties["btnText"].Value; }   else   {     queryResult_btnText = String.Empty;   }  Document vstoDocument = Globals.Factory.GetVstoObject(this.Application.ActiveDocument); Word.Range rng = vstoDocument.Range(queryResult_StartPosition, queryResult_EndPosition);
 Word.Selection selection = this.Application.Selection; if (selection != null && rng != null) {   Button button = new Button();   button.Click += new EventHandler(Generatedbtn_Click);   button = vstoDocument.Controls.AddButton(rng, 100, 30, queryResult_btnName);   button.Text = queryResult_btnText; } }//end of wb.Documents

}//end of ThisAddIn_Startup

Open in new window

the above code should be able to get all your info from the custom property that you made and re-create the button. if for some reason when you start the document and the adds-in tab does not show up in word that means there is an error in your ThisAddIn_Startup function -to debug try putting everything in ThisAddIn_Startup function into a separate button then test and see the error code. Please Note: you can basicaly do everything you would do in a normal C# form (opening forms closing forms,reading text files, and etc)
i have provided a sample in github with a debug button you can check
Persist Dynamic Controli found a solution to this problem.
1. all dynamically created host controls are disconnected from their events and they lose their data binding functionality. You can add code to your solution to re-create the host controls when the document is reopened (Dynamic MsControl)

Step 1 create a word-addsin
User generated image
Step 2Add reference to the project for
Microsoft.Office.Tools.Word.v4.0.Utilities.dll

Open in new window

User generated image
Step 3 Add a ribbon to the project
User generated image
  • Now your project should look like this 
User generated image
Step 4go to your ThisAddsIn.cs file
 public partial class ThisAddIn    {

        private void ThisAddIn_Startup(object sender, System.EventArgs e)        {        }         private void ThisAddIn_Shutdown(object sender, System.EventArgs e)        {        }         #region VSTO generated code
        /// <summary>        /// Required method for Designer support - do not modify        /// the contents of this method with the code editor.        /// </summary>        private void InternalStartup()        {            this.Startup += new System.EventHandler(ThisAddIn_Startup);            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);        }       
        #endregion    } 

Open in new window

create a new method in the above class name it WhenRibionBtnIsClicked()
  private Microsoft.Office.Tools.Word.Controls.Button button = null;//decalre a global varible  internal void WhenRibionBtnIsClicked()  {    Document vstoDocument = Globals.Factory.GetVstoObject(this.Application.ActiveDocument);    Word.Selection selection = this.Application.Selection;    if (selection != null && selection.Range != null)    {      string name ="myBtn";      Button button = new Button();      button.Click += new EventHandler(Generatedbtn_Click);      button = vstoDocument.Controls.AddButton(selection.Range, 100, 30, name);    
      button.Text = "I am A Generated Button";      button.Name = name;    
     /*this part is done so that when the document is closed the button state is saved in the document property*/      string startPosition = selection.Range.Start.ToString();      string endPosition = selection.Range.End.ToString();      //create a custom property to save infornmation needed to recreate the button    Microsoft.Office.Core.DocumentProperties properties =(DocumentProperties)Globals.ThisAddIn.Application.ActiveDocument.CustomDocumentProperties; 
//save the start range pos   if (properties.Cast<DocumentProperty>().Where(c => c.Name == "startPosition").Count() == 0)    {      properties.Add("startPosition", false, MsoDocProperties.msoPropertyTypeString, startPosition);    }    else{          properties["startPosition"].Value = startPosition;          Globals.ThisAddIn.Application.ActiveDocument.Saved = false; //important somtimes        }       //save the End range pos   if (properties.Cast<DocumentProperty>().Where(c => c.Name == "endPosition").Count() == 0)   {     properties.Add("endPosition", false, MsoDocProperties.msoPropertyTypeString, endPosition);   }else       {         properties["endPosition"].Value = endPosition;         Globals.ThisAddIn.Application.ActiveDocument.Saved = false; //important somtimes       }   // Store Button Info   if (properties.Cast<DocumentProperty>().Where(c => c.Name == "btnName").Count() == 0)     {      properties.Add("btnName", false, MsoDocProperties.msoPropertyTypeString, name);     } else            {               properties["btnName"].Value = startPosition;               Globals.ThisAddIn.Application.ActiveDocument.Saved = false; //important somtimes            }   if (properties.Cast<DocumentProperty>().Where(c => c.Name == "btnText").Count() == 0)  {    properties.Add("btnText", false, MsoDocProperties.msoPropertyTypeString, "I am A Generated Button");  }else      {        properties["btnText"].Value = "I am A Generated Button";        Globals.ThisAddIn.Application.ActiveDocument.Saved = false; //important somtimes                          }   //you can add more custom properties just like how i added start and end strings    }//end of if selection != null && selection.Range != null
  }//end of WhenRibionBtnIsClicked Method

Open in new window

Step 5Create a button click event for the button that has been generated in the word file
 void Generatedbtn_Click(object sender, EventArgs e) {    MessageBox.Show("I have Been Clicked :-O"); } 

Open in new window

Step 6and now in the Ribbon.cs file get the click event of the button created in the ribbon and point it to the above created class
  private void btnTest_Click(object sender, RibbonControlEventArgs e)        {            Globals.ThisAddIn.WhenRibionBtnIsClicked();        }

Open in new window

so now when you run it you should be able to see the following:
User generated image
however the problem you will face after this is when you save the document this things are removed the so what you must do is re-create this buttons when the word starts and use the properties you have stored to get the button state or other info about the button
Step 7go to the function ThisAddIn_Startup
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{  Word.Application wb;  wb = this.Application;  if (wb.Documents.Count > 0)   {      String queryResult_StartPosition = String.Empty;      String queryResult_EndPosition = String.Empty;      String queryResult_btnName = String.Empty;      String queryResult_btnText = String.Empty;      Microsoft.Office.Core.DocumentProperties properties = (DocumentProperties)Globals.ThisAddIn.Application.ActiveDocument.CustomDocumentProperties;      //get doc info about start pos     if (properties.Cast<DocumentProperty>().Where(c => c.Name == "startPosition").Count() > 0)    {        queryResult_StartPosition = properties["startPosition"].Value;    }    else       {         queryResult_StartPosition = String.Empty;       }       //get doc info about end pos               
    if (properties.Cast<DocumentProperty>().Where(c => c.Name == "endPosition").Count() > 0)  {    queryResult_EndPosition = properties["endPosition"].Value;  }   else       {         queryResult_EndPosition = String.Empty;       }   //get info about btn name    if (properties.Cast<DocumentProperty>().Where(c => c.Name == "btnName").Count() > 0)     {       queryResult_btnName = properties["btnName"].Value;     }      else         {           queryResult_btnName = String.Empty;         }      //get info about btn text if (properties.Cast<DocumentProperty>().Where(c => c.Name == "issue_title").Count() > 0) {   queryResult_btnText = properties["btnText"].Value; }   else   {     queryResult_btnText = String.Empty;   }  Document vstoDocument = Globals.Factory.GetVstoObject(this.Application.ActiveDocument); Word.Range rng = vstoDocument.Range(queryResult_StartPosition, queryResult_EndPosition);
 Word.Selection selection = this.Application.Selection; if (selection != null && rng != null) {   Button button = new Button();   button.Click += new EventHandler(Generatedbtn_Click);   button = vstoDocument.Controls.AddButton(rng, 100, 30, queryResult_btnName);   button.Text = queryResult_btnText; } }//end of wb.Documents

}//end of ThisAddIn_Startup

Open in new window

the above code should be able to get all your info from the custom property that you made and re-create the button. if for some reason when you start the document and the adds-in tab does not show up in word that means there is an error in your ThisAddIn_Startup function -to debug try putting everything in ThisAddIn_Startup function into a separate button then test and see the error code. Please Note: you can basicaly do everything you would do in a normal C# form (opening forms closing forms,reading text files, and etc)
i have provided a sample in github with a debug button you can check
Persist Dynamic Control