WPF中TextBox在"双击和失去焦点事件"之间的只读状态开关 "on/off"

6

我有一个像下面的XAML控件,启用了只读功能。

          <TextBox  Text="{Binding Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"  Background="Transparent" IsReadOnly="True" BorderThickness="0" TextWrapping="Wrap" >   

现在当我双击文本框时,应该能够输入文本。ReadOnly属性应变为false。
如果我移动到窗口中的另一个项目而不是此文本框,则文本框应再次变为只读。
我正在尝试使用触发器来实现它,但没有得到正确的提示。有人可以在这里帮助我吗?

当失去焦点时,您是否希望使此文本框启用IsReadOnly? - Lai32290
4个回答

4
你可以使用 System.Windows.Interactivity 组件(msdn) 来实现这个功能。
首先,创建帮助类来设置属性:
public class SetterAction : TriggerAction<DependencyObject>
{
    public SetterAction()
    {
        Setters = new List<Setter>();
    }

    public List<Setter> Setters { get; set; }

    protected override void Invoke(object parameter)
    {
        foreach (var item in Setters)
        {
            AssociatedObject.SetValue(item.Property, item.Value);
        }
    }
}

XAML:
<TextBox Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
         Background="Transparent" IsReadOnly="True" BorderThickness="0" TextWrapping="Wrap"
         Height="30" Width="200">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseDoubleClick">
            <i:EventTrigger.Actions>
                <local:SetterAction>
                    <local:SetterAction.Setters>
                        <Setter Property="TextBox.IsReadOnly" Value="False" />
                        <Setter Property="TextBox.Background" Value="Green" />
                    </local:SetterAction.Setters>
                </local:SetterAction>
            </i:EventTrigger.Actions>
        </i:EventTrigger>
        <i:EventTrigger EventName="LostFocus">
            <i:EventTrigger.Actions>
                <local:SetterAction>
                    <local:SetterAction.Setters>
                        <Setter Property="TextBox.IsReadOnly" Value="True" />
                        <Setter Property="TextBox.Background" Value="Red" />
                    </local:SetterAction.Setters>
                </local:SetterAction>
            </i:EventTrigger.Actions>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TextBox>

其中i的含义是:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

这个 Blend SDK 的程序集是有许可证的吗? - shahul hameed
@shahulhameed 你可以使用这个汇编。查看这个线程:http://social.msdn.microsoft.com/Forums/expression/en-US/8523aec4-1a10-4864-8ad4-f95a3627bb4a/redistribution-of-systemwindowsinteractivitydll - kmatyaszek

4
你可以使用 Style 和 EventTrigger 来实现这个功能。
<Window xmlns:sys="clr-namespace:System;assembly=mscorlib" ...>
    <Window.Resource>
        <Style x:Key="MyTextBoxStyle" TargetType="{x:Type TextBox}">
            <Style.Triggers>
                <EventTrigger RoutedEvent="LostFocus">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Duration="0"
                                    Storyboard.TargetProperty="(TextBox.IsReadOnly)">
                                    <DiscreteObjectKeyFrame KeyTime="0">
                                        <DiscreteObjectKeyFrame.Value>
                                            <sys:Boolean>True</sys:Boolean>
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
                <EventTrigger RoutedEvent="MouseDoubleClick">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Duration="0"
                                       Storyboard.TargetProperty="(TextBox.IsReadOnly)">
                                    <DiscreteObjectKeyFrame KeyTime="0">
                                        <DiscreteObjectKeyFrame.Value>
                                            <sys:Boolean>False</sys:Boolean>
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resource>
    ...
    <TextBox Style="{StaticResource MyTextBoxStyle}" .../>
</Window>

不行,但是它的行为不符合我的要求。 - shahul hameed
我尝试了一下,但是它给了我这个错误:“无法使用'System.Windows.Media.Animation.ObjectAnimationUsingKeyFrames'在'System.Windows.Controls.TextBox'上动画化'IsReadOnly'属性。” - Hossein Shahdoost

4

您可以使用两个事件来完成此操作,即MouseDoubleClick和LostFocus

<Grid>
    <TextBox IsReadOnly="True"
             MouseDoubleClick="TextBox_MouseDoubleClick"
             LostFocus="TextBox_LostFocus"/>
</Grid>

在您的过程化代码中:
private void TextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    TextBox textBox = sender as TextBox;
    textBox.IsReadOnly = false;
    //textBox.CaretIndex = textBox.Text.Count();
    textBox.SelectAll();
}

private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
    TextBox textBox = sender as TextBox;
    textBox.IsReadOnly = true;
}

我尝试过这个方法,它可以工作。但是当我双击文本框时,正常的行为是选择所有数据。在这种方法中,默认情况下光标会移动到内容的最后一个元素。但是我需要的是正常的光标选择行为。我该怎么做呢? - shahul hameed

0

最佳答案是使用附加依赖属性的形式。用法:

xmlns:e="clr-namespace:Extensions"

<TextBox e:TextBoxExtensions.IsEditableOnlyOnDoubleClick="True"/>

#nullable enable

namespace Extensions;

public static class TextBoxExtensions
{
    #region IsEditableOnlyOnDoubleClick

    public static readonly DependencyProperty IsEditableOnlyOnDoubleClickProperty =
        DependencyProperty.RegisterAttached(
            nameof(IsEditableOnlyOnDoubleClickProperty).Replace("Property", string.Empty),
            typeof(bool),
            typeof(TextBoxExtensions),
            new PropertyMetadata(false, OnIsEditableOnlyOnDoubleClickChanged));

    [AttachedPropertyBrowsableForType(typeof(TextBox))]
    public static bool GetIsEditableOnlyOnDoubleClick(DependencyObject element)
    {
        return (bool)element.GetValue(IsEditableOnlyOnDoubleClickProperty);
    }

    public static void SetIsEditableOnlyOnDoubleClick(DependencyObject element, bool value)
    {
        element.SetValue(IsEditableOnlyOnDoubleClickProperty, value);
    }

    private static void OnIsEditableOnlyOnDoubleClickChanged(
        DependencyObject element,
        DependencyPropertyChangedEventArgs args)
    {
        if (element is not TextBox textBox)
        {
            throw new ArgumentException($"{nameof(element)} should be {nameof(TextBox)}.");
        }

        if (args.OldValue is true)
        {
            textBox.MouseDoubleClick -= TextBox_MouseDoubleClick;
            textBox.LostFocus -= TextBox_LostFocus;
            textBox.IsReadOnly = false;
        }

        if (args.NewValue is true)
        {
            textBox.MouseDoubleClick += TextBox_MouseDoubleClick;
            textBox.LostFocus += TextBox_LostFocus;
            textBox.IsReadOnly = true;
        }
    }

    private static void TextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        if (sender is not TextBox textBox)
        {
            return;
        }

        textBox.IsReadOnly = false;
        textBox.SelectAll();
    }

    private static void TextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        if (sender is not TextBox textBox)
        {
            return;
        }

        textBox.IsReadOnly = true;
    }

    #endregion
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接