BUG:无法选择DatePicker上浮动VSTO外接程序之外的日期 [英] BUG: Can't choose dates on a DatePicker that fall outside a floating VSTO Add-In

查看:108
本文介绍了BUG:无法选择DatePicker上浮动VSTO外接程序之外的日期的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在此处记录了Microsoft的问题-可以下载Repro:





对该问题的回答表明,该问题已报告了一段时间,但仍然没有VSTO 4.0 SP1的解决方案:



F5,然后单击日历控件,现在尝试选择任务窗格区域之外的日期。 不会触发任何Value Change事件,其行为就像在日历外单击一样!



注意:我尝试了一个下拉列表,但该列表没有控制权,但

解决方案

浮动是问题的关键。永远不会出现问题(有时会引起奇怪的事情)是依靠Excel中的消息泵来分发Windows消息,使这些控件响应输入的消息。在WPF中,这和Winforms一样出错,它们具有自己的分派循环,可以在将消息传递到窗口之前对其进行过滤。当不使用各自的调度程序时,会出错的关键问题是诸如制表键和快捷键之类的东西。



然后,这种问题可能是由以下原因引起的: Excel在分派消息之前先做自己的过滤。我猜想这是一种反恶意软件功能,微软永远担心程序会与Office应用程序发生混乱。



Winforms解决方案与WPF解决方案相同,您可以需要抽出自己的消息循环。这需要进行一些手术,DateTimePicker不会配合,因为它不允许取消其DropDown事件,并且已经显示日历 后引发了它。解决方法是愚蠢但有效的,在表单中添加一个按钮,使其看起来像DTP上的下拉箭头,并使它与箭头重叠,以便单击它而不是箭头。



一些使按钮与下拉箭头重叠的示例代码:

  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。点击+ = showMonthCalendar;
dateTimePicker1.Resize + =委托{
btn.Width = btn.Height = dateTimePicker1.ClientSize.Height;
};
}

Click事件处理程序需要显示一个包含MonthCalendar的对话框:

  private void showMonthCalendar(object sender,EventArgs e){
dateTimePicker1.Focus();
使用(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);
}
}

使用CalendarForm,您添加的表格是无边界的,仅包含一个MonthCalendar:

 公共部分类CalendarForm:表单{
公共事件DateRangeEventHandler DateSelected;

public CalendarForm(){
InitializeComponent();
this.StartPosition = FormStartPosition.Manual;
monthCalendar1.Resize + =委托{
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;
}
}


I logged the issue with Microsoft here - the Repro is available for download: https://connect.microsoft.com/VisualStudio/feedback/details/741454/value-change-event-doesnt-fire-for-datetimepicker-controls-used-in-vsto-add-ins

If you put a DateTimePicker in a Excel VSTO floating Add-In and position it so when the calendar drops down, it is outside the edge of the add-in, see here:

Choosing any of the dates circled in the green works as expected, but when clicking any dates circled in red, it just closes the calendar drop down and doesn't set the date!

Does anyone know how I can fix this?

Edit

This SO user has experienced the problem using WPF: VSTO WPF ContextMenu.MenuItem Click outside a TaskPane not raised

The answer to that question shows the issue was reported to connect a while back but still no solution with VSTO 4.0 SP1: https://connect.microsoft.com/VisualStudio/feedback/details/432998/excel-2007-vsto-custom-task-pane-with-wpf-context-menu-has-focus-problems

One of the workarounds is to use the DispatcherFrame to pump messages and subscribe to GotFocusEvent and LostFocusEvent for the menu. http://blogs.msdn.com/b/vsod/archive/2009/12/16/excel-2007-wpf-events-are-not-fired-for-items-that-overlap-excel-ui-for-wpf-context-menus.aspx but this is all WPF code for menu's not a solution for Winform DateTimePicker.

Repro for Microsoft Connect:

New Project > Excel 2010 Add-In

using TaskPane;
using Microsoft.Office.Core;

namespace ExcelAddIn2
{
public partial class ThisAddIn
{
    TaskPaneView MyTaskView = null;
    Microsoft.Office.Tools.CustomTaskPane MyTaskPane = null;

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    //setup custom taskpane
    MyTaskView = new TaskPaneView();
    MyTaskView.currentInstance = Globals.ThisAddIn.Application;
    MyTaskPane = this.CustomTaskPanes.Add(MyTaskView, "MyTaskView");
    MyTaskPane.DockPosition = Microsoft.Office.Core.MsoCTPDockPosition.msoCTPDockPositionFloating;
    MyTaskPane.DockPositionRestrict = MsoCTPDockPositionRestrict.msoCTPDockPositionRestrictNoChange;
    MyTaskPane.Visible = true;
}
}

File Menu > Add > New Project > Class Library > named TaskPane

Then in the TaskPane project create a User Control called TaskPaneView

public partial class TaskPaneView : UserControl
{
    public TaskPaneView()
    {
        InitializeComponent();
    }

    public Microsoft.Office.Interop.Excel.Application currentInstance { get; set; }

    public TaskPaneCtrl getTaskPaneCtrl
    {
        get { return this.taskPaneCtrl1; }
    }  

}

Next create a User Control with a DateTimePicker, make sure the Calendar control is located toward the bottom right of the user control

public partial class TaskPaneCtrl : UserControl
{
public TaskPaneCtrl()
{
    InitializeComponent();
}
}

In the TaskPane class library Reference the Excel Interop (eg c:\Program Files x86\Microsoft Visual Studio 14.0\Visual Studio Tools for Office\PIA\Office14\Microsoft.Office.Interop.Excel.dll).

Build the solution. Commenting out parts that dont work. Build Solution.

Now drag and drop the TaskPaneCtrl onto the TaskPaneView and uncomment thing that failed to compile.

F5 and click the Calendar Control, now try to select a date that is outside the taskpane area. No Value Change event fires, it behaves like a click outside the calendar!

Note: I tried a drop downlist that falls off the control but its events DO FIRE!

解决方案

"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;
    }
}

这篇关于BUG:无法选择DatePicker上浮动VSTO外接程序之外的日期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆