C# VSTO-Powerpoint-TaskPanes in separate windows. - c#

I'm creating a VSTO for my company, and have ran across a interesting issue that I could use some help with. I will try to explain this to the best of my ability. I have the AddIn set up right now for it to create 2 customTaskPanes upon start up via Application.AfterNewPresentation events. And the ability to hide/show these based on user input from togglebuttons on the Ribbon.
Now when I fire up the first PowerPoint 2010 called "Presentation1" everything works great, I can show/hide the TaskPanes and everything inserts the way it should. Now then I open up a second template called "Presentation2"(to help keep things straight here) Everything works great again, I can show/hide the TaskPanes and everything inserts fine. If I go back to "Presentation1" the inserts and everything functions fine, but when I got to hide/show the TaskPanes it hides/shows them on "Presentation2". And if I create a "Presentation3" the same thing will happen but both "Presentation1" and "Presentation2" control "Presentation3" TaskPanes. And if I close the "Presentation2" and "Presentation3" the "Presentation1" buttons do not show/hide anything at all.
Code in the ThisAddIn
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Application.AfterNewPresentation += new PowerPoint.EApplication_AfterNewPresentationEventHandler(Application_AfterNewPresentation);
}
private void Application_AfterNewPresentation(PowerPoint.Presentation Pres)
{
PowerPoint.Application app = Pres.Application;
PowerPoint.DocumentWindow docWin = null;
foreach (PowerPoint.DocumentWindow win in Globals.ThisAddIn.Application.Windows)
{
if (win.Presentation.Name == app.ActivePresentation.Name)
{
docWin = win;
}
}
this.myWebForm = new SearchWebForm();
this.myWebFormTaskPane = this.CustomTaskPanes.Add(myWebForm, "Search ",docWin);
this.myWebFormTaskPane.DockPosition = Office.MsoCTPDockPosition.msoCTPDockPositionRight;
this.myWebFormTaskPane.Width = 345;
this.myWebFormTaskPane.VisibleChanged += new EventHandler(WebFormTaskPane_VisibleChanged);
}
private void WebFormTaskPane_VisibleChanged(object sender, System.EventArgs e)
{
Globals.Ribbons.Ribbon1.searchButton.Checked = myWebFormTaskPane.Visible;
if (Globals.Ribbons.Ribbon1.searchButton.Checked == true)
{
myWebForm.SearchForm_Navigate();
}
}
And then this is in the ribbon
private void searchButton_Click(object sender, RibbonControlEventArgs e)
{
Globals.ThisAddIn.WebFormTaskPane.Visible = ((RibbonToggleButton)sender).Checked;
}

In PowerPoint 2007, custom task panes are shared across all presentation windows. If you want to have separate task panes assigned to each presentation you need to handle the corresponding events (WindowActivate, PresentationClose, etc.). You would also need to manage a list of all the task panes that you've created so you can show/hide the appropriate one. This is actually a well-known Outlook pattern frequently referred to in VSTO-world as InspectorWrappers - or in your case a DocumentWindowWrapper.
This has been changed for Powerpoint 2010 and now each taskpane is associated with a specific window. See this article.
Your error is that Globals.ThisAddIn.WebFormTaskPane does not necessarily correspond to the current presentations task pane - you need to lookup the proper task pane in your managed list (as mentioned above). When you create a new task pane (AfterNewPresentation), add it to your CustomTaskPane collection and provide a means of retrieving it.
public partial class ThisAddIn
{
private Dictionary<PowerPoint.DocumentWindow, DocumentWindowWrapper> pptWrappersValue =
new Dictionary<PowerPoint.DocumentWindow, DocumentWindowWrapper>();
}

Related

Switch InfoPath form view based on field value

I have an InfoPath 2013 form I'm working on, and we have the need to switch views based on job category. We can easily determine this based on the job number at the top of the form, but a rule placed in "Form Load" isn't working because the job number field gets populated after the form loads in InfoPath Filler. Essentially I want to show View A if the job number starts with a "1", View B if it starts with a "2", and so on.
This form already has C# code behind it, but I'm banging my head on the desk trying to get this working. In theory, I should be able to use the Change event of the field to switch views, but so far it ignores any code I put in for the "After Change" context of the field.
I'm using the following to switch views:
this.ViewInfos.SwitchView("MyView");
Does anyone have an example of how this should work?
Here is an example about switch view when InfoPath form data changed like Drop-Down List Changed Event for your reference:
public void InternalStartup()
{
EventManager.FormEvents.ContextChanged += new ContextChangedEventHandler(FormEvents_ContextChanged);
EventManager.XmlEvents["/my:DoanhNghieps/my:loaiHinhHoatDong"].Changed += new XmlChangedEventHandler(loaiHinhHoatDong_Changed);
}
public void FormEvents_ContextChanged(object sender, ContextChangedEventArgs e)
{
if (canRedirect)
{
canRedirect = false;
ViewInfos.SwitchView("CN");
}
}
private bool canRedirect = false;
public void loaiHinhHoatDong_Changed(object sender, XmlEventArgs e)
{
// Write your code here to change the main data source.
canRedirect = true;
}
More information: InfoPath: Drop-Down List Changed, Switch View

Windows Forms graph freezes when the title bar is clicked

I created a Windows Forms application (C#, .NET Framework 4.5) that graphs the data received via the serial line (ex. a sensor attached to a microcontroller). For the graphing I am using ZedGraph library. The problem I ran into is that if I click on the titlebar when the data is being received and plotted, the graph freezes for the duration of the click. As soon as I release the left mouse button, it normally continues the plotting, except that it now just connects the data point received right before the click with the one received right after the mouse button was released. An example is shown below:
This seems to happen only with the graphing part, since the program also outputs the received data to a file. If I plot the received data in another program after the measurement (Excel, Matlab, ...), all the data is as it should be, without any interruptions.
I tried to find what the reason might be, but I could not find a similar example anywhere, so any help would be appreciated.
In order to simplify the process of finding the solution I have also created a smaller program that simply prints the time from the stopwatch to a textBox. Here is the code:
namespace ClickFreezeTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
System.Threading.Thread t = new System.Threading.Thread(() => updateTextBox());
t.IsBackground = true;
t.Start();
}
private void updateTextBox()
{
Stopwatch myStopWatch = new Stopwatch();
myStopWatch.Start();
int a = 1;
while (a == 1)
{
textBox1.BeginInvoke(new Action(() => { textBox1.Text = Convert.ToString(myStopWatch.Elapsed); }));
Thread.Sleep(100);
}
}
}
}
The form looks like this:
As shown in the code above, the textBox is updated every 100 ms on a separate thread. The same problem appears as with the serial graphing program, so I am assuming that if the solution exists it has to do something with the form settings, maybe a control that allows the user to do something with the mouse.

TFS GetService in Outlook add-in run once

I want to attach a message from outlook in TFS workitem, so I created a small add-in in outlook to attach a message to workitem.
every thing is work fine, but it's work just once.
when i want to attach a message to a workitem, i've open a window form and select the workitem and attach the message. even though when the form closed it's wont be open again.
i check every thing and i found the problem : when i use tfsTeamProjectCollection.GetService(); the form wont be open again.
also I tried to put GetService in the startup, the Click event of button wont work.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
TeamConfigurations = new Configuration();
_inspectors = Application.Inspectors;
TeamConfigurations.TempFolder = #"C:\EntekhabTfsOutlook\";
LoadSetting();
CheckTempFolder();
AddMenuBar();
}
and the click event
private void AddMailToAttachment_Click(CommandBarButton ctrl, ref bool cancelDefault)
{
var explorer = Application.ActiveExplorer();
var selection = explorer.Selection;
var frm = new FrmWorkItemSelector(selection);
frm.ShowDialog();
}
and finally
var tfsTeamProjectCollection = new TfsTeamProjectCollection(new Uri(url))
{
ClientCredentials = new TfsClientCredentials(true)
};
var workService = tfsTeamProjectCollection.GetService<WorkItemStore>();
i couldn't find the problem.
p.s: i don't want to use another add-in like http://www.teamsystemsolutions.com/teamlook/features/microsoft-outlook-integration.aspx Or another 3rd party tools.
the problem is not related to tfs
I chnage the Create toolbar method , it's work
http://msdn.microsoft.com/en-us/library/ms268864(v=vs.90).aspx

Basic use of user controls, closing a user control and opening another, user control parameters

ladies and gentlemen, once again I unfortunately am going to bother you with newbie stuff. I have searched for this information for hours, so if there’s a thread with what I want on it, it’s buried deeper than I could find.
This was my first question here, and Mark Hall was kind enough to set me straight. Since then, I have created a new project and recreated my first three screens as user controls – a container/login, a choice screen, and a main screen (currently empty). If a user has more than one collection, the choice screen pops up and allows them to choose a collection.
I did run into a snag with parameters, but I’m solving that by overloading the form declaration (solution found here) – yes, I know it’s much better to send parameters through calls, but I’d hate to have to create a call for each parameter (do I?) and… OK, OK, I’m better at this than {get, set}. Man, I hate being a newbie.
Anyways, I’m having trouble with the choice form – I can’t seem to call it, close it, then go to the main form. I have no problem (if there’s only one collection) in going straight to the main form, it’s that darn choice form. Yes, I know I could include a choice datagridview, but a few of our end users aren’t the sharpest bulb in the tool shed, and need hand-holding. Anyways, here’s the code.
CONTAINER/LOGIN SCREEN
namespace DeleteThis
{
public partial class ContainerForm : Form
{
Main Main = new Main();
LoginCollectionChoice LoginChoice = new LoginCollectionChoice();
DataTable dtPermissions = new DataTable();
public ContainerForm()
{
InitializeComponent();
Main.ExitEvent += new Main.ExitEventHandler(Main_ExitEvent);
LoginChoice.ExitEvent += new
LoginCollectionChoice.ExitEventHandler(LoginChoice_ExitEvent);
}
void LoginChoice_ExitEvent(object sender, EventArgs e)
{
pnlcontainer.Controls.Remove(LoginChoice);
}
void Main_ExitEvent(object sender, EventArgs e)
{
pnlcontainer.Controls.Remove(Main);
}
private void btnLogin_Click(object sender, EventArgs e)
{
LoginProcedure();
}
private void LoginProcedure()
{
DataTable dtPermissions = AdminClass.GetCollectionsForUser(int.Parse(txtbxUserName.Text));
if (dtPermissions.Rows.Count == 1)
{
//Valid user, one collection. Head right in.
pnlcontainer.Controls.Add(Main);
Main.BringToFront();
}
else
{
//More than one collection found. Giving user choice
LoginCollectionChoice LoginChoice = new LoginCollectionChoice(dtPermissions);
pnlcontainer.Controls.Add(LoginChoice);
LoginChoice.BringToFront();
}
}
private void btnExitProgram_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
I hope I didn't kill anything in the snipping. And now the choice screen…
public partial class LoginCollectionChoice : UserControl
{
public delegate void ExitEventHandler(object sender, EventArgs e);
public event ExitEventHandler ExitEvent;
private static DataTable dtPermit;
DataTable dtPermissions = new DataTable();
public LoginCollectionChoice()
{
}
public LoginCollectionChoice(DataTable dtPermissions)
{
InitializeComponent();
GrdCollection.DataSource = dtPermissions;
dtPermit = dtPermissions;
}
private void btnChoose_Click(object sender, EventArgs e)
{
//Code for the user to choose a collection
ExitEvent(this, new EventArgs());
}
}
I’ve snipped all non-relevent code, I hope you gentlemen and ladies can help this newbie get on the right path. Please, be gentle, you wouldn’t want to see me cry, would you? :) Oh! And if you know of any great tutorial sites, please email them to me. I'd prefer to spend a week on tutorials than a week on stumbling and asking here. Thank you all very much.
Are you calling logonForm.Show()?
It seems like you'd need to show it that way.
You use BringToFront(), but I think it needs to be shown first.
The first thing that jumps out to me is that you are calling
LoginCollectionChoice LoginChoice = new LoginCollectionChoice(dtPermissions);
which is creating a local variable called LoginChoice, which is not the same as your class variable even though they share the same name.
What you want to do is to not declare a local variable in that method. Change that line to
LoginChoice = new LoginCollectionChoice(dtPermissions);
Having said that, I believe tylerjgarland in that you need to call .Show() first. And the way you are closing the form is certainly odd. I would create a form, showDialog, get the result and then close the form that way.

Excel Automation: Protect single worksheet from deletion by user

VSTO 4.0 / Office 2007
In an Excel document-level automation project, I have a worksheet that must not be deleted from the workbook. I am afraid that a careless user might delete it by accident, which currently causes a lot of grief (exceptions galore).
I can not protect the entire workbook, because the user must be able to create, delete and otherwise modify this file. Exactly one sheet needs to be protected from deletion, but I may be overlooking something, so if there exists such solution I'm all ears. For example I could imagine that I can Protect() and Unprotect() the workbook when the sheet is visible, but this solution seems messy.
Googling yielded the following VBA code:
Private Sub Worksheet_Activate()
Dim CB As CommandBar
Dim Ctrl As CommandBarControl
For Each CB In Application.CommandBars
Set Ctrl = CB.FindControl(ID:=847, recursive:=True)
If Not Ctrl Is Nothing Then
Ctrl.OnAction = "RefuseToDelete"
Ctrl.State = msoButtonUp
End If
Next
End Sub
I'm not familiar with VBA, but I tried running this from the VSTO generated startup method:
private void Sheet1_Startup(object sender, System.EventArgs e)
{
//Is there a neater way to iterate through all Office Collections?
for (var i = 1; i <= Application.CommandBars.Count; i++)
{
var commandBar = Application.CommandBars[i];
//847 is a magical constant that any fule no has something to do with sheet deletion
var control = commandBar.FindControl(Id: 847, Recursive: true);
if (control != null) control.OnAction = null;
}
}
This code seems to do exactly nothing. You may ask "Hey, Gleno, why are you setting OnAction to null" , well I don't know what to set it to... The linked VBA solution attaches to Activate and Deactivate events, so there's more code where that came from.
Thanks in advance.
I had to do something very similar today. I would just disable the sheet delete buttons whenever your one "undeleteable" sheet is active. If there's a keyboard shortcut to delete a sheet, I can't find one. (If there is, you could disable that too.)
This would go in your ThisWorkbook class:
private void ThisWorkbook_Startup(object sender, System.EventArgs e)
{
this.SheetActivate += (sh) =>
{
this.ribbon.InvalidateBuiltinControl("SheetDelete");
};
}
public bool CanDeleteActiveSheet()
{
if (this.ActiveSheet == null)
return true;
// Replace Sheet1 with your sheet's CodeName
return ((Excel.Worksheet)this.ActiveSheet).CodeName != "Sheet1";
}
// Keep a local reference to the ribbon in your ThisWorkbook class
// so you can call InvalidateControl() from it.
Ribbon ribbon;
protected override IRibbonExtensibility CreateRibbonExtensibilityObject()
{
this.ribbon = new Ribbon();
return this.ribbon;
}
This would go in your ribbon code behind:
public void InvalidateBuiltinControl(string controlID)
{
this.ribbon.InvalidateControlMso(controlID);
}
public bool deleteButton_GetEnabled(IRibbonControl control)
{
return Globals.ThisWorkbook.CanDeleteActiveSheet();
}
And this would go in your ribbon xml:
<commands>
<command idMso="SheetDelete" getEnabled="deleteButton_GetEnabled" />
</commands>
I'm still a little leery of holding on to that ribbon reference in ThisWorkbook, but so far no one has mentioned a better way in the question I posted earlier. Hope this helps!
I'm having a similar problem where I know how to protect the worksheet but I need to turn protection on after the sheet has been populated with external data from a SQL connection. I cannot locate the correct event to do this.
This should help you put it in the startup event for the worksheet:
Me.Protect(password:="password", allowFiltering:=True, allowSorting:=True, allowUsingPivotTables:=True)

Categories