Outlook AddIn Zoom.Percentage - c#

Im trying to translate this VBA code from an Outlook AddIn to C#
Private Sub objInspector_Activate() Handles objInspector.Activate
Dim wdDoc As Microsoft.Office.Interop.Word.Document = objInspector.WordEditor
wdDoc.Windows(1).Panes(1).View.Zoom.Percentage = lngZoom
End Sub
But I can't get access to the Panes.View.Zoom.Percentage property
The main idea is that when the user opens an email, he will get a custom zoom level.
What I got at the moment is:
void Inspector_Activate()
{
// this bool is true
// bool iswordMail = objInspector.IsWordMail();
//I get the word document
Document word = objInspector.WordEditor as Microsoft.Office.Interop.Word.Document;
word.Application.ActiveDocument.ActiveWindow.View.Zoom.Percentage = 150;
// at this point i'm getting an exception
// I've also tried with
// word.ActiveWindow.ActivePane.View.Zoom.Percentage = 150; getting the same exception
}
The exception is :
An exception of type 'System.Runtime.InteropServices.COMException'
occurred in OutlookAddInTest.dll but was not handled in user code
Additional information: This object model command is not available in
e-mail.
I'm quite new in C# and Office addins, any advise?

Use word.Windows.Item(1).View.Zoom.Percentage = 150 (where word comes from Inspector.WordEditor)

word.Application.ActiveDocument.ActiveWindow.View.Zoom.Percentage = 150;
What property exactly fires the exception?
Anyway, there is no need to call the Application and ActiveDocument properties in the code. The WordEditor property of the Inspector class returns an instance of the Document class (not Word Application instance).

Thanks to Eugene Astafiev for his help.
The square brackets did the trick
VBA
Private Sub objInspector_Activate() Handles objInspector.Activate
Dim wdDoc As Microsoft.Office.Interop.Word.Document = objInspector.WordEditor
wdDoc.Windows(1).Panes(1).View.Zoom.Percentage = 150
End Sub
C#
private void Inspector_Activate()
{
Document wdDoc = objInspector.WordEditor;
wdDoc.Windows[1].Panes[1].View.Zoom.Percentage = 150;
}

I've been wanting this forever, and then I stumbled on a nice project in the MSDN Gallery Outlook 2010: Developing an Inspector Wrapper. It has a set of wrappers for all the Outlook objects, so you get a true event for every item of interest. Not sure if it's the most efficient thing ever, but it seems to work.
I have trouble with my eyesight so want black everything, and zoom everything. I seem to be able to do that by overriding the Activate() method. It's all pretty new so we'll see if it survives long term.
protected virtual void Activate() {
var activeDocument = Inspector.WordEditor as Document;
if (activeDocument == null)
return;
var mailZoom = GetSetting("MailZoom", 125);
if (mailZoom != 0)
activeDocument.Windows[1].View.Zoom.Percentage = mailZoom;
if (GetSetting("MailBlack", true)) {
activeDocument.Background.Fill.ForeColor.RGB = 0;
activeDocument.Background.Fill.Visible = msoTrue;
activeDocument.Saved = true;
}
}
In this example, GetSetting is just a function that returns a setting from an INI file. you can use constants or some other storage method.
There might be a better way to get the white on black text, but this seems pretty good.

Related

How to set ContentControl.Range to the current ContentControl I am working from?

I am not finding a way to set the ContentControl.Range.Text from where the C# is executing from (inside the content control). Perhaps I should be looking at it from a completely different perspective.
Currently I have a content control that produces a set of text with some text between [] square brackets and I want to select text and format the colour by setting the start and end of the range of characters between the []. I am stuck on trying to set the initial range to the contentcontrol I am currently using.
Most of what I have managed/found/patched together below.
object word;
Microsoft.Office.Interop.Word.Document _PWdDoc;
try
{
word = System.Runtime.InteropServices.Marshal.GetActiveObject("Word.Application");
//If there is a running Word instance, it gets saved into the word variable
}
catch (Exception ex)
{
//If there is no running instance, it creates a new one
Type type = Type.GetTypeFromProgID("Word.Application");
word = System.Activator.CreateInstance(type);
}
Microsoft.Office.Interop.Word.Application oWord = (Microsoft.Office.Interop.Word.Application) word;
_PWdDoc = oWord.ActiveDocument;
System.Collections.IEnumerator ContentX = _PWdDoc.ContentControls.GetEnumerator();
//Microsoft.Office.Interop.Word.ContentControl ContentX = Microsoft.Office.Interop.Word.ContentControls.Item[];
//Microsoft.Office.Interop.Word.Range rng = Microsoft.Office.Interop.Word.ContentControl.Range.Duplicate(ref ContentX);
//var rngX = Microsoft.Office.Interop.Word.ContentControl.Range(ContentX);
//Microsoft.Office.Interop.Word.ContentControl cc1 = ContentX;
Excuse the coding mess but it's all I can come up with with the minimal experience I have with this.
Now I have gotten the IEnumerator fo the Content Control(I think) I have no idea how to use it besides from what I have read, they say to iterate through the IEnumerables accessing each of them. That's not what I want to do. I want 1 content control. The current one that I am working in. I want to find it's range and assign it to a value. Then in that range's "text" I want to do some [fancy] highlighting.
Determining whether the current selection or a specific Range is in a content control and doing something with that content control is not a trivial matter. Most other Word objects will return something that they're "in"; content controls do not.
So the approach I use is to
create a Range that reaches from the current selection (or a specific Range) back to the beginning of the document
count the number of content controls in that range
then check whether the current selection is in the same range as the last content control of the extended range.
if it is, then I know the selection is within a content control and I can access the content control.
Here's some sample code. The snippet that calls the function I use to return the information:
Word.Range rng = null;
//Substitute a specific Range object if working with a Range, rather than a Selection
Word.ContentControl cc = IsSelectionInCC(wdApp.Selection.Range);
if ( cc != null)
{
rng = cc.Range;
rng.HighlightColorIndex = Word.WdColorIndex.wdYellow;
}
The function:
private Word.ContentControl IsSelectionInCC(Word.Range sel)
{
Word.Range rng = sel.Range;
Word.Document doc = (Word.Document) rng.Parent;
rng.Start = doc.Content.Start;
int nrCC = rng.ContentControls.Count;
Word.ContentControl cc = null;
bool InCC = false;
rng.Start = doc.Content.Start;
if (nrCC > 0)
{
if (sel.InRange(doc.ContentControls[nrCC].Range))
{
InCC = true; //Debug.Print ("Sel in cc")
cc = doc.ContentControls[nrCC];
}
else
{
sel.MoveEnd(Word.WdUnits.wdCharacter, 1);
if (sel.Text == null)
{
//Debug.Print ("Sel at end of cc")
InCC = true;
cc = doc.ContentControls[nrCC];
}
}
}
return cc;
}
Assuming you mean that the insertion point is inside a Content Control, and your Word Application object is called oWord, then you can get the range of that content control using e.g.
Microsoft.Office.Interop.Word.Range r = oWord.Selection.Range.ParentContentControl.Range
If you have nested controls You can verify that the insertion point is in a Content Control (Word 2013 and later, I think) by checking the value of inCC as follows:
Boolean inCC = (Boolean)oWord.Selection.Information[Microsoft.Office.Interop.Word.WdInformation.wdInContentControl]
However, when dealing with content controls, be aware that selecting a content control in the UI is different from selecting the "range of the content control". Programmatically, it's obvious how to select the Range - not so obvious how to select the control. If you select the Range, the ParentContentControl should be the control whose range you've selected. If you (or the user) selected the control, OTTOMH I am not so sure.

Invalid printer exception

I have following code in a printing dialog in a windows form.
myPrintDialog = new PrintDialog();
System.Drawing.Bitmap memoryImage = new System.Drawing.Bitmap(pnVTCard.Width, pnVTCard.Height);
pnVTCard.DrawToBitmap(memoryImage, pnVTCard.ClientRectangle);
if (myPrintDialog.ShowDialog() == DialogResult.OK)
{
System.Drawing.Printing.PrinterSettings values;
values = myPrintDialog.PrinterSettings;
myPrintDialog.Document = printDocument1;
printDocument1.PrintController = new StandardPrintController();
printDocument1.Print();//This line shows system.drawing invalid printer exception when i hover over the code.
saveToVC(Convert.ToInt32(cmbVID.SelectedItem.ToString()), cmbElectionName.SelectedItem.ToString());
}
printDocument1.Dispose();
public System.Drawing.Printing.PrintDocument printDocument1 { get; set; }
when I try to handle the exception it shows null reference. Can someone kindly show what to correct.
As I don't know much about this can some one explain me what the wrong I'm doing here.?
pnVTcard is a panel control
Make sure you use references which are set to an instance of an object (sounds familiar? :) )
Maybe you are not setting printDocument1 before accessing its properties. Or maybe some other object, like those cmb... SelectedItem.
If you still can't pinpoint the culprit, go ahead and use break points and manually inspect the references.

Highlighting a particular control while capturing screenshot of a dialog in web page in c#

I have a requirement to capture the screen shot of the opened dialog with a particular html control highlighted ( whose static id is given ). currently I Implemented the code following manner :
public void Snapshot()
{
Image currentImage = null;
currentImage = GetOpenedDialogFrame().CaptureImage();
}
public UITestControl GetOpenedDialogFrame()
{
var dialogsFrames = new HtmlDiv(this.BrowserMainWindow.UiMobiControlDocument);
dialogsFrames.SearchProperties.Add(new PropertyExpression(HtmlControl.PropertyNames.Class, "mcw-dialog", PropertyExpressionOperator.Contains));
var dialogs = dialogsFrames.FindMatchingControls();
if (dialogs.Count == 0)
{
return null;
}
return dialogs[dialogs.Count - 1];
}
Now I have to write the code to highlight the particular html control while taking a screenshot. The DrawHighlight() method of Microsoft.VisualStudio.TestTools.UITesting.dll does not take any parameter so how can I highlight a particular html control in the screenshot.
DrawHighlight() is a method of a UI Control. It could be used in this style:
public void Snapshot()
{
Image currentImage = null;
var control = GetOpenedDialogFrame();
// TODO: protect the code below against control==null.
control.DrawHighlight();
currentImage = control.CaptureImage();
}
Whilst that answers your question about DrawHighlight, I am not sure it will achieve what you want. Please see this question the Microsoft forums where they are trying to do a similar screen capture.
Why not simply user the playback settings:
Playback.PlaybackSettings.LoggerOverrideState = HtmlLoggerState.AllActionSnapshot;
This will produce the html log file with all the screenshots that your codedui test went threw.
After searching for the matching controls you can try to highlight each one of them.
something like:
foreach( var control in controls)
{
control.drawhighlight();
}
that way you'll be able to which controls are located by the playback(qtagent to be more precise). furthermore this will help you decide which instance to refer to. (run and wait to see which controls are highlighted, pick the one you need and hard code it to be part of the test).
so after the test run you'll end up with something like:
var dialogs = dialogsFrames.FindMatchingControls();
dialogs[desiredLocation].drawhighlight();
hope this helps.

Page-Range-Problem at Printing a Document

i try to print out the content of my editor:
PrintDialog pd = new PrintDialog();
pd.PageRangeSelection = PageRangeSelection.AllPages;
pd.UserPageRangeEnabled = true;
FlowDocument fd = DocumentPrinter.CreateFlowDocumentForEditor(CurrentDocument.Editor);
DocumentPaginator dp = ((IDocumentPaginatorSource)fd).DocumentPaginator;
bool? res = pd.ShowDialog();
if (res.HasValue && res.Value)
{
fd.PageHeight = pd.PrintableAreaHeight;
fd.PageWidth = pd.PrintableAreaWidth;
fd.PagePadding = new Thickness(50);
fd.ColumnGap = 0;
fd.ColumnWidth = pd.PrintableAreaWidth;
pd.PrintDocument(dp, CurrentDocument.Editor.FileName);
}
The test-document i used has about 14 pages (with this pagesize-settings).
i tested it: the printdialog appears and I´ve chosen a pagerange (i typed "1-3" into the textbox) and clicked print. above the printdocument() I set a breakpoint and looked into the printdialog-object. it says pd.PageRangeSelection = PageRangeSelection.UserPage and pd.PageRange = {1-3}. I guess this is right, because I wanted to print out only page 1-3. then the printdocument() executed and in the output-pdf (for testing I use a pdf-printer) has 14 pages (the whole document was printed).
where is my mistake? why does the pagerange-setting not work?
thanks for your help
In your code you manually set:
pd.PageRangeSelection = PageRangeSelection.AllPages;
This is why your code prints all the pages.
The reason for this is because FlowDocument's DocumentPaginator does not handle UserPageRanges. You can see that FlowDocument implementation creates a FlowDocumentPaginator, and it doesn't take into account ranges.
If it did handle it, in FlowDocumentPaginator.(Async)GetPage you would see, code checking to see if the page requested to be printed is in an index of available pages; or maybe if a key exists in a Dictionary whose value is the DocumentPage to print.
In other words, and the reason the PrintDialog default has UserPageRangeEnabled set to false, is because in order to use that feature, you'll usually have to write your own DocumentPaginator or you have to add some logic to compile a new temporary document to hold only the pages you want to print.
Feel free to ask any questions.

_MailAutoSig Bookmark missing (Outlook 2010)

I wrote an addin a while back for Outlook that adds/removes an optional tagline below the signature in an outlook message. This add-in works with no issues.
I'm writing a second add-in that needs to potentially add information below that (whether or not the optional signature is there) and am again referencing the _MailAutoSig bookmark from the Word editor. The issue I'm running into is that this bookmark no longer seems to appear, nor does the bookmark from my other add-in.
One difference in the two pieces of code below is that the first one has the MailItem being converted from an object passed by ItemSend, whereas the second is processed BEFORE the ItemSend event.
Here is the code from what I am currently writing:
Word.Document toMsg = msg.GetInspector.WordEditor as Word.Document;
foreach (Word.Bookmark b in toMsg.Bookmarks)
Debug.Print(b.ToString());
Word.Range r_toMsg;
try
{
string oBookmark = "_MailAutoSig"; // Outlook internal bookmark for location of the e-mail signature`
object oBookmarkObj = oBookmark;
if (toMsg.Bookmarks.Exists(oBookmark) == true)
Debug.Print("sigbookmark");
r_toMsg = toMsg.Bookmarks.get_Item(ref oBookmarkObj).Range;
}
catch
{
string oOffsiteBookmark = "OffsiteBookmark";
object oOffsiteBookmarkObj = oOffsiteBookmark;
if (toMsg.Bookmarks.Exists(oOffsiteBookmark) == true) // if the custom bookmark exists, remove it
Debug.Print("offsite bookmark");
}
finally
{
r_toMsg = toMsg.Range(missing,missing);
}
and here is code from my working add-in:
void InsertOffsiteSig(Outlook.MailItem oMsg)
{
object oBookmarkName = "_MailAutoSig"; // Outlook internal bookmark for location of the e-mail signature
string oOffsiteBookmark = "OffsiteBookmark"; // bookmark to be created in Outlook for the Offsite tagline
object oOffsiteBookmarkObj = oOffsiteBookmark;
Word.Document SigDoc = oMsg.GetInspector.WordEditor as Word.Document; // edit the message using Word
string bf = oMsg.BodyFormat.ToString(); // determine the message body format (text, html, rtf)
// Go to the e-mail signature bookmark, then set the cursor to the very end of the range.
// This is where we will insert/remove our tagline, and the start of the new range of text
Word.Range r = SigDoc.Bookmarks.get_Item(ref oBookmarkName).Range;
object collapseEnd = Word.WdCollapseDirection.wdCollapseEnd;
r.Collapse(ref collapseEnd);
string[] taglines = GetRssItem(); // Get tagline information from the RSS XML file and place into an array
// Loop through the array and insert each line of text separated by a newline
foreach (string taglineText in taglines)
r.InsertAfter(taglineText + "\n");
r.InsertAfter("\n");
// Add formatting to HTML/RTF messages
if (bf != "olFormatPlain" && bf != "olFormatUnspecified")
{
SigDoc.Hyperlinks.Add(r, taglines[2]); // turn the link text into a hyperlink
r.Font.Underline = 0; // remove the hyperlink underline
r.Font.Color = Word.WdColor.wdColorGray45; // change all text to Gray45
r.Font.Size = 8; // Change the font size to 8 point
r.Font.Name = "Arial"; // Change the font to Arial
}
r.NoProofing = -1; // turn off spelling/grammar check for this range of text
object range1 = r;
SigDoc.Bookmarks.Add(oOffsiteBookmark, ref range1); // define this range as our custom bookmark
if (bf != "olFormatPlain" && bf != "olFormatUnspecified")
{
// Make the first line BOLD only for HTML/RTF messages
Word.Find f = r.Find;
f.Text = taglines[0];
f.MatchWholeWord = true;
f.Execute();
while (f.Found)
{
r.Font.Bold = -1;
f.Execute();
}
}
else
{
// otherwise turn the plain text hyperlink into an active hyperlink
// this is done here instead of above due to the extra formatting needed for HTML/RTF text
Word.Find f = r.Find;
f.Text = taglines[2];
f.MatchWholeWord = true;
f.Execute();
SigDoc.Hyperlinks.Add(r, taglines[2]);
}
r.NoProofing = -1; // disable spelling/grammar checking on the updated range
r.Collapse(collapseEnd);
}
The problem is that Microsoft converts the "Office HTML" (I'm not sure the proper term) to normal HTML before it fires the ItemSend() event, which causes the _MailAutoSig bookmark to disappear.
The only way to get the _MailAutoSig bookmark back is to first CANCEL the ItemSend() event, then fire off a timer to run a function which will in turn access the email object and manipulate it how you want, add a user property to mark the email has been processed, and then send the email again.
For example:
Dim modItem As Object 'need to hold the item somewhere so the timer can access it
Sub object_ItemSend(ByVal Item As Object, Cancel As Boolean)
If Item.UserProperties.Item("isModded") Is Nothing Then
'User has composed a mail and hit "Send", we need to make our modifications to the signature though
modItem = item
Cancel = True 'cancel the Send so we can make the modifications
mytimer.Enabled = True 'fire off a timer to make the modifications
Exit Sub
Else
Item.UserProperties.Item("isModded").Delete 'this flag will keep the email from ping-ponging between ItemSend and the timer
End If
End Sub
'10 millisecond timer? I think the cancel is almost instant, but experiment
Sub mytimer_Timer()
mytimer.Enabled = False
If Not modItem Is Nothing Then
modItem.HtmlBody = ...... the signature bookmark will be intact again, so make your modifications ......
modItem.UserProperties.Add("isModded", olText) 'no ping-pong
modItem.Send 'send it again
modItem = Nothing
End If
End Sub
I had to do something similar for a project where some Outlook fields were not set until I was in the ItemSend() event, so I was forcing the email to send, get my information, then cancel the send. It worked great.
Now, this was written off the top of my head, so I'm sure the code above will not be perfect, but it should give you the idea of what needs to be done.

Categories