Bug: Can't Choose Dates on a Datepicker That Fall Outside a Floating VSto Add-In

BUG: Can't choose dates on a DatePicker that fall outside a floating VSTO Add-In

"Floating" is the key to the problem here. What's never not a problem (occasionally responsible for odd things) is relying on the message pump in Excel to dispatch Windows messages, the messages that make these controls respond to input. This goes wrong in WPF as much as Winforms, they have their own dispatch loop that filters messages before they are delivered to the window. Key things that go wrong when their respective dispatcher isn't used are stuff like tabbing and short-cut keystrokes.

And then some, this kind of problem would be induced by Excel doing its own filtering before dispatching messages. I'd guess at an anti-malware feature, Microsoft is forever worried about programs messing with Office apps.

The Winforms solution is the same one as the WPF workaround, you need to pump your own message loop. That requires some surgery, DateTimePicker isn't going to cooperate since it doesn't allow its DropDown event to be cancelled and it is raised after the calendar is already shown. The workaround is silly but effective, add a button to your form that looks just like the dropdown arrow on the DTP and make it overlap the arrow so it gets clicked instead of the arrow.

Some example code for getting the button to overlap the dropdown arrow:

    public Form1() {
InitializeComponent();
var btn = new Button();
btn.BackgroundImage = Properties.Resources.DropdownArrow;
btn.FlatStyle = FlatStyle.Flat;
btn.BackColor = Color.FromKnownColor(KnownColor.Window);
btn.Parent = dateTimePicker1;
btn.Dock = DockStyle.Right;
btn.Click += showMonthCalendar;
dateTimePicker1.Resize += delegate {
btn.Width = btn.Height = dateTimePicker1.ClientSize.Height;
};
}

The Click event handler needs to show a dialog that contains a MonthCalendar:

    private void showMonthCalendar(object sender, EventArgs e) {
dateTimePicker1.Focus();
using (var dlg = new CalendarForm()) {
dlg.DateSelected += new DateRangeEventHandler((s, ea) => dateTimePicker1.Value = ea.Start);
dlg.Location = dateTimePicker1.PointToScreen(new Point(0, dateTimePicker1.Height));
dlg.ShowDialog(this);
}
}

With CalendarForm a form you add that's borderless and contains just a MonthCalendar:

public partial class CalendarForm : Form {
public event DateRangeEventHandler DateSelected;

public CalendarForm() {
InitializeComponent();
this.StartPosition = FormStartPosition.Manual;
monthCalendar1.Resize += delegate {
this.ClientSize = monthCalendar1.Size;
};
monthCalendar1.DateSelected += monthCalendar1_DateSelected;
}

void monthCalendar1_DateSelected(object sender, DateRangeEventArgs e) {
if (DateSelected != null) DateSelected(this, e);
this.DialogResult = DialogResult.OK;
}
}

VSTO WPF ContextMenu.MenuItem Click outside a TaskPane not raised

The Custom Task Pane has focus problems with WPF Context Menus. Look here.

I have the same problems. Actually I use one of the recommended workarounds (Dispatcher Frame). Is is working, but I am not very happy with this clumsy solution.

VSTO WPF modal dialogs cursor doesn't blink in TextBox

Noticed that none of the focus methods you mentioned seem to be the simplest case of Control.Focus(). So for example:

// Constructor of your window
public MyWindow()
{
// ...
// set any DataContext before InitializeComponent() if needed
// ...
InitializeComponent();
// ...
// add anything else you need to do within the constructor of the window
// ...
textbox1.Focus(); // Set focus on your text box
}

This was tested in an Excel VSTO add-in project and it immediately allows typing to go to the focused textbox as soon as the dialog is shown. Use with your window Owner hack (had to do similar in my project).

Difference between Control.Focus() and FocusManager.SetFocusedElement()

EDIT

Here's what I found after starting a VSTO add-in project from scratch. It appears to be one of many quirks with using WPF within an Excel VSTO add-in, where you can't reliably get Control.Focus() to work if it is used with the first WPF window ever created.

Workaround: Create a dummy WPF window. Close it. And then create the real one that will use Control.Focus().

Never noticed the problem before since my project always had a short "Loading" window open and close itself before presenting the real window.

bool isFirstTime = true;
private void button1_Click(object sender, RibbonControlEventArgs e)
{
if (isFirstTime)
{
// For some reason the Control.Focus() is unreliable for the first WPF window ever shown within the VSTO addin. :(
// So make a dummy one once before we open the real window...
// NOTE: The reason why I never noticed such a problem before in my own project, is since my project always showed a short loading window that closed itself
// before the main window is shown. So I could use Control.Focus() in the main window without issues
var pretendLoadWindow = new Window();
pretendLoadWindow.SizeToContent = SizeToContent.WidthAndHeight;
pretendLoadWindow.Visibility = Visibility.Collapsed;
pretendLoadWindow.Loaded += (_sender, _e) => pretendLoadWindow.Close();
pretendLoadWindow.ShowDialog();
isFirstTime = false;
}
var window = new Window();
var excelHwnd = m_ExcelApplication != null ? new IntPtr(m_ExcelApplication.Hwnd) : Process.GetCurrentProcess().MainWindowHandle;
WindowInteropHelper interopHelper = new WindowInteropHelper(window)
{
Owner = excelHwnd
};
window.Content = new UserControl1();
window.SizeToContent = SizeToContent.WidthAndHeight;
// FYI: just in case you have any breakpoints that switch the focus away from the Excel (to your VS IDE), then when this window is shown it won't typically get focus. Below should fix this...
window.Loaded += (_sender, _e) => window.Focus();
window.ShowDialog();
}

Full test code accessible from here

VSTO Word 2016: Squiggly underline without affecting undo

I stumbled on this question, which is asking exactly the same thing:
Prevent actions to be added to the word undo redo OR Remove actions from the undo redo CommandBarComboBox

As @Manu pointed out that same question was also asked over at MSDN where the answer was:

The UndoClear method will empty the list. That's the best you can do.

There is also a similar question on here Word VSTO override CTRL+Z / CTRL+Y
which suggests the Microsoft.Office.Interop.Word.UndoRecord route or MessageHooks in AddIns.


After more research I noticed a great idea at the end of this thread: MSDN Draw my own squigglies on Word document where you keep track of your actions and then skip past them in Undo and Redo operations.

Here is an excellent example of code to do "transactional undo/redo's" Can I create an undo transaction in Word or Excel? (VSTO) . You can do this same method in VSTO except for one big problem, as noted by Dirk Vollmar in his answer:

I don't think that overwriting built-in Word commands is possible using VSTO alone, though

I have overwritten some built-in commands in VSTO using keyboard hooking events to intercept commands: How to perform .Onkey Event in an Excel Add-In created with Visual Studio 2010?
However I'm not sure if you can recreate the Ribbon to intercept button commands. More specifically, the Undo and Redo are built-in galleries in the Ribbon UI and you can do nothing with a built-in Ribbon gallery. And in versions like 2010 the Undo/Redo buttons are in the title bar - and you cannot add/edit buttons on the title bar using VSTO:

Sample Image

So if you're concerned with trapping the button commands (everyone I know uses Ctrl+Z & Y), you might inject VBA code to get access to EditUndo and EditRedo events, eg:

VB._VBComponent vbModule = VBProj.VBE.ActiveVBProject.VBComponents.Add(VB.vbext_ComponentType.vbext_ct_StdModule);
String functionText = "Public Sub EditUndo() \n";
functionText += "MsgBox \"Undo Happened\"\n";
functionText += "End Sub";
vbModule.CodeModule.AddFromString(functionText);

Main problem with this approach is Trust needs to be granted.


Another answer in this same QA
Can I create an undo transaction in Word or Excel? (VSTO) is by Mike Regan who answered 3 months after Dirk. He used a hidden document and placed it in the real document when needed to make any amount of VSTO actions a single undo.

Still doesn't solve the problem of preventing an action being recorded in the Undo History.


I did try a Registry key to limit the UndoHistory to 0 and reset it back to 100 (in order to disable the History while adding an action), but it appears its only for Excel
https://support.microsoft.com/en-gb/kb/211922

There maybe an undocumented reg key to disable the Undo/Redo history altogether but it would only be read by Word on startup. I thought the UndoHistory key containing a number would be read before each Undo/Redo, but no luck with this approach at all.


It's not an easy problem to solve there are big limitations, so it might be easier to:

a) Accept that your spell/grammer checker Add-In is included in the Undo/Redo list (defeat).

b) Work out where the line/text is on screen and show a transparent tooltip highlighting the problem. This is a lot harder than it seems and is less than ideal, here are two great answers to guide you on this method: Detecting text changes in Word 2016 from VSTO add-in or a much simpler approach to detect XY positions from this Microsoft email thread: https://groups.google.com/forum/#!topic/microsoft.public.word.vba.general/pKq4PsqD3cM

//position dialog relative to word insertion point (caret)
int left = 0;
int top = 0;
int width = 0;
int height = 0;
MSWord.Range r = Globals.ThisDocument.Application.Selection.Range;
MSWord.Window w = Globals.ThisDocument.ActiveWindow;

w.GetPoint(out left, out top, out width, out height, r);

frmPopUp newForm = new frmPopUp();
newForm.SetDesktopLocation( left + width + 2, top - newForm.Height + height );

c) Only trap Undo/Redo events by the Keyboard with Transactional Undo/Redo's and let users see the atomic Undo/Redo's using the buttons. It would be extremely dodgy to remove the Undo/Redo buttons and that will cause heaps of Where's my Undo button gone? support cases. So don't do this, if you're curious, quick access toolobar customization can be done from ".qat" files. Ref: https://support.microsoft.com/en-us/kb/926805

d) Use a Mouse Hook and detect when the Undo/Redo buttons are clicked. Here is the code to hook up the mouse, note this wont play nice with corporate Anti-Virus products and I dont recommend it: https://blogs.msdn.microsoft.com/andreww/2009/02/24/message-hooks-in-add-ins/ or https://github.com/gmamaladze/globalmousekeyhook.

e) Try to intercept raw windows messages, eg Excel CustomTaskPane with WebBrowser control - keyboard/focus issues just beware as per my comment referencing BUG: Cant choose dates on a DatePicker that fall outside a floating VSTO Add-In that message pumps in Office sometimes exhibit weird behaviour.



Related Topics



Leave a reply



Submit