Click-to-Edit Control LostFocus event issue

前提是你 提交于 2019-12-02 08:13:23

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>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!