点击到编辑控件LostFocus事件问题 [英] Click-to-Edit Control LostFocus event issue

查看:149
本文介绍了点击到编辑控件LostFocus事件问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我工作的一个简单的自定义控件应该去通过双击它来编辑模式



这个概念是基于对这个问题的点击后打开编辑在Silverlight



在一个双击它改变对编辑模板
初始模板,这似乎是很清楚,除了部分(5)如何更改模板背的时候,控制失去重心
失落的焦点事件只发射当包含控件失去焦点
这里是谈论它的文章
http://programmerpayback.com/2008/11/20/gotfocus-and-lostfocus-events-on-containers/



我试图实现同样的工艺,但仍没有结果,我不能当我点击一个控制范围之外获得LostFocus事件为我工作



哪里是我的问题?



我的XAML



<级=前片段 - 代码HTML语言,HTML prettyprint-覆盖> < ContentControl中X:类=Splan_RiaBusinessApplication。 Controls.TimeCodeControl的xmlns =http://schemas.microsoft.com/winfx/2006/xaml/presentation的xmlns:X =http://schemas.microsoft.com/winfx/2006/xaml的xmlns:D = http://schemas.microsoft.com/expression/blend/2008的xmlns:MC =http://schemas.openxmlformats.org/markup-compatibility/2006的xmlns:OBJ =CLR的命名空间:System.Windows .Controls;装配= System.Windows.Controls.Data的xmlns:I =CLR的命名空间:System.Windows.Interactivity;装配= System.Windows.Interactivity的xmlns:行为=CLR的命名空间:Splan_RiaBusinessApplication.Behavior的xmlns :控制=CLR的命名空间:Splan_RiaBusinessApplication.Controls的xmlns:原生=CLR的命名空间:System.Windows.Controls.Primitives;装配= System.Windows.Controls.DataMC:可忽略=DIsTabStop =真IsEnabled =真能见度=可见D:DesignHeight =100D:DesignWidth =200D:HEIGHT =200D:WIDTH =200> < ContentControl.Resources> <控件模板X:键=DisplayTemplate> <网格和GT; < Grid.RowDefinitions> < RowDefinition /> < RowDefinition /> < /Grid.RowDefinitions> < TextBlock的VerticalAlignment =中心的Horizo​​ntalAlignment =中心文本={结合Target.Code,模式=双向}/> < StackPanel的Grid.Row =1方向=横向VerticalAlignment =中心的Horizo​​ntalAlignment =中心> < TextBlock的文本={结合Target.Start,模式=双向,的StringFormat = hh\\:毫米}/> &所述; TextBlock的文本=' - '/> < TextBlock的文本={结合Target.End,模式=双向,的StringFormat = hh\\:毫米}/> < / StackPanel的> < /网格和GT; < /控件模板> <控件模板X:键=EditTemplate> <网格背景=水族HEIGHT =200WIDTH =200> < Grid.RowDefinitions> < RowDefinition /> < RowDefinition /> < /Grid.RowDefinitions> <组合框宽度=100HEIGHT =25X:NAME =cbTimeCode的ItemsSource ={结合时间码}的SelectedValue ={结合Target.CodeId,模式=双向}SelectedValuePath =TimeCodeId> < ComboBox.ItemTemplate> <&DataTemplate的GT; <网格和GT; < Grid.ColumnDefinitions> < ColumnDefinition宽度=40/> < ColumnDefinition WIDTH =100/> < /Grid.ColumnDefinitions> < TextBlock的文本={结合Target.Code}/> < TextBlock的Grid.Column =1文本={结合Target.Description}/> < /网格和GT; < / DataTemplate中> < /ComboBox.ItemTemplate> < I:Interaction.Triggers> < I:&的EventTrigger GT; <行为:ResolveElementName属性名=的ItemsSource/> < /我:&的EventTrigger GT; < /我:Interaction.Triggers> < /组合框> !< - <控制:TimeRangePickerControl Grid.Row =1开始时间={结合Target.Start,模式=双向}结束时间={结合Target.End,模式=双向}/> - > < /网格和GT; < /控件模板> < /ContentControl.Resources> <电网X:NAME =布局的背景=海蓝宝石> < ItemsControl的X:名称=占位符模板={StaticResource的DisplayTemplate}> < / ItemsControl的> < /网格>< / ContentControl中>



代码背后



<预类= 段代码 - HTML语言,HTML prettyprint-覆盖> 使用系统;使用System.Collections.Generic;使用System.ComponentModel;使用System.Diagnostics程序;使用System.Linq的;使用System.Net;使用System.Windows;使用System.Windows.Controls的;使用System.Windows.Controls.Primitives;采用System.Windows.Documents;采用System.Windows.Input;采用System.Windows.Media;采用System.Windows.Media.Animation ;采用System.Windows.Shapes;命名空间Splan_RiaBusinessApplication.Controls {公共类时间码{公众诠释TimeCodeId {获取;集;}公共字符串代码{搞定;组; }公共字符串描述{搞定;组; }}公共类TimeDetail {公众诠释TimeDetailId {获取;设置; }公众诠释代码ID {获取;设置;}公共时间跨度开始{搞定;组; }公共时间跨度结束{搞定;组; }公共字符串代码{搞定;组; }公共字符串评论{搞定;组; }}公共部分类TimeCodeControl:ContentControl中{公共类TimeCodeControlEventArgs:EventArgs的{公共字符串username {搞定;组; }}私有静态时间跨度DoubleClickThreshold = TimeSpan.FromMilliseconds(300);私人的DateTime _lastClick;私人布尔m_EditMode = FALSE;公共布尔编辑模式{{返回m_EditMode; }集合{如果(m_EditMode =价值!){开关(值){案假:PlaceHolder.Template = this.Resources [DisplayTemplate]作为控件模板;打破;真实案例:PlaceHolder.Template = this.Resources [EditTemplate]作为控件模板;打破; } m_EditMode =价值; }}}公共BOOL IsFocused {{返回FocusManager.GetFocusedElement()==这一点; }}公共TimeCodeControl(){时间码=新的List<&时间码GT;(){新的时间代码{TimeCodeId = 200,代码=C,说明=现金}};的InitializeComponent(); Layout.DataContext =这一点; 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; }无效TimeCodeControl_GotFocus(对象发件人,RoutedEventArgs E){}无效TimeCodeControl_LostFocus(对象发件人,RoutedEventArgs E){}公共TimeDetail来源{{返回(TimeDetail)的GetValue(在SourceProperty); } {设定的SetValue(在SourceProperty,值); }}公共静态只读的DependencyProperty在SourceProperty = DependencyProperty.Register(源的typeof(TimeDetail)的typeof(TimeCodeControl),新PropertyMetadata(SourceChanged));私有静态无效SourceChanged(DependencyObject的发件人,DependencyPropertyChangedEventArgs E){风险控制=发件人为TimeCodeControl;如果(控制== NULL)回报; control.Target = control.Source; //。复制(); }公开名单<&时间码GT;时间码{搞定;组; }公共TimeDetail目标{搞定;组; }私人布尔FocusIsInside(对象父){布尔RS = FALSE;动态oFocus = FocusManager.GetFocusedElement();而(oFocus!= NULL){尝试如果((oFocus.GetType()== parent.GetType())及及(oFocus.Equals(本))){RS = TRUE;打破; }其他{oFocus = oFocus.Parent; }}赶上{打破; }回RS; }私人布尔hasFocus = FALSE;保护覆盖无效OnGotFocus(RoutedEventArgs E){base.OnGotFocus(E);如果(hasFocus!){hasFocus = TRUE;的Debug.WriteLine(容器GOT焦点); }}保护覆盖无效OnLostFocus(RoutedEventArgs E){base.OnLostFocus(E); //如果(FocusIsInside(布局)!)// {// hasFocus = FALSE; //的Debug.WriteLine(容器失去焦点); //编辑模式= FALSE; //}}无效Layout_MouseLeftButtonDown(对象发件人,MouseButtonEventArgs E){如果(DateTime.Now - this._lastClick< = DoubleClickThreshold){编辑模式=真实的; this._lastClick = DateTime.Now; e.Handled =真实的;返回; } this._lastClick = DateTime.Now; }}}



更新:
我决定利用定时器到时用户使焦点从容器的外部,或只是切换焦点从一个控制到容器的另一内确定一个场景。它可能不是最好的解决方案,但它似乎是工作现在。我希望在不同的方法或实现任何意见或建议。



<预类=片段 - 代码HTML语言,HTML prettyprint-覆盖> 公共部分类MyControl:ContentControl中{...公共事件的EventHandler< RoutedEventArgs>引发LostFocus;公共事件的EventHandler< RoutedEventArgs>的GotFocus;布尔重点= FALSE; DispatcherTimer FocusTimer = NULL;保护覆盖无效OnGotFocus(RoutedEventArgs E){base.OnGotFocus(E);如果(聚焦)回报;专注= TRUE; //它从// becouse定时器未在先前LostFocused事件初始化//由其他控制在同一ContentControl中contaner产生的控制的外侧集中,如果(FocusTimer == NULL){如果(的GotFocus!= NULL)的GotFocus(e.OriginalSource,E);的Debug.WriteLine(得到焦点);返回; } //它从一个切换控制主机到另一个FocusTimer.Stop(); FocusTimer = NULL; }保护覆盖无效OnLostFocus(RoutedEventArgs E){base.OnLostFocus(E);如果(e.OriginalSource为组合框和放大器;&安培; FocusManager.GetFocusedElement()是ComboBoxItem)回报; FocusTimer =新DispatcherTimer();专注= FALSE; FocusTimer.Interval =新时间跨度(0,0,0,0,50); FocusTimer.Tick + =(S,参数)=> {FocusTimer.Stop(); FocusTimer = NULL; //如果焦点仍没有//被触发//另一个包含的元素在超时后我们失去了一个焦点在容器上,如果{如果(引发LostFocus!= NULL)引发LostFocus(e.OriginalSource,E)(聚焦!);的Debug.WriteLine(失去焦点); }}; FocusTimer.Start(); } ...}


解决方案

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




为什么你不当您单击控制之外得到一个LostFocus事件?




好吧,我前一段时间的牺牲品这种错误的假设了。点击外面,除非你点击,明确将焦点设置本身的点击控件(如一个文本框呢,还是各种按钮)不会更改焦点。
按Tab键将键盘焦点转到下一个控件,看看事件引发



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




控件模板X:键=DisplayTemplate控件模板X:关键=EditTemplate




使用 CONTROLTEMPLATES 此不推荐的方式。而使用的DataTemplate 和相应的 ContentPresenters




TimeCodeControl:ContentControl中 X:类=Splan_RiaBusinessApplication.Controls.TimeCodeControl




是的,我知道这是可能的,但不是真的有用。让我来解释一下:
你可以写你自己的专业的点击Edit控件作为一次性工具:具有硬编码DisplayTemplate和EditTemplate编辑时间码 TimeDetail 数据(你做基本上是)。但你没有使用过它,并指定另一对模板以允许其他数据类型的编辑机会。
所以也没有太大的意义自ContentControl派生,你可以同时从用户控件派生



另一种方法是:写你的点击 - 编辑控制作为一般的和可重复使用的控制,这提供了两个公共属性:DisplayTemplate和EditTemplate。不要让你的DataContext任何假设。并再次不存在来自具有ContentControl中作为父类受益。
我建议你从控制派生,添加两个类型的 DependencyProperties 的DataTemplate 正如前面提到的,定义默认控件模板里面一个或两个ContentPresenters。在你的控制代码,你需要相应的处理的MouseLeftButtonDown和LostFocus和更新布尔标记



下面是一个工作的例子:



...扩展方法来确定重点:

 公共静态类ControlExtensions 
{
公共静态布尔IsFocused(此UIElement控制)
{
DependencyObject的父母;
为(DependencyObject的potentialSubControl = FocusManager.GetFocusedElement()作为DependencyObject的; potentialSubControl = NULL;!potentialSubControl =父)
{
如果(object.ReferenceEquals(potentialSubControl,控制))
{
返回真;
}
父= VisualTreeHelper.GetParent(potentialSubControl);
如果(家长== NULL)
{
FrameworkElement的元素= potentialSubControl为FrameworkElement的;
如果(元素!= NULL)
{
父= element.Parent;
}
}
}
返回false;
}
}



...和一个不错的自定义控件:

 公共类ClickToEditControl:控制
{
公共ClickToEditControl()
{
DefaultStyleKey = typeof运算(ClickToEditControl);
的MouseLeftButtonDown + = OnMouseLeftButtonDown在;
}

私人无效OnMouseLeftButtonDown在(对象发件人,MouseButtonEventArgs E)
{
如果(e.ClickCount == 2)
{
GotoEditMode();
e.Handled = TRUE;
}
}

保护覆盖无效OnLostFocus(RoutedEventArgs E)
{
base.OnLostFocus(E);

如果
GotoDisplayMode()(this.IsFocused()!);
}

私人无效GotoDisplayMode()
{
IsInEditMode = FALSE;
}

私人无效GotoEditMode()
{
IsInEditMode = TRUE;
}

公众的DataTemplate EditTemplate
{
{返回(的DataTemplate)的GetValue(EditTemplateProperty); }
集合{的SetValue(EditTemplateProperty,值); }
}

公共静态只读的DependencyProperty EditTemplateProperty =
DependencyProperty.Register(EditTemplate的typeof(DataTemplate中)的typeof(ClickToEditControl),NULL);

公众的DataTemplate DisplayTemplate
{
{返回(的DataTemplate)的GetValue(DisplayTemplateProperty); }
集合{的SetValue(DisplayTemplateProperty,值); }
}

公共静态只读的DependencyProperty DisplayTemplateProperty =
DependencyProperty.Register(DisplayTemplate的typeof(DataTemplate中)的typeof(ClickToEditControl),NULL);

公共BOOL IsInEditMode
{
{返回(布尔)的GetValue(IsInEditModeProperty); }
集合{的SetValue(IsInEditModeProperty,值); }
}

公共静态只读的DependencyProperty IsInEditModeProperty =
DependencyProperty.Register(IsInEditMode的typeof(布尔)的typeof(ClickToEditControl),NULL);
}



...和控件模板:

 < clickToEdit:BoolToVisibilityConverter X:键=VisibleIfInEditMode/> 
< clickToEdit:BoolToVisibilityConverter X:键=CollapsedIfInEditModeVisibleIfTrue =FALSE/>

<风格的TargetType =clickToEdit:ClickToEditControl>
< setter属性=模板>
< Setter.Value>
<的ControlTemplate的TargetType =clickToEdit:ClickToEditControl>
<网格和GT;
< ContentPresenter
的ContentTemplate ={TemplateBinding EditTemplate}
含量={结合}
能见度={结合IsInEditMode,的RelativeSource = {的RelativeSource TemplatedParent},转换器= {StaticResource的VisibleIfInEditMode}}/>
< ContentPresenter
的ContentTemplate ={TemplateBinding DisplayTemplate}
含量={结合}
能见度={结合IsInEditMode,的RelativeSource = {的RelativeSource TemplatedParent},转换器= {StaticResource的CollapsedIfInEditMode}}/>
< /网格和GT;
< /控件模板>
< /Setter.Value>
< /二传手>
< /样式和GT;



BoolToVisibilityConverter

 公共类BoolToVisibilityConverter:的IValueConverter 
{
公共BOOL VisibleIfTrue {搞定;组; }

公共BoolToVisibilityConverter(){VisibleIfTrue = TRUE;}

公共对象转换(对象的值,类型TARGETTYPE,对象参数,CultureInfo的文化)
{
如果(VisibleIfTrue)
回报率((布尔)值)? Visibility.Visible:Visibility.Collapsed;
,否则
回报率((布尔)值)? Visibility.Collapsed:Visibility.Visible;
}

公共对象ConvertBack(对象的值,类型TARGETTYPE,对象参数,CultureInfo的文化){抛出新NotSupportedException异常();}
}

用法:

 < clickToEdit :ClickToEditControl高度=20宽度=200> 
< clickToEdit:ClickToEditControl.DisplayTemplate>
<&DataTemplate的GT;
< TextBlock的文本={结合MYTEXT}/>
< / DataTemplate中>
< / clickToEdit:ClickToEditControl.DisplayTemplate>
< clickToEdit:ClickToEditControl.EditTemplate>
<&DataTemplate的GT;
<文本框的文本={结合MYTEXT,模式=双向}/>
< / DataTemplate中>
< / clickToEdit:ClickToEditControl.EditTemplate>
< / clickToEdit:ClickToEditControl>


I am working on a simple Custom Control that should go to Edit mode by double clicking on it

The concept is based on this question Click-to-edit in Silverlight

On a double click it changes initial template on Edit Template and it seems to be pretty clear, except the part (5) How to change the template Back when the Control Looses the focus The Lost Focus event is fired only when contained controls are loosing focus Here is an article that talk about it http://programmerpayback.com/2008/11/20/gotfocus-and-lostfocus-events-on-containers/

I have tried to implement same Technic but still no result, I cannot get LostFocus event working for me when I click outside of a control

Where is my issue?

My XAML

<ContentControl x:Class="Splan_RiaBusinessApplication.Controls.TimeCodeControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:obj="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"  
    xmlns:behaviour="clr-namespace:Splan_RiaBusinessApplication.Behavior"
    xmlns:controls="clr-namespace:Splan_RiaBusinessApplication.Controls"
    xmlns:Primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"
    mc:Ignorable="d"
    IsTabStop="True"
    IsEnabled="True"
    Visibility="Visible"
    d:DesignHeight="100" d:DesignWidth="200"
    d:Height="200" d:Width="200"
                >
    <ContentControl.Resources>
        <ControlTemplate x:Key="DisplayTemplate">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding Target.Code, Mode=TwoWay}" />
                <StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" >
                    <TextBlock Text="{Binding Target.Start, Mode=TwoWay, StringFormat=hh\\:mm }" />
                    <TextBlock Text='-' />
                    <TextBlock Text="{Binding Target.End, Mode=TwoWay, StringFormat=hh\\:mm }" />
                </StackPanel>
            </Grid>
        </ControlTemplate>
        
        <ControlTemplate x:Key="EditTemplate">
            <Grid Background="Aqua" Height="200" Width="200">
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <ComboBox Width="100" Height="25" x:Name="cbTimeCode"
                        ItemsSource="{Binding TimeCodes}"
                        SelectedValue="{Binding Target.CodeId, Mode=TwoWay}"                                                                                           
                        SelectedValuePath="TimeCodeId"
                    >
                    <ComboBox.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="40"/>
                                    <ColumnDefinition Width="100"/>
                                </Grid.ColumnDefinitions>
                                <TextBlock Text="{Binding Target.Code}" />
                                <TextBlock Grid.Column="1" Text="{Binding Target.Description}" />
                            </Grid>
                        </DataTemplate>
                    </ComboBox.ItemTemplate>
                    <i:Interaction.Triggers>
                        <i:EventTrigger>
                            <behaviour:ResolveElementName PropertyName="ItemsSource" />
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                </ComboBox>

                <!--<controls:TimeRangePickerControl Grid.Row="1" StartTime="{Binding Target.Start, Mode=TwoWay}"  EndTime="{Binding Target.End, Mode=TwoWay}"/>-->
            </Grid>
        </ControlTemplate>
    </ContentControl.Resources>
    
    <Grid x:Name="Layout" Background="Aquamarine">
        <ItemsControl x:Name="PlaceHolder" Template="{StaticResource DisplayTemplate}">
        </ItemsControl>
    </Grid>
</ContentControl>

Code Behind

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<TimeCode>() { 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<TimeCode> 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;

        }
    }
}

UPDATE : I decided to utilize timers to identify a scenario when user brings a focus from outside of the container or just switches focus from one control to another inside of the container. it may be not the best solution but it seems to be working for now. I would appreciate any suggestions or recommendations on different approaches or implementations.

public partial class    MyControl: ContentControl
{
   ...

   public event EventHandler<RoutedEventArgs> LostFocus;
   public event EventHandler<RoutedEventArgs> 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();
        }

   ...
}

解决方案

There are several issues. Let's see...

Why are you not getting a LostFocus event when you click outside of the control?

Well, I fell victim to this false assumption some time ago too. The click outside does not change the focus unless you click a control that explicitly sets focus to itself on click (like a TextBox does, or the various Buttons). Press Tab to navigate the keyboard focus to the next control and see if the event is raised.

But let's talk about the other issues:

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

Using ControlTemplates this way is not recommended. Rather use DataTemplate and corresponding ContentPresenters.

TimeCodeControl : ContentControl and x:Class="Splan_RiaBusinessApplication.Controls.TimeCodeControl"

Yes I know that's possible, but not really useful. Let me explain: You can write your own specialized Click-To-Edit Control as a one-shot tool: having a hardcoded DisplayTemplate and EditTemplate to edit TimeCode and TimeDetail data (basically what you did). But then you have no chance of ever using it and specifying another pair of Templates to allow editing of other data types. So it doesn't make much sense to derive from ContentControl, you could as well derive from UserControl.

An alternative would be: Write your Click-To-Edit control as a general and reusable control, that offers two public properties: DisplayTemplate and EditTemplate. And don't make any assumptions about your DataContext. And again there is no benefit from having ContentControl as the parent class. I recommend you derive from Control, add two DependencyProperties of type DataTemplate as mentioned earlier, define a default ControlTemplate with one or two ContentPresenters inside. In your control code you need to handle MouseLeftButtonDown and LostFocus and update a boolean flag accordingly.

Here is a working example:

...extension method to determine focus:

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

...and a nice custom control:

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

...and ControlTemplate:

<clickToEdit:BoolToVisibilityConverter x:Key="VisibleIfInEditMode"/>
<clickToEdit:BoolToVisibilityConverter x:Key="CollapsedIfInEditMode" VisibleIfTrue="False"/>

<Style TargetType="clickToEdit:ClickToEditControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="clickToEdit:ClickToEditControl">
                <Grid>
                    <ContentPresenter
                        ContentTemplate="{TemplateBinding EditTemplate}"
                        Content="{Binding}"
                        Visibility="{Binding IsInEditMode, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource VisibleIfInEditMode}}"/>
                    <ContentPresenter
                        ContentTemplate="{TemplateBinding DisplayTemplate}"
                        Content="{Binding}"
                        Visibility="{Binding IsInEditMode, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource CollapsedIfInEditMode}}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

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

Usage:

<clickToEdit:ClickToEditControl Height="20" Width="200">
        <clickToEdit:ClickToEditControl.DisplayTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding MyText}"/>
            </DataTemplate>
        </clickToEdit:ClickToEditControl.DisplayTemplate>
        <clickToEdit:ClickToEditControl.EditTemplate>
            <DataTemplate>
                <TextBox Text="{Binding MyText, Mode=TwoWay}"/>
            </DataTemplate>
        </clickToEdit:ClickToEditControl.EditTemplate>
    </clickToEdit:ClickToEditControl>

这篇关于点击到编辑控件LostFocus事件问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆