必须在WPF中的Calendar中单击两次

时间:2014-08-17 19:40:59

标签: c# wpf focus

编辑2:谢谢大家的反馈。我通过将其添加到我的SelectedDatesChanged事件来解决了这个问题:

Mouse.Capture(null);

当我在日历中选择日期时,我想点击“开始”按钮。但是,我需要点击两次“开始”按钮:一次取消对焦日历,再次实际按下它。如果在其中选择了一个项目,则鼠标离开事件不会在日历上触发,并且Keyboard.ClearFocus()也不会将其解除焦点。

请问,每当我选择日期时,如何摆脱日历的焦点? 谢谢!

编辑:接下来单击“开始”按钮仅仅是一个例子;如果我想选择一个文本框而我刚刚选择了一个日期,我还需要点击两次才能进入文本框。主要问题是,一旦日历与之交互,必须在与任何其他元素交互之前单击一次。

7 个答案:

答案 0 :(得分:16)

我通过将其添加到我的SelectedDatesChanged事件来解决问题:

Mouse.Capture(null);

答案 1 :(得分:9)

如果您选择相同的日期,则 SelectedDatesChanged 不会被提出,您将看到需要点击两次的相同问题。

理想情况下,您应该挂钩 GotMouseCapture 事件并 从原始发件人发布鼠标捕获 ,以避免按日历捕获任何鼠标控制。

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

注意 - 您可以通过使用其他答案中提到的附加属性来提取此行为。

答案 2 :(得分:8)

释放SelectedDatesChangedGotMouseCapture中的所有鼠标点击将打破日历控件上月份之间的导航。正如另一个答案中所指出的,SelectedDatesChanged在选择相同日期时不会触发。

所以我使用GotMouseCapture并且仅在点击的UIElement是日历日时才释放鼠标焦点。它解决了焦点问题,并没有打破其余的控制。

private void Calendar_GotMouseCapture(object sender, MouseEventArgs e)
{
    UIElement originalElement = e.OriginalSource as UIElement;
    if (originalElement is CalendarDayButton || originalElement is CalendarItem)
    {
        originalElement.ReleaseMouseCapture();
    }
}

答案 3 :(得分:3)

所以似乎Calendar专门捕获鼠标,一个选项可能是让AttachedProperty在用户点击时释放捕获

示例:

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

现在,您可以将此AttachedProperty应用于Calender,并且一旦选择了某个项目,它就会散焦。

完整示例:

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

代码:

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

答案 4 :(得分:1)

我在几周前遇到过同样的问题,你可以使用DatePicker,它是一个包含日历的控件,一旦用户点击一个按钮就会显示日历,当你选择一个自动关闭的日期时,DatePicker还包含一个文本框当日期可见时,如果需要,可以将其设为ReadOnly:这里是使用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>    

希望这有帮助。

结果:

result

答案 5 :(得分:1)

找到了

代码:

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

XAML:

<Calendar PreviewMouseUp="ReleaseMouse" />

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

简单,似乎没有缺点:

答案 6 :(得分:0)

我处理了SelectedDatesChanged事件,我正在显示MouseButtonEventArgs的属性OriginalSource。当您选择日期时,它会显示路径,矩形等,但是当您选择实例按钮或日历以外的任何内容时,它会精确显示System.Windows.Controls.Primitives.CalendarItem。显然,日历需要再单击一次才能将鼠标转移到另一个UIelement。我的想法是在第一次点击日历后调用事件,以便它可以立即丢失捕获。

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

我创建了UIelement(在本例中为按钮)来调用其MouseDown事件。我不得不将它添加到Panel(设置可见性不起作用),否则事件不允许调用自身。现在,当您单击日历时,它会调用tempButton.Click,它会丢失捕获,当您按下按钮“GO”时,它不需要单击两次。我知道这是一个肮脏的出路,但工作。

  <StackPanel>
        <Calendar local:CalendarM.RemoveProperty="True"/>
        <Button Content="Go" Click="but_Click"/>
        <TextBox Text="Text"/>
    </StackPanel>
</StackPanel>