C# 必须在WPF中从日历中单击两次

C# 必须在WPF中从日历中单击两次,c#,wpf,focus,C#,Wpf,Focus,编辑2:谢谢大家的反馈。我通过将此添加到SelectedDatesChanged事件中解决了此问题: Mouse.Capture(空) 当我在日历中选择日期时,我想单击我的“开始”按钮。但是,我需要单击“Go”按钮两次:一次取消日历的焦点,另一次实际按下它。如果在日历中选择了某个项目,则不会在日历上触发鼠标离开事件,并且Keyboard.ClearFocus()也不会解除该项目的焦点 请问,每当我选择一个日期时,我如何摆脱日历的焦点? 谢谢大家! 编辑:点击“下一步”按钮只是一个例子;如果我想选

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

Mouse.Capture(空)

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

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


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

我在几周前也遇到过同样的问题,您可以使用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>    

希望这有帮助

结果:


因此,
日历
似乎只捕获鼠标,一个选项是在用户单击时制作一个
附件属性
来释放捕获

例如:

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
应用于您的
日历
,一旦选中某个项目,它将散焦

完整示例:

Xaml:


代码:

使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Input;
命名空间WpfApplication2
{
/// 
///MainWindow.xaml的交互逻辑
/// 
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
}
}
公共静态类CalandarHelper
{
公共静态只读从属属性SingleClickDisfocus属性=
DependencyProperty.RegisterAttached(“SingleClickDefocus”)、typeof(bool)、typeof(Calendar)
,新的FrameworkPropertyMetadata(false,新的PropertyChangedCallback(SingleClickDisfocusChanged));
公共静态bool GetSingleClick离焦(DependencyObject obj){
返回(bool)对象获取值(SingleClickDisfocusProperty);
}
公共静态无效设置SingleClick离焦(DependencyObject obj,布尔值){
对象设置值(SingleClickDisfocus属性,值);
}
私有静态void SingleClickDisfocusChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
如果(d是日历)
{
日历=d作为日历;
calendar.PreviewMouseDown+=(a、b)=>
{
if(Mouse.Captured为日历| | Mouse.Captured为System.Windows.Controls.Primitives.CalendarItem)
{
鼠标捕捉(空);
}
};
}
}
}
}

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

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(在本例中为button)来调用其MouseDown事件。我必须将它添加到面板中(设置可见性不起作用),否则事件不允许调用自身。现在,当您单击日历时,它会调用tempButton。单击,它会释放捕获,当您按下按钮“GO”时,它不需要单击两次。我知道这是一个肮脏的出路,但工作

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

我通过将此添加到我的SelectedDatesChanged事件中解决了问题:


Mouse.Capture(空)

如果选择相同的日期,则不会出现
已选择日期更改的问题,您将看到需要单击两次的相同问题

理想情况下,您应该钩住
GotMouseCapture
事件,并从原始发件人释放鼠标捕获,以避免通过日历控件捕获任何鼠标

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

注意-您也可以使用另一个答案中提到的附加属性在行为中提取该属性。

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

因此,我使用了
GotMouseCapture
,仅当单击的ui元素是日历日时才释放鼠标焦点。它修复了焦点问题,不会破坏控件的其余部分

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

代码:

XAML:


资料来源:


简单而且似乎没有缺点:

您希望在日历中选择日期后立即通过Go按钮捕获鼠标吗?您是否尝试过将
日历
按钮
组合在同一个
聚焦镜
中?如果您使用MVVM模式,这是一个非常好的选择。非常感谢。这会破坏月份导航按钮,并且在选择相同日期时不起作用。请看我的答案。这会破坏月份导航按钮,并且不会
private void calendar_GotMouseCapture(object sender, MouseEventArgs e)
{
    UIElement originalElement = e.OriginalSource as UIElement;
    if (originalElement != null)
    {
        originalElement.ReleaseMouseCapture();
    }
}
private void Calendar_GotMouseCapture(object sender, MouseEventArgs e)
{
    UIElement originalElement = e.OriginalSource as UIElement;
    if (originalElement is CalendarDayButton || originalElement is CalendarItem)
    {
        originalElement.ReleaseMouseCapture();
    }
}
public void ReleaseMouse()
{
    if (Mouse.Captured is CalendarItem) Mouse.Capture(null);
}
<Calendar PreviewMouseUp="ReleaseMouse" />