Have to click away twice from Calendar in WPF

后端 未结 7 1713
南旧
南旧 2021-02-01 19:17

Edit 2: Thank you all for your feedback. I solved the problem by adding this to my SelectedDatesChanged event:

Mouse.Capture(null);

When I select a

相关标签:
7条回答
  • 2021-02-01 19:34

    I solved the problem by adding this to my SelectedDatesChanged event:

    Mouse.Capture(null);

    0 讨论(0)
  • 2021-02-01 19:37

    I handled SelectedDatesChanged event and I was displaying property OriginalSource of MouseButtonEventArgs. When you pick a date it displays Path, Rectangle and so on but when you pick for instance button or whatever beyond calendar it displays precisely System.Windows.Controls.Primitives.CalendarItem. Apparently calendar needs one more click in order to transfer mouse to another UIelement. My idea was to invoke event after first click on calendar so that it could lost capture right away.

    public static class CalendarM
    {
        private static Button tempButton;
        public static bool GetRemoveProperty(DependencyObject obj)
        {
            return (bool)obj.GetValue(RemoveFocusProperty);
        }
    
        public static void SetRemoveProperty(DependencyObject obj, bool value)
        {
            obj.SetValue(RemoveFocusProperty, value);
        }
        public static readonly DependencyProperty RemoveFocusProperty = DependencyProperty.RegisterAttached("RemoveFocus", typeof(bool), typeof(CalendarM),
            new FrameworkPropertyMetadata(new PropertyChangedCallback((x, y) =>
            {
                if (x is Calendar && GetRemoveProperty((DependencyObject)x))
                {
                    tempButton = new Button() { Width = 0, Height = 0 };
                    ((System.Windows.Controls.Panel)((FrameworkElement)x).Parent).Children.Add(tempButton);
                    tempButton.Click += (s1, s2) =>
                    {
                    };
                    ((Calendar)x).SelectedDatesChanged += CalendarM_SelectedDatesChanged;
                }
            })));
        static void CalendarM_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
        {
            tempButton.RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left) { RoutedEvent = Button.MouseDownEvent });
        }
    }
    

    I created UIelement(in this case button) to invoke its MouseDown event. I had to add it to Panel(setting visibility did not work), otherwise event does not allow to invoke itself. Now when you click calendar it invokes tempButton.Click, it looses capture and when you press your Button "GO" it does not require clicking two times. I know it is a dirty way out, but working.

      <StackPanel>
            <Calendar local:CalendarM.RemoveProperty="True"/>
            <Button Content="Go" Click="but_Click"/>
            <TextBox Text="Text"/>
        </StackPanel>
    </StackPanel>
    
    0 讨论(0)
  • 2021-02-01 19:51

    If you select the same date then SelectedDatesChanged won't be raised and you will see the same issue where you need to click twice.

    Ideally you should hook to GotMouseCapture event and release the mouse capture from original sender to avoid any mouse captures by calendar control.

    private void calendar_GotMouseCapture(object sender, MouseEventArgs e)
    {
        UIElement originalElement = e.OriginalSource as UIElement;
        if (originalElement != null)
        {
            originalElement.ReleaseMouseCapture();
        }
    }
    

    Note - You can extract out this in behavior as well by using attached property like mentioned in another answer.

    0 讨论(0)
  • 2021-02-01 19:51

    So it seems the Calendar captures the Mouse exclusively, One option could be to make a AttachedProperty to release the capture when the user clicks

    Example:

    public static class CalandarHelper 
    {
        public static readonly DependencyProperty SingleClickDefocusProperty =
            DependencyProperty.RegisterAttached("SingleClickDefocus", typeof(bool), typeof(Calendar)
            , new FrameworkPropertyMetadata(false, new PropertyChangedCallback(SingleClickDefocusChanged)));
    
        public static bool GetSingleClickDefocus(DependencyObject obj) {
            return (bool)obj.GetValue(SingleClickDefocusProperty);
        }
    
        public static void SetSingleClickDefocus(DependencyObject obj, bool value) {
            obj.SetValue(SingleClickDefocusProperty, value);
        }
    
        private static void SingleClickDefocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is Calendar) 
            {
                Calendar calendar = d as Calendar;
                calendar.PreviewMouseDown += (a, b) =>
                {
                    if (Mouse.Captured is Calendar || Mouse.Captured is System.Windows.Controls.Primitives.CalendarItem)
                    {
                        Mouse.Capture(null);
                    }
                };
            }
        }
    }
    

    Now you can apply this AttachedProperty to your Calender and it will defocus once an item is selected.

    Full Example:

    Xaml:

    <Window x:Class="WpfApplication2.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:helpers="clr-namespace:WpfApplication2"
            Title="MainWindow" Width="300" >
    
        <StackPanel>
            <Calendar helpers:CalandarHelper.SingleClickDefocus="True" />
            <TextBox />
        </StackPanel>
    </Window>
    

    Code:

    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    
    namespace WpfApplication2 
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window 
        {
            public MainWindow() 
            {
                InitializeComponent();
            }
        }
    
        public static class CalandarHelper 
        {
            public static readonly DependencyProperty SingleClickDefocusProperty =
                DependencyProperty.RegisterAttached("SingleClickDefocus", typeof(bool), typeof(Calendar)
                , new FrameworkPropertyMetadata(false, new PropertyChangedCallback(SingleClickDefocusChanged)));
    
            public static bool GetSingleClickDefocus(DependencyObject obj) {
                return (bool)obj.GetValue(SingleClickDefocusProperty);
            }
    
            public static void SetSingleClickDefocus(DependencyObject obj, bool value) {
                obj.SetValue(SingleClickDefocusProperty, value);
            }
    
            private static void SingleClickDefocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                if (d is Calendar) 
                {
                    Calendar calendar = d as Calendar;
                    calendar.PreviewMouseDown += (a, b) =>
                    {
                        if (Mouse.Captured is Calendar || Mouse.Captured is System.Windows.Controls.Primitives.CalendarItem)
                        {
                            Mouse.Capture(null);
                        }
                    };
                }
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-01 19:52

    I had the same issue weeks ago, you can use DatePicker which is a control that contains a Calendar, the calendar is displayed once the user clicks a button and when you select a date its automatically closed, the DatePicker Contains also a textbox when the date is visible, you can make it ReadOnly if you want: here a sample code for using DatePicker:

    <DatePicker Name="TestDatePicker" Width="120" Height="25" >
            <DatePicker.Resources>
                <Style TargetType="DatePickerTextBox">
                    <Setter Property="IsReadOnly" Value="True"></Setter>
                    <Setter Property="Text" Value="Select a Date"></Setter>
                </Style>
            </DatePicker.Resources>
    </DatePicker>    
    

    Hope this helps.

    result :

    result

    0 讨论(0)
  • 2021-02-01 19:52

    Found this

    Code:

    public void ReleaseMouse()
    {
        if (Mouse.Captured is CalendarItem) Mouse.Capture(null);
    }
    

    XAML:

    <Calendar PreviewMouseUp="ReleaseMouse" />
    

    Source: https://quick-geek.github.io/answers/819560/index.html

    Easy and seems to have no drawbacks:

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