Выделите весь текст в пользовательском элементе управления TextBox в DataGrid на CellEditingTemplate - PullRequest
0 голосов
/ 26 марта 2020

Вопрос говорит сам за себя: я могу использовать События, чтобы выделить весь текст в DataBrid Custom TextBox, НО он не работает при первоначальном создании TextBox (т.е. когда ячейка входит в режим редактирования и создает TextBox).

ЕСЛИ я щелкаю в TextBox после его создания, текст полностью выделен, но он должен быть уже выбран после отображения TextBox. Это не работает. Я попытался установить Focus в коде или использовать FocusManager в XAML, но это не помогает.

Вот код (за исключением свойств зависимости):

<ResourceDictionary xmlns       = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x     = "http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:ccont = "clr-namespace:App.Controls">

    <!-- Default style for the Validation Buttons -->
    <Style TargetType="{x:Type ccont:vokDataGridEdit}">

        <Setter Property="SnapsToDevicePixels"  Value="true" />

        <Setter Property="Template">
            <Setter.Value>

                <ControlTemplate TargetType="{x:Type ccont:vokDataGridEdit}">

                    <TextBox Text                               = "{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType=ccont:vokDataGridEdit}}"
                             BorderThickness                    = "0"
                             ContextMenuService.Placement       = "Right"
                             ContextMenuService.PlacementTarget = "{Binding Path=., RelativeSource={RelativeSource Self}}"
                             x:Name                             = "TextBox">

                        <TextBox.ContextMenu>
                            <ContextMenu x:Name="Menu">
                                <ContextMenu.Template>
                                    <ControlTemplate>

                                        <Border CornerRadius    = "5"
                                                Background      = "LightGray"
                                                BorderThickness = "1" 
                                                BorderBrush     = "Gray"
                                                Padding         = "2">

                                            <StackPanel Orientation="Vertical">

                                                <!-- Title -->
                                                <TextBlock Text="Test" x:Name = "Test" />

                                                <!-- TODO: List of matches -->
                                                <TextBox Text               = "{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType=ccont:vokDataGridEdit}}" 
                                                         BorderThickness    = "0" />

                                            </StackPanel>

                                        </Border>

                                    </ControlTemplate>
                                </ContextMenu.Template>
                            </ContextMenu>
                        </TextBox.ContextMenu>

                    </TextBox>

                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

и код (свойства зависимости не показаны) :

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace App.Controls
{
    /// <summary>
    /// DataGrid Edit control (see: https://www.c-sharpcorner.com/article/wpf-routed-events/ for RoutedEvents)
    /// </summary>
    public class vokDataGridEdit : Control
    {
        static vokDataGridEdit()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(vokDataGridEdit), new FrameworkPropertyMetadata(typeof(vokDataGridEdit)));
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            // Demo purpose only, check for previous instances and remove the handler first  
            if (this.GetTemplateChild("TextBox") is TextBox button)
            {
                button.PreviewMouseLeftButtonDown   += this.SelectContentPreparation;
                button.GotKeyboardFocus             += this.SelectContent;
                button.MouseDoubleClick             += this.SelectContent;
                //button.GotFocus                     += this.SelectContent;
            }
        }

        /// <summary>
        /// Prepare the Control to ensure it has focus before subsequent event fire
        /// </summary>
        private void SelectContentPreparation(object sender, MouseButtonEventArgs e)
        {
            if (sender is TextBox tb)
            {
                if (!tb.IsKeyboardFocusWithin)
                {
                    e.Handled = true;
                    tb.Focus();
                }
            }
        }

        private void SelectContent(object sender, RoutedEventArgs e)
        {
            if (sender is TextBox tb)
            {
                e.Handled = true;
                tb.SelectAll();
            }
        }
    }
}

1 Ответ

0 голосов
/ 12 апреля 2020

Хорошо, наконец, использование Поведения решило мою проблему в связи с использованием Events в CustomControl. Я до сих пор не знаю, почему он не работал с помощью Events ...

CustomControl XAML:

<ResourceDictionary xmlns       = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x     = "http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:b     = "http://schemas.microsoft.com/xaml/behaviors"

                    xmlns:ccont = "clr-namespace:App.Controls"
                    xmlns:res   = "clr-namespace:App.Controls.Resources"
                    xmlns:valid = "clr-namespace:App.Controls.Validation"
                    xmlns:conv  = "clr-namespace:Common.MVVM.Converter;assembly=Common.MVVM"
                    xmlns:behav = "clr-namespace:Common.MVVM.Behavior;assembly=Common.MVVM">

    <!-- Default style for the Validation Buttons -->
    <Style TargetType="{x:Type ccont:vokDataGridEdit}">

        <Setter Property="SnapsToDevicePixels"  Value="true" />

        <Setter Property="Template">
            <Setter.Value>

                <ControlTemplate TargetType="{x:Type ccont:vokDataGridEdit}">

                    <Grid>

                        <!-- !!! The Value edited !!! -->
                        <TextBox BorderThickness    = "0"
                                 x:Name             = "textBox">

                            <!-- Create a binding proxy to serve binding properties to data validation Binding Wrapper
                                 see: https://social.technet.microsoft.com/wiki/contents/articles/31422.wpf-passing-a-data-bound-value-to-a-validation-rule.aspx -->
                            <TextBox.Resources>
                                <valid:BindingProxy x:Key="proxy" Context="{Binding HintsInternal, RelativeSource={RelativeSource AncestorType=ccont:vokDataGridEdit}}"/>
                            </TextBox.Resources>

                            <!-- Bind with data validation -->
                            <TextBox.Text>
                                <Binding RelativeSource         = "{RelativeSource AncestorType=ccont:vokDataGridEdit}"
                                         Path                   = "Text"
                                         Mode                   = "TwoWay"
                                         UpdateSourceTrigger    = "PropertyChanged"
                                         ValidatesOnExceptions  = "True">

                                    <Binding.ValidationRules>
                                        <valid:vokDataGridEditValidation>
                                            <valid:vokDataGridEditValidation.Bindings>
                                                <valid:vokDataGridEditValidationBindings InvalidEntries="{Binding Context, Source={StaticResource proxy}}" />
                                            </valid:vokDataGridEditValidation.Bindings>
                                        </valid:vokDataGridEditValidation>
                                    </Binding.ValidationRules>

                                </Binding>
                            </TextBox.Text>

                            <!-- Select all text on focus -->
                            <b:Interaction.Behaviors>
                                <behav:TextBoxSelectAllBehavior />
                            </b:Interaction.Behaviors>

                        </TextBox>

                    </Grid>

                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

И объявления событий CustomControl:

/// <summary>
/// DataGrid Edit control (see: https://www.c-sharpcorner.com/article/wpf-routed-events/ for RoutedEvents)
/// </summary>
public class vokDataGridEdit : Control
{
    #region Initialization

    static vokDataGridEdit()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(vokDataGridEdit), new FrameworkPropertyMetadata(typeof(vokDataGridEdit)));
    }

    /// <summary>
    /// Set event handlers when applying the template
    /// </summary>
    public override void OnApplyTemplate()
    {   
        base.OnApplyTemplate();

        // OnLoaded set focus to the TextBox (lambda and own Event need not Garbage Collection) and ensure it has focus for new text input
        if (this.GetTemplateChild("textBox") is TextBox textBox)
        {
            this.Loaded             += (sender, e) => { textBox.Focus(); };
            this.PreviewTextInput   += (sender, e) => { textBox.Focus(); };
        }
    }

    #endregion

    // Skipping Dependency Properties
}

И Finaly поведение, основанное на ответе пользователя Рекшино здесь ({ ссылка }):

/// <summary>
/// Behavior for control to select all text in TextBox on GotFocus
/// </summary>
public class TextBoxSelectAllBehavior : Behavior<TextBox>
{
    /// <summary>
    /// Flag marking if Selection is to be performed on MouseUp
    /// </summary>
    private bool _doSelectAll = false;

    /// <summary>
    /// Setup the behavior
    /// </summary>
    protected override void OnAttached()
    {
        base.OnAttached();

        this.AssociatedObject.GotFocus          += this.AssociatedObject_GotFocus;
        this.AssociatedObject.PreviewMouseUp    += this.AssociatedObject_MouseUp;
        this.AssociatedObject.PreviewMouseDown  += this.AssociatedObject_MouseDown;
    }

    /// <summary>
    /// Select all via dispatcher if action set for MouseUp
    /// </summary>
    private void AssociatedObject_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        if (this._doSelectAll)
        {
            this.AssociatedObject.Dispatcher.BeginInvoke((Action)(() => { this.AssociatedObject.SelectAll(); }));
        }

        this._doSelectAll = false;
    }

    /// <summary>
    /// Triggers SelectAll on mouse up if focus was not set
    /// </summary>
    private void AssociatedObject_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        this._doSelectAll = !this.AssociatedObject.IsFocused;
    }

    /// <summary>
    /// Selects all
    /// </summary>
    private void AssociatedObject_GotFocus(object sender, System.Windows.RoutedEventArgs e)
    {
        this.AssociatedObject.SelectAll();
    }

    /// <summary>
    /// Clean-up the behavior
    /// </summary>
    protected override void OnDetaching()
    {
        this.AssociatedObject.GotFocus          -= this.AssociatedObject_GotFocus;
        this.AssociatedObject.PreviewMouseUp    -= this.AssociatedObject_MouseUp;
        this.AssociatedObject.PreviewMouseDown  -= this.AssociatedObject_MouseDown;

        base.OnDetaching();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...