Outlook 2013 Form Region and the Delete Key - c#

So, I've finally had to deal with this annoying issue. It seems that it's a known "bug" and there doesn't seem to be a great work-around. I was wondering what seems to be the best work around for this.
A little bit of info. In my form region I have a Winform control and a WPF control. The user can't do certain key combinations on the Winform control (Ctrl-A to select all, Delete key deletes email instead of highlighted text), but everything works fine on the WPF control.
I've tried adding the Winform control to the WPF control using a WindowsFormHost, but that made it worse as it wouldn't register the backspace key after that. I tried capturing the delete event for the email, but I can't get the .BeforeDelete to trigger. Same for the Explorer.BeforeItemCut event. Currently I'm trying to capture the WndProc event to re-direct the key events, but it seems like there should be a better/easier way.
Not sure how to continue from here. Any help in direction is welcomed. Below is my how I'm trying to capture email delete event.
Outlook.MailItem _selEmail;
// This does get triggered
private void Explorer_SelectionChange()
{
var actExplorer = this.Application.ActiveExplorer();
if(this.Application.ActiveExplorer().Selection.Count > 0)
{
var selObject = actExplorer.Selection[1];
if(selObject is Outlook.MailItem)
{
_selEmail = selObject as Outlook.MailItem;
_selEmail.BeforeEmailDelete -=
new Outlook.ItemEvents_10_BeforeDeleteEventHandler(Email_BeforeDelete);
_selEmail.BeforeEmailDelete +=
new Outlook.ItemEvents_10_BeforeDeleteEventHandler(Email_BeforeDelete);
}
}
}
// Haven't gotten this to trigger. The Console.Write("") is there
// only for a breakpoint;
private void Email_BeforeDelete(object sender, ref bool cancel)
{
Console.WriteLine("");
}

First of all, I'd suggest breaking the chain of property and method calls and declare each property or method call on a separate line of code. Thus, you will be able to release underlying COM objects inplace. Use System.Runtime.InteropServices.Marshal.ReleaseComObject to release an Outlook object when you have finished using it. Then set a variable to Nothing in Visual Basic (null in C#) to release the reference to the object. You can read more about that in the Systematically Releasing Objects article in MSDN.
Try to turn off the Single key reading using the space bar option in Outlook which allows you to move quickly through your messages in the Reading Pane. The space bar scrolls the current item. At the end of the item, it jumps to the next unread item in your message list.
Finally, using WPF controls on Outlook forms produce a well-known issue. Outlook has a habit of swallowing various keys and not sending them along to your code or form region. The spacebar, tab, backspace keys are among those affected when the keys are pressed in the reading pane. You can find a similar forum thread.

Related

Modifying current email item visually

I'd like to ask if there is any possibility to modify the currently selected item's sender visually only. I just want to visualize text next to the sender's email.
I'm applying image to visualize what I am trying to achieve
What I have tried so far is using the ActiveInspector and modifying the current item's Sender.Address value.
public static void test(MailItem item)
{
var currItem = item.GetInspector.CurrentItem;
if(currItem == Microsoft.Office.Interop.Outlook.OlObjectClass.olMail)
{
currItem.Sender.Address += "test";
}
}
Any ideas? I've seen that the currentItem is System.__ComObject.
There is no trivial way for customizing the sender's information without changing the underlying properties of Outlook items.
You may try using Windows API functions for subclassing Outlook windows and injecting your own form on top of built-in ones. See SetWindowsHookEx for more information, it installs an application-defined hook procedure into a hook chain. You would install a hook procedure to monitor the system for certain types of events. These events are associated either with a specific thread or with all threads in the same desktop as the calling thread.
Also you may consider using the replacement-all form region layout. A replacement form region is a page that replaces the default page of a standard form, and a replace-all form region replaces all pages in a standard Outlook form.
As the last resort, you may consider using Add-in Express layouts where you can hide the sender's pane and show your own.

Cannot read held state of key in WPF inside mouse event handler - Inconsistent behaviour in Windows guest on VMWare Fusion for Mac

This issue is caused by an inconsistent setting in vmware fusion - the secondary mouse function of MacOS (ie, right click) was being triggered by the control-left click, in spite of being configured in vmware fusion settings to send control through as a separate key.
I am leaving this here as it outlines the diagnostic steps taken to isolate the issue.
This behaviour might be present in other virtualisation solutions on MacOS.
This is not a duplicate - I have tried the approaches suggested - They Do Not Work. Genuine help is required please.
I am trying to implement a multiple select function in a WPF canvas, and need to be able to detect when either control key is held while clicking on an item.
I have already tried using the documented and accepted methods of getting the key (Keyboard.IsKeyDown and Keyboard.Modifiers) and although they are supposed to work, they do not in this case.
I cannot place a key handler in the main window of the application as this solution is implemented in a component, and as such, I do not have access to the main window.
I cannot create a keyboard OnKeyDown override, as it does not register the event. I do not know why this is, but suffice to say, I have tried all the methods I can find after an extensive search on Stack Overflow, and am currently out of ideas.
In the current logic, clicking an item in my canvas will clear any selected items already. If the control key is held, I will not clear selected items.
The structure of my application is as follows:
Main Window -> Dock Panel -> ScrollViewer -> Main Custom Component (extends Canvas) -> ChildCanvas objects (multiple instances, all also extend Canvas).
The component will not have any reference back to the main window - this is absolutely not negotiable.
The component must not require a reference to the main window to be passed in to it.
The component cannot rely on any logic, properties, methods or other functional code being placed in the MainWindow - it must be totally reusable.
Multiple instances of a custom object (ChildCanvas) which itself derives from Canvas are placed onto a base custom object (ExtendedCanvas) which also extends Canvas.
I am detecting the mouse click in a mouse event handler within my custom item.
Although I can in theory handle the key event and set a bool flag when the key goes down and clear it when the key goes up, this is not a reliable way to do things as it would require the focus to be on whichever component is handling the key.
All the examples I have looked at require a specific event handler for the key press, but I cannot apply that approach in this case as it simply does not seem to register the key press, even if the component where the event handler is implemented has focus at the time.
I would like to find a way to check if a key is held down in the mouse click handler if this is possible in WPF.
EDIT: I have tried using the Keyboard.Modifiers approach, but this returns no result.
EDIT: I have also tried Keyboard.IsKeyDown(Key.LeftCtrl), which does not return any values.
EDIT: I have tried to handle the key event globally by registering on the EventManager as shown. What I have found is that key up and key down events are triggered in quick succession. The first event has the property e.IsRepeat as false, and all subsequent event occurrences have e.IsRepeat set as true. Using these events, I have set a bool which is set to true in the first keyDown event is e.IsRepeat is false (the first initial key press), and set to false in the keyUp event if e.IsRepeat is false (the keyUp event fire when the key is physically released).
This does not work - clicking the item on the canvas when the key is held altered the state of the bool setting it to false.
This behaviour is totally inconsistent with what Microsoft document for the way the keyboard handling should work.
EventManager.RegisterClassHandler(typeof(Control),
Keyboard.KeyDownEvent,new KeyEventHandler(keyDown),true);
I can call Keyboard.IsKeyToggled(Key.LeftCtrl) which does show the toggled state, but it changes with every key press, and does not show me whether the key is held down at the time of the mouse click.
public class ExtendedCanvas:Canvas {
public ExtendedCanvas() {
MouseUp += thisMouseUp;
}
void thisMouseUp(object sender,MouseButtonEventArgs e) {
if ((Keyboard.Modifiers & ModifierKeys.Control) > 0) {
//This does not work - no key is registered, as for some reason, Keyboard.Modifiers does not register that the key is held.
}
//If CONTROL is held down
if (!ControlKeyHeldDown) {
ClearSelectedItems();
}
IsSelected = true;
}
}
The inconsistent behaviour is the result of a VMWare option.
This VM is being run on MacOS and in spite of VMWare Fusion being configured to send the Control key straight through, Control Click was still triggering the secondary mouse button (ie, right click in this case).
The solution is:
1) Shut down the VM.
2) In VMWare Fusion Preferences -> Keyboard And Mouse -> Mouse Shortcuts, ENABLE (Check) the "Secondary Button" option if it is not checked.
3) Close the preferences window to save the settings.
4) Close VMWare Fusion
5) Reboot the host system
6) In VMWare Fusion Preferences -> Keyboard And Mouse -> Mouse Shortcuts, DISABLE (UnCheck) the "Secondary Button" option.
7) Start the VM, and confirm that Control Click no longer triggers the secondary click.
This option had been configured to send Control straight through prior to upgrading VMWare Fusion from 8 to 10.
It appears that although the Secondary Function checkbox was unchecked, the property was set to true, and it was behaving inconsistently from the way the settings suggested it should.

In vsto C#, when copying(no pasting) a range, how to know this range(row/col/rect info)?

Example in excel 2013,
I select range A1:C3 and ctrl-c or right click copy.
Noticing there will be an animation moving arounding this range, meaning you just copied it.
Now I just want to get this range in my code.
I just hooked the copy invent in windows message. And how can I get this range?
pls help.
Example code:
private override onCopy(Excel.Range source){
//...
base.onCopy(source);
//Now I can get the source range when copy happens.
//But in vsto, there is no such method.
//Help me to work around
}
You can use the Selection object to get the current selection. But it may return another selection, not just copied.
To get the copied Seletion object you need to set a keyboard hook for intercepting the Ctrl+C keyboard buttons and/or repurspose the ribbon controls.
See Using shortcut keys to call a function in an Office Add-in to get started with Windows Hooks.
Most probably repurposing the ribbon controls will be enough (context menus use the Ribbon UI). See Temporarily Repurpose Commands on the Office Fluent Ribbon for more information.

Sharepoint Ribbon: Circumventing Context sensitivity

I'm new to sharepoint development and I'm trying to modify the behaviour of the Sharepoint ribbon. As you all know, the ribbon is such that when something else gains focus(e.g a list item), the ribbon automatically switches to an appropriate tab or tab group(e.g the List tools tab group).
I'd like to disable this constant switching of tabs and make the browse tab to always be the active tab, unless the user explicitly clicks on another tab.
I've tried doing the following in the Page_Load() of a Usercontrol, but it only works once, when the page is initially loaded. What am I doing wrong? More importantly, how could I do it right, if at all?
Basically, I'm hoping someone could point me to the event that's fired when the context changes and the ribbon switches, and how I could hook up to this event and force the ribbon to switch back to the browse tab.
protected void Page_Load()
{
string showBrowseTabScript = string.Empty;
showBrowseTabScript = #"
function ShowBrowseTab() {
var ribbon = SP.Ribbon.PageManager.get_instance().get_ribbon();
SelectRibbonTab(""Ribbon.Read"", true);
}
SP.SOD.executeOrDelayUntilScriptLoaded(function() {
var pm = SP.Ribbon.PageManager.get_instance();
pm.add_ribbonInited(function() {
ShowBrowseTab();
});
var ribbon = null;
try
{
ribbon = pm.get_ribbon();
}
catch (e) { }
if (!ribbon) {
if (typeof(_ribbonStartInit) == ""function"")
_ribbonStartInit(_ribbon.initialTabId, false, null);
}
else {
ShowBrowseTab();
}
},
""sp.ribbon.js"");
";
this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "BrowseTabScript", showBrowseTabScript, true);
}
Here is my solution to the problem, in case anyone is interested.
Taking Ken Henderson's suggestion into consideration, I was able to achieve what I've been trying to do, although I achieved this by modifying the code of the SP.Ribbon.js and SP.Ribbon.debug.js files. I'm using the SP.Ribbon.debug.js to show my solution below, since it is not as cryptic as the SP.Ribbon.js.
Basically, I use the code below to trick the ribbon into thinking that the User is on a different tab and has clicked on the "Browse" tab. You will notice that I set the old tab information in the code. It will still work without me doing this, but I did it just in case the ribbon needs that information for something else I'm not aware of. This code, in combination with the Page_Load() function I posted in the first post, cause the ribbon to behave just like I needed it to.
SP.Ribbon.PageManager.prototype = {
executeRootCommand: function (commandId, properties, commandInfo, root) {
ULSMg8: ;
var $v_0;
if (!SP.ScriptUtility.isNullOrUndefined(commandInfo) && commandId !== 'RibbonEvent' && (commandId !== 'CommandContextChanged' || (!SP.ScriptUtility.isNullOrUndefined(properties) && properties['ChangedByUser']))) {
// My changes to SP.Ribbon
if (properties["ChangedByUser"] === false) {
properties["ChangedByUser"] = true;
var $NewContextId = properties["NewContextId"];
var $NewContextCommand = properties["NewContextCommand"];
properties["OldContextId"] = $NewContextId;
properties["OldContextCommand"] = $NewContextCommand;
properties["NewContextId"] = "Ribbon.Read";
properties["NewContextCommand"] = "ReadTab";
SelectRibbonTab("Ribbon.Read", true);
}
// End of changes to SP.Ribbon
// the rest of the code has been ommitted for clarity
return $v_0;
}
}
To the best of my knowledge SharePoint doesn't expose any events to detect when the ribbon tabs update (either tabs adding/removing or which is active). At least I was unable to find any a few weeks ago when I was trying to detect when tabs were added/removed (I didn't care which was active just the number/width of them).
(Sorry for the lack of details, the SharePoint dev environment at the office is unavailable at the moment so I can't look up the details very easily.)
There are two possiblities for solving this problem (each has risks/problems):
Override JS Functionality
Figure out what JavaScript function is being called when the user clicks on an item that updates the ribbon. You might be able to replace that function with your own that provides the behavior you want. This would be similar in concept to a custom master page that scrolls on the window and has to change the behavior of the width sizing. I'm unable to verify the details at the moment but it looks like the function is called SingleItemSelect in core.js.
This could be a problem if you have exceptions to when to override this behavior and if MS changes anything in the future you're implementation may break and/or need to be updated.
Add your own event handler
In your JavaScript code try to find an appropriate DOM event to attach an event handler to in the ribbon to detect when MS's code changes the ribbon. There is a good chance given the limitations of the DOM events that there will not be an event to attach a handler to. You may end up adding a function that is called periodically (polling loop/timer) that detects ribbon tab changes and resets the active one.
Honestly this will not work well since there will be flickering as MS's ribbon code changes the active tab and your's changes it back. Additionally you'll need to detect when the user clicks on a tab so that you don't undo their changes.
Wrap up
Honestly I would push back and get this requirement changed so that the ribbon behavior works the way MS designed and not try to fight it. If the ribbon showing up when the user clicks on an item is really an issue then I would propose to the client that instead of forcing the Browse tab as active to add an additional link in the ribbon area somewhere that allows the ribbon (or at least the part that expands over the title area) to be toggled as hidden/shown independently of what MS's JavaScript is doing to the ribbon.

AxAcroPDF swallowing keys, how to get it to stop?

The AxAcroPDF swallows all key-related events as soon as it gets focus, including shortcuts, key presses, etc. I added a message filter, and it doesn't get any key-related messages either. It's a COM component, could that be relevant?
Is there any way to catch these before the control starts swallowing them?
Hans is correct, the Acrobat Reader spawns two child AcroRd32 processes which you have no direct access to from within your managed code.
I have experimented with this and you have three viable options:
You can create a global system hook, and then look for and filter out / respond to WM_SETFOCUS messages sent to your child AcroRd32 windows. You can accomplish some of this from within C# by using a wrapper library, such as the one here: http://www.codeproject.com/KB/system/WilsonSystemGlobalHooks.aspx
You also need to identify the correct processes as there may be more than one instance of your application, or other instances of AcroRd32. This is the most deterministic solution, but because your application will now be filtering messages sent to every single window in existence, I generally don't recommend this approach because then your program could negatively affect system stability.
Find an alternate PDF viewing control. See this answer for a few commercial components: .net PDF Viewer control , or roll your own: http://www.codeproject.com/KB/applications/PDFViewerControl.aspx
Find an acceptable hack. Depending on how robust your application needs to be, code such as the following may be suitable (it was suitable for my case):
DateTime _lastRenav = DateTime.MinValue;
public Form1()
{
InitializeComponent();
listBox1.LostFocus += new EventHandler(listBox1_LostFocus);
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
axAcroPDF1.src = "sample.pdf"; //this will cause adobe to take away the focus
_lastRenav = DateTime.Now;
}
void listBox1_LostFocus(object sender, EventArgs e)
{
//restores focus if it were the result of a listbox navigation
if ((DateTime.Now - _lastRenav).TotalSeconds < 1)
listBox1.Focus();
}
I might finally have a ridiculously simple answer. So far in testing this is working.
Having suffered from this problem for quite some time and having built a complex system of each custom control recording which of them last had focus and using a timer to flip focus back (when acropdf grabbed it) I revisited this problem and read a great number of answers (looking for recent solutions). The information gleaned helped me with the idea.
The idea is to disable the (acropdf) control whilst it is loading as in the following example (code reduced for clarity)
AxAcroPDF_this.Enabled = False
AxAcroPDF_this.src = m_src
Then on a timer, after say 1 second.
AxAcroPDF_this.Enabled = True
Basically the idea is to tell Windows not to let users use the acropdf control until allowed, so asking Windows to prevent it from getting focus (because users are not allowed in there).
So far this is holding up, I will edit this if anything changes. If it doesn't work completely for you then maybe the idea points into a useful direction.
It is an out-of-process COM component, that's the problem. Completely in violation of Windows SDK requirements as laid out in SetParent(). Once its window gets the focus, the message loop in the acroread.exe process gets all the messages, your message filter cannot see any messages anymore.
Technically it is fixable by using SetWindowsHookEx() to inject a DLL into the process and monitor messages with WH_GETMESSAGE. But you can't write such a DLL in the C# language.
Major suck, I know. There never seems to be any lack of it with that program.
For some reason Tim's answer, disabling the AxAcroPDF control directly, didn't work in my case. The Leave event on the previously-selected Textbox would never fire, either.
What is working is nesting the AxAcroPDF control inside of a disabled GroupBox. Since the users of my application need to only see the PDF, not interact with it, the GroupBox's Enabled property is set to False in the designer.

Categories

Resources