Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Have to click away twice from Calendar in WPF

Tags:

c#

focus

wpf

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 date in my calendar, I want to click my "Go" button. However, I need to click the "Go" button twice: once to de-focus the calendar, and again to actually press it. The mouse leave event does not trigger on the calendar if an item is selected inside of it, and Keyboard.ClearFocus() does not de-focus it either.

Please, how can I get rid of the calendar's focus whenever I select a date? Thank you!

Edit: Clicking the "Go" button next was merely an example; if I want to select a textbox and I have just selected a date, I would also have to click twice to enter the textbox. The main issue is that, once the calendar is interacted with, it must be clicked out of once before interacting with any other elements.

like image 306
user3076399 Avatar asked Aug 17 '14 19:08

user3076399


7 Answers

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

Mouse.Capture(null);

like image 120
user3076399 Avatar answered Oct 02 '22 15:10

user3076399


Releasing all mouse clicks in SelectedDatesChanged or GotMouseCapture will break the navigation between months on the calendar control. As pointed out in another answer, SelectedDatesChanged does not fire when the same date is selected.

So I used GotMouseCapture and only released the mouse focus when the clicked UIElement was a calendar day. It fixes the focus issue and doesn't break the rest of the control.

private void Calendar_GotMouseCapture(object sender, MouseEventArgs e)
{
    UIElement originalElement = e.OriginalSource as UIElement;
    if (originalElement is CalendarDayButton || originalElement is CalendarItem)
    {
        originalElement.ReleaseMouseCapture();
    }
}
like image 40
Millie Smith Avatar answered Oct 02 '22 15:10

Millie Smith


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.

like image 28
Rohit Vats Avatar answered Oct 02 '22 16:10

Rohit Vats


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);
                    }
                };
            }
        }
    }
}
like image 25
sa_ddam213 Avatar answered Oct 02 '22 16:10

sa_ddam213


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

like image 24
Redaa Avatar answered Oct 02 '22 16:10

Redaa


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:

like image 38
Franz Avatar answered Oct 02 '22 17:10

Franz


Other answers had always some glitches with changing months and days. The only thing working for me I've found here

private void Calendar_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
    if (Mouse.Captured is CalendarItem)
    {
        Mouse.Capture(null);
    }
}
like image 45
Apfelkuacha Avatar answered Oct 02 '22 15:10

Apfelkuacha