Click-to-Edit控制LostFocus事件问题

我正在开发一个简单的自定义控件,它应该通过双击进入编辑模式

该概念基于Silverlight中的点击编辑问题

双击它会更改编辑模板上的初始模板,它似乎很清楚,除了部分(5)如何更改模板返回当控件失去焦点时失去焦点事件仅在包含的控件失去焦点时触发这是一篇关于它的文章http://programmerpayback.com/2008/11/20/gotfocus-and-lostfocus-events-on-containers/

我试图实现相同的Technic但仍然没有结果,当我点击控件外部时,我无法让LostFocus事件为我工作

我的问题在哪里?

我的XAML

                                         <!---->         

代码背后

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; namespace Splan_RiaBusinessApplication.Controls { public class TimeCode { public int TimeCodeId {get;set;} public string Code { get; set; } public string Description { get; set; } } public class TimeDetail { public int TimeDetailId { get;set; } public int CodeId { get;set;} public TimeSpan Start { get; set; } public TimeSpan End { get; set; } public string Code { get; set; } public string Comment { get; set; } } public partial class TimeCodeControl : ContentControl { public class TimeCodeControlEventArgs : EventArgs { public string userName { get; set; } } private static TimeSpan DoubleClickThreshold = TimeSpan.FromMilliseconds(300); private DateTime _lastClick; private Boolean m_EditMode = false; public Boolean EditMode { get { return m_EditMode; } set { if (m_EditMode != value) { switch (value) { case false: PlaceHolder.Template = this.Resources["DisplayTemplate"] as ControlTemplate; break; case true: PlaceHolder.Template = this.Resources["EditTemplate"] as ControlTemplate; break; } m_EditMode = value; } } } public bool IsFocused { get { return FocusManager.GetFocusedElement() == this; } } public TimeCodeControl() { TimeCodes = new List() { new TimeCode { TimeCodeId = 200, Code= "C", Description="Cash" } }; InitializeComponent(); Layout.DataContext = this; this.IsTabStop = true; this.Visibility = Visibility.Visible; this.IsEnabled = true; this.Focus(); Layout.MouseLeftButtonDown += Layout_MouseLeftButtonDown; //Layout.KeyDown += Layout_KeyDown; //Layout.KeyUp += Layout_KeyUp; this.LostFocus += TimeCodeControl_LostFocus; this.GotFocus += TimeCodeControl_GotFocus; } void TimeCodeControl_GotFocus(object sender, RoutedEventArgs e) { } void TimeCodeControl_LostFocus(object sender, RoutedEventArgs e) { } public TimeDetail Source { get { return (TimeDetail)GetValue(SourceProperty); } set { SetValue(SourceProperty, value); } } public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(TimeDetail), typeof(TimeCodeControl), new PropertyMetadata(SourceChanged)); private static void SourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var control = sender as TimeCodeControl; if (control == null) return; control.Target = control.Source; //.Copy(); } public List TimeCodes { get; set; } public TimeDetail Target { get; set; } private bool FocusIsInside(object parent) { bool rs = false; dynamic oFocus = FocusManager.GetFocusedElement(); while (oFocus != null) try { if ((oFocus.GetType() == parent.GetType()) && (oFocus.Equals(this))) { rs = true; break; } else { oFocus = oFocus.Parent; } } catch { break; } return rs; } private Boolean hasFocus = false; protected override void OnGotFocus(RoutedEventArgs e) { base.OnGotFocus(e); if (!hasFocus) { hasFocus = true; Debug.WriteLine("Container Got Focus"); } } protected override void OnLostFocus(RoutedEventArgs e) { base.OnLostFocus(e); //if (!FocusIsInside(Layout)) //{ // hasFocus = false; // Debug.WriteLine("Container Lost Focus"); // EditMode = false; //} } void Layout_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (DateTime.Now - this._lastClick <= DoubleClickThreshold) { EditMode = true; this._lastClick = DateTime.Now; e.Handled = true; return; } this._lastClick = DateTime.Now; } } } 

更新:我决定利用计时器来识别用户从容器外部引出焦点或仅将焦点从一个控件切换到另一个控件内部的情况。 它可能不是最好的解决方案,但它似乎现在正在起作用。 我将不胜感激任何有关不同方法或实施的建议或建议。

 public partial class MyControl: ContentControl { ... public event EventHandler LostFocus; public event EventHandler GotFocus; bool Focused = false; DispatcherTimer FocusTimer = null; protected override void OnGotFocus(RoutedEventArgs e) { base.OnGotFocus(e); if (Focused) return; Focused = true; // it focused from the outside of the control // becouse the timer wasn't initialised on the previous LostFocused event // generated by other control in the same ContentControl contaner if (FocusTimer == null) { if (GotFocus != null) GotFocus(e.OriginalSource, e); Debug.WriteLine("Got Focus "); return; } // It was switched from one hosted control to another one FocusTimer.Stop(); FocusTimer = null; } protected override void OnLostFocus(RoutedEventArgs e) { base.OnLostFocus(e); if (e.OriginalSource is ComboBox && FocusManager.GetFocusedElement() is ComboBoxItem) return; FocusTimer = new DispatcherTimer(); Focused = false; FocusTimer.Interval = new TimeSpan(0, 0, 0, 0, 50); FocusTimer.Tick += (s, args) => { FocusTimer.Stop(); FocusTimer = null; // if after the timeout the focus still not triggered // by another contained element // the We lost a focus on the container if (!Focused ) { if(LostFocus != null) LostFocus(e.OriginalSource, e); Debug.WriteLine("Lost Focus " ); } }; FocusTimer.Start(); } ... } 

有几个问题。 让我们来看看…

当您在控件外部单击时,为什么没有获得LostFocus事件?

好吧,前段时间我也成了这个错误假设的受害者。 除非您单击在单击时显式将焦点设置为自身的控件(如TextBox,或各种按钮),否则单击外部不会更改焦点。 按Tab键将键盘焦点导航到下一个控件,并查看是否引发了事件。

但是,我们来谈谈其他问题:

ControlTemplate x:Key="DisplayTemplate"ControlTemplate x:Key="EditTemplate"

建议不要使用ControlTemplates 。 而是使用DataTemplate和相应的ContentPresenters

TimeCodeControl : ContentControlx:Class="Splan_RiaBusinessApplication.Controls.TimeCodeControl"

是的我知道这是可能的,但并不是真的有用。 让我解释一下:您可以将自己专门的Click-To-Edit控件编写为一次性工具:使用硬编码的DisplayTemplate和EditTemplate来编辑TimeCodeTimeDetail数据(基本上就是您所做的)。 但是,您没有机会使用它并指定另一对模板以允许编辑其他数据类型。 所以从ContentControl派生出来没有多大意义,你也可以从UserControl派生出来。

另一种方法是:将Click-To-Edit控件编写为通用和可重用控件,它提供两个公共属性:DisplayTemplate和EditTemplate。 并且不要对DataContext做任何假设。 再次将ContentControl作为父类没有任何好处。 我建议您从Control派生,添加两个类型为DataTemplate DependencyProperties ,如前所述,定义一个内置一个或两个ContentPresenters的默认ControlTemplate。 在您的控制代码中,您需要处理MouseLeftButtonDown和LostFocus并相应地更新布尔标志。

这是一个工作示例:

…确定焦点的扩展方法:

 public static class ControlExtensions { public static bool IsFocused( this UIElement control ) { DependencyObject parent; for (DependencyObject potentialSubControl = FocusManager.GetFocusedElement() as DependencyObject; potentialSubControl != null; potentialSubControl = parent) { if (object.ReferenceEquals( potentialSubControl, control )) { return true; } parent = VisualTreeHelper.GetParent( potentialSubControl ); if (parent == null) { FrameworkElement element = potentialSubControl as FrameworkElement; if (element != null) { parent = element.Parent; } } } return false; } } 

…还有一个不错的自定义控件:

 public class ClickToEditControl : Control { public ClickToEditControl() { DefaultStyleKey = typeof (ClickToEditControl); MouseLeftButtonDown += OnMouseLeftButtonDown; } private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (e.ClickCount==2) { GotoEditMode(); e.Handled = true; } } protected override void OnLostFocus(RoutedEventArgs e) { base.OnLostFocus(e); if (!this.IsFocused()) GotoDisplayMode(); } private void GotoDisplayMode() { IsInEditMode = false; } private void GotoEditMode() { IsInEditMode = true; } public DataTemplate EditTemplate { get { return (DataTemplate) GetValue( EditTemplateProperty ); } set { SetValue( EditTemplateProperty, value ); } } public static readonly DependencyProperty EditTemplateProperty = DependencyProperty.Register( "EditTemplate", typeof( DataTemplate ), typeof( ClickToEditControl ), null ); public DataTemplate DisplayTemplate { get { return (DataTemplate) GetValue( DisplayTemplateProperty ); } set { SetValue( DisplayTemplateProperty, value ); } } public static readonly DependencyProperty DisplayTemplateProperty = DependencyProperty.Register( "DisplayTemplate", typeof( DataTemplate ), typeof( ClickToEditControl ), null ); public bool IsInEditMode { get { return (bool) GetValue( IsInEditModeProperty ); } set { SetValue( IsInEditModeProperty, value ); } } public static readonly DependencyProperty IsInEditModeProperty = DependencyProperty.Register( "IsInEditMode", typeof( bool ), typeof( ClickToEditControl ), null ); } 

…和ControlTemplate:

    

BoolToVisibilityConverter

 public class BoolToVisibilityConverter : IValueConverter { public bool VisibleIfTrue { get; set; } public BoolToVisibilityConverter(){VisibleIfTrue = true;} public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (VisibleIfTrue) return ((bool) value) ? Visibility.Visible : Visibility.Collapsed; else return ((bool) value) ? Visibility.Collapsed : Visibility.Visible; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture){throw new NotSupportedException();} } 

用法: