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

前端 未结 2 1319
清酒与你
清酒与你 2020-11-28 14:34

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-fi

相关标签:
2条回答
  • 2020-11-28 15:12

    "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;
        }
    }
    
    0 讨论(0)
  • 2020-11-28 15:22

    speculation:
    The bug occurs because the datetimepicker's surface is not rendered until after the click message is sent.

    Next steps to Answer:
    If available try testing with some 3rd party date time picker controls. I realise this isn't a complete answer since I don't know yet whether it will fix your problem.

    Other possible answer: Resize the taskpane to fit the control. This will sort out the bug, but look a bit weird from a user point of view.

    0 讨论(0)
提交回复
热议问题