BUG:无法在DatePicker上选择不在浮动VSTO加载项之外的日期

Jer*_*son 10 .net c# excel vsto datetimepicker

在这里记录了微软的问题 - 可以下载Repro:https://connect.microsoft.com/VisualStudio/feedback/details/741454/value-change-event-doesnt-fire-for-datetimepicker-controls-used-在-VSTO-插件

如果将DateTimePicker放在Excel VSTO浮动加载项中并在日历下拉时将其定位,则它位于加载项的边缘之外,请参见此处:

在此输入图像描述

选择绿色中圈出的任何日期按预期工作,但点击任何以红色圈出的日期时,它只会关闭日历下拉菜单并且不会设置日期!

有谁知道我怎么解决这个问题?

编辑

这个SO用户使用WPF遇到了这个问题: VSTO WPF ContextMenu.MenuItem在没有引发的TaskPane外部单击

在此输入图像描述

该问题的答案显示该问题已被报道连接一段时间但仍然没有VSTO 4.0 SP1的解决方案:https://connect.microsoft.com/VisualStudio/feedback/details/432998/excel-2007-vsto-custom-任务窗格与- WPF的上下文菜单具有焦点,问题

其中一个解决方法是使用DispatcherFrame来为消息添加消息并为菜单订阅GotFocusEvent和LostFocusEvent.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但这是菜单的所有WPF代码,而不是Winform DateTimePicker的解决方案.

Repro for Microsoft Connect:

使用TaskPane的新项目> Excel 2010加载项; 使用Microsoft.Office.Core;

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

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;
}
}
Run Code Online (Sandbox Code Playgroud)

文件菜单>添加>新建项目>类库>命名为TaskPane

然后在TaskPane项目中创建一个名为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; }
    }  

}
Run Code Online (Sandbox Code Playgroud)

接下来,使用DateTimePicker创建用户控件,确保Calendar控件位于用户控件的右下角

public partial class TaskPaneCtrl : UserControl
{
public TaskPaneCtrl()
{
    InitializeComponent();
}
}
Run Code Online (Sandbox Code Playgroud)

在TaskPane类库中引用Excel Interop(例如c:\ Program Files x86\Microsoft Visual Studio 14.0\Visual Studio Tools for Office\PIA\Office14\Microsoft.Office.Interop.Excel.dll).

构建解决方案.评论不起作用的部分.构建解决方案

现在将TaskPaneCtrl拖放到TaskPaneView上并取消注释无法编译的内容.

在此输入图像描述

F5并单击日历控件,现在尝试选择任务窗格区域之外的日期.没有Value Change事件触发,它表现得像日历外的点击!

注意:我尝试了一个不受控制的下拉列表,但它的事件发生了火灾!

Han*_*ant 7

"浮动"是这里问题的关键.从来没有问题的是,您依靠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.Click += showMonthCalendar;
        dateTimePicker1.Resize += delegate {
            btn.Width = btn.Height = dateTimePicker1.ClientSize.Height;
        };
    }
Run Code Online (Sandbox Code Playgroud)

Click事件处理程序需要显示包含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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

使用CalendarForm,您可以添加无边界的表单,并且只包含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;
    }
}
Run Code Online (Sandbox Code Playgroud)