【问题标题】:Click-to-edit control - how to do it?点击编辑控件 - 怎么做?
【发布时间】:2010-11-01 08:24:05
【问题描述】:

我正在使用 Silverlight 2.0 Unleashed + Silverlight 4.0 Unleashed 来学习 Silverlight,而且,我只是在玩弄它 :-)

作为其中的一部分,我正在尝试开发一个非常简单的内容控件:一个可以通过单击来编辑的标签。

我的灵感来自另一个 SO 问题 (Click-to-edit in Silverlight),但我遇到了一些与我刚接触 Silverlight 相关的问题。 :)

我将首先发布代码,然后是我的问题。

这是我的 XAML:

<ContentControl x:Class="MyTestProject.SilverlightControl1"
    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:sdk="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Input" xmlns:local="clr-namespace:MyTestProject" mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    <ContentControl.Resources>
        <ControlTemplate x:Key="EditableLabelTextboxTemplate">
            <TextBox Name="UCTB" Text="{Binding Text, Mode=TwoWay}" Width="100" Height="25" ></TextBox>
        </ControlTemplate>
        <ControlTemplate x:Name="LabelTemplate" TargetType="local:SilverlightControl1">
            <sdk:Label x:Name="UCLBL" Content="{Binding Text}" />
        </ControlTemplate>
        <Style TargetType="local:SilverlightControl1">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate x:Name="LabelTemplate2" TargetType="local:SilverlightControl1">
                        <sdk:Label x:Name="UCLBL" Content="{Binding Text}" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ContentControl.Resources>
</ContentControl>

这是我的代码隐藏:

public partial class SilverlightControl1 : ContentControl
    {
        public string Text { get; set; }

        public SilverlightControl1()
        {
            this.DataContext = this;
            Text = "Hello, World!";
            this.DefaultStyleKey = typeof(SilverlightControl1);
            //Template = this.Resources["LabelTemplate"] as ControlTemplate;
            InitializeComponent();
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this.MouseLeftButtonDown += HandleLabelLeftMouseDown;
            this.KeyDown += HandleTextboxEnter;
        }

        public static void HandleTextboxEnter(object sender, KeyEventArgs args)
        {
            var ctrl = sender as SilverlightControl1;
            if (ctrl.Template == ctrl.Resources["EditableLabelTextboxTemplate"])
            {
                if (args.Key == Key.Enter)
                {
                    ctrl.Template = ctrl.Resources["LabelTemplate"] as ControlTemplate;
                }
            }
        }

        public static void HandleLabelLeftMouseDown(object sender, MouseButtonEventArgs args)
        {
            var editableLabel = sender as SilverlightControl1;
            if (editableLabel.Template != editableLabel.Resources["EditableLabelTextboxTemplate"])
            {
                editableLabel.Template = editableLabel.Resources["EditableLabelTextboxTemplate"] as ControlTemplate;
            }
        }
    }

首先,我必须实例化我的控件的方式非常笨拙和多余。

有 2 个 ControlTemplates(一个标签,一个文本框),但还有一个带有用于初始化的标签的样式。如果我删除它(以及代码隐藏中构造函数中的对应行),即使我也设置了模板,也不会呈现任何内容。一定有更好的办法吗?

我的第二个问题是,当我将控件放入测试项目并使用它时,控件移动?它会从中间左侧作为标签开始,然后单击它,文本框出现在页面中心,输入时标签重新出现在屏幕左侧?

编辑: 第三,一旦我用文本框替换标签,文本框没有焦点,我不知道如何给它?

最后,现在停止编辑的唯一方法是点击“输入”。理想情况下,我只想在文本框外单击,但是使用丢失焦点事件(如链接中所建议的那样)似乎过于频繁地触发我无法使用它?

我希望一些 Silverlight 大师可以帮助我! :-)

哦,如果有人知道我可以使用/查看可用的点击编辑控件,我也会非常感激 :)

提前致谢!

【问题讨论】:

    标签: silverlight silverlight-4.0


    【解决方案1】:

    我希望我能理解你。你需要的是一个TextBox,它一开始是不可编辑的,但如果你点击它,它就会是可编辑的。

    为此,我创建了一个从 TextBox 继承的控件(我使用了 vs 模板 TemplatedControl)。我添加了两个状态(Edit 和 NotEdit)和一个 Rectangle,它作为非编辑模式的文本层(以防止在鼠标悬停时更改鼠标光标)。我还在边界上做了一些任务。例如。在非编辑状态下,边框大小为 0,因此 TextBox 看起来像一个标签。

    这是 xaml:

    <Style TargetType="local:ClickToEditTextBox">
                <Setter Property="BorderThickness" Value="1"/>
                <Setter Property="Background" Value="#FFFFFFFF"/>
                <Setter Property="Foreground" Value="#FF000000"/>
                <Setter Property="Padding" Value="2"/>
                <Setter Property="BorderBrush">
                    <Setter.Value>
                        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                            <GradientStop Color="#FFA3AEB9" Offset="0"/>
                            <GradientStop Color="#FF8399A9" Offset="0.375"/>
                            <GradientStop Color="#FF718597" Offset="0.375"/>
                            <GradientStop Color="#FF617584" Offset="1"/>
                        </LinearGradientBrush>
                    </Setter.Value>
                </Setter>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="local:ClickToEditTextBox">
                            <Grid x:Name="RootElement">
                                <VisualStateManager.VisualStateGroups>
                                    <VisualStateGroup x:Name="CommonStates">
                                        <VisualState x:Name="Normal"/>
                                        <VisualState x:Name="MouseOver"/>
                                        <VisualState x:Name="Disabled">
                                            <Storyboard>
                                                <DoubleAnimation Storyboard.TargetName="DisabledVisualElement" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
                                            </Storyboard>
                                        </VisualState>
                                        <VisualState x:Name="ReadOnly">
                                            <Storyboard>
                                                <DoubleAnimation Storyboard.TargetName="ReadOnlyVisualElement" Storyboard.TargetProperty="Opacity" To="1" Duration="0" />
                                            </Storyboard>
                                        </VisualState>
                                    </VisualStateGroup>
                                    <VisualStateGroup x:Name="FocusStates">
                                        <VisualState x:Name="Focused">
                                            <Storyboard>
                                                <DoubleAnimation Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
                                            </Storyboard>
                                        </VisualState>
                                        <VisualState x:Name="Unfocused">
                                            <Storyboard>
                                                <DoubleAnimation Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity" To="0" Duration="0"/>
                                            </Storyboard>
                                        </VisualState>
                                    </VisualStateGroup>
                                    <VisualStateGroup x:Name="ValidationStates">
                                        <VisualState x:Name="Valid"/>
                                        <VisualState x:Name="InvalidUnfocused">
                                            <Storyboard>
                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility">
                                                    <DiscreteObjectKeyFrame KeyTime="0">
                                                        <DiscreteObjectKeyFrame.Value>
                                                            <Visibility>Visible</Visibility>
                                                        </DiscreteObjectKeyFrame.Value>
                                                    </DiscreteObjectKeyFrame>
                                                </ObjectAnimationUsingKeyFrames>
                                            </Storyboard>
                                        </VisualState>
                                        <VisualState x:Name="InvalidFocused">
                                            <Storyboard>
                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility">
                                                    <DiscreteObjectKeyFrame KeyTime="0">
                                                        <DiscreteObjectKeyFrame.Value>
                                                            <Visibility>Visible</Visibility>
                                                        </DiscreteObjectKeyFrame.Value>
                                                    </DiscreteObjectKeyFrame>
                                                </ObjectAnimationUsingKeyFrames>
                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsOpen">
                                                    <DiscreteObjectKeyFrame KeyTime="0">
                                                        <DiscreteObjectKeyFrame.Value>
                                                            <sys:Boolean>True</sys:Boolean>
                                                        </DiscreteObjectKeyFrame.Value>
                                                    </DiscreteObjectKeyFrame>
                                                </ObjectAnimationUsingKeyFrames>
                                            </Storyboard>
                                        </VisualState>
                                    </VisualStateGroup>
                                    <!-- The new edit states -->
                                    <VisualStateGroup x:Name="EditStates">
                                        <VisualState x:Name="Edit">
                                            <Storyboard>
                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="rectangle">
                                                    <DiscreteObjectKeyFrame KeyTime="0">
                                                        <DiscreteObjectKeyFrame.Value>
                                                            <Visibility>Collapsed</Visibility>
                                                        </DiscreteObjectKeyFrame.Value>
                                                    </DiscreteObjectKeyFrame>
                                                </ObjectAnimationUsingKeyFrames>
                                            </Storyboard>
                                        </VisualState>
                                        <VisualState x:Name="NotEdit">
                                            <Storyboard>
                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderThickness)" Storyboard.TargetName="Border">
                                                    <DiscreteObjectKeyFrame KeyTime="0">
                                                        <DiscreteObjectKeyFrame.Value>
                                                            <Thickness>0</Thickness>
                                                        </DiscreteObjectKeyFrame.Value>
                                                    </DiscreteObjectKeyFrame>
                                                </ObjectAnimationUsingKeyFrames>
                                            </Storyboard>
                                        </VisualState>
                                    </VisualStateGroup>
                                </VisualStateManager.VisualStateGroups>
                                <Border x:Name="Border" CornerRadius="1" Opacity="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1">
                                    <Grid>
                                        <Border x:Name="ReadOnlyVisualElement" Opacity="0" Background="#5EC9C9C9"/>
                                        <Border x:Name="MouseOverBorder" BorderThickness="1" BorderBrush="Transparent">
                                            <Grid>
                                                <ScrollViewer x:Name="ContentElement" Padding="{TemplateBinding Padding}" BorderThickness="0" IsTabStop="False"/>
                                                <Rectangle x:Name="rectangle" Fill="#02FFFFFF" />
                                            </Grid>
                                        </Border>
                                    </Grid>
                                </Border>
                                <Border x:Name="DisabledVisualElement" Background="#A5F7F7F7" BorderBrush="#A5F7F7F7" BorderThickness="{TemplateBinding BorderThickness}" Opacity="0" IsHitTestVisible="False"/>
                                <Border x:Name="FocusVisualElement" BorderBrush="#FF6DBDD1" BorderThickness="{TemplateBinding BorderThickness}" Margin="1" Opacity="0" IsHitTestVisible="False"/>
                                <Border x:Name="ValidationErrorElement" BorderThickness="1" CornerRadius="1" BorderBrush="#FFDB000C" Visibility="Collapsed">
                                    <ToolTipService.ToolTip>
                                        <ToolTip x:Name="validationTooltip" Template="{StaticResource ValidationToolTipTemplate}" Placement="Right" 
                             PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
                             DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
                                            <ToolTip.Triggers>
                                                <EventTrigger RoutedEvent="Canvas.Loaded">
                                                    <BeginStoryboard>
                                                        <Storyboard>
                                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsHitTestVisible">
                                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                                    <DiscreteObjectKeyFrame.Value>
                                                                        <sys:Boolean>true</sys:Boolean>
                                                                    </DiscreteObjectKeyFrame.Value>
                                                                </DiscreteObjectKeyFrame>
                                                            </ObjectAnimationUsingKeyFrames>
                                                        </Storyboard>
                                                    </BeginStoryboard>
                                                </EventTrigger>
                                            </ToolTip.Triggers>
                                        </ToolTip>
                                    </ToolTipService.ToolTip>
                                    <Grid Width="12" Height="12" HorizontalAlignment="Right" Margin="1,-4,-4,0" VerticalAlignment="Top" Background="Transparent">
                                        <Path Margin="1,3,0,0" Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" Fill="#FFDC000C"/>
                                        <Path Margin="1,3,0,0" Data="M 0,0 L2,0 L 8,6 L8,8" Fill="#ffffff"/>
                                    </Grid>
                                </Border>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
    

    如您所见,有两个新状态,Edit 和 NotEdit。

    所以在后面的代码中,我将控件初始“发送”到 NotEdit 状态并添加两个处理程序,用于 GotFocus 和 LostFocus。

    this.GotFocus += ClickToEditTextBox_GotFocus;
    this.LostFocus += ClickToEditTextBox_LostFocus;
    VisualStateManager.GoToState(this, "NotEdit", false);
    

    处理程序只是“发送”处于定义状态的控件:

    void ClickToEditTextBox_LostFocus(object sender, RoutedEventArgs e)
            {
                VisualStateManager.GoToState(this, "NotEdit", false);
            }
    
            void ClickToEditTextBox_GotFocus(object sender, RoutedEventArgs e)
            {
                VisualStateManager.GoToState(this, "Edit", false);
            }
    

    这是完整的 src 代码:

    public class ClickToEditTextBox : TextBox
        {
            public ClickToEditTextBox()
            {
                this.DefaultStyleKey = typeof(ClickToEditTextBox);
            }
    
            public override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
    
                this.GotFocus += ClickToEditTextBox_GotFocus;
                this.LostFocus += ClickToEditTextBox_LostFocus;
    
                VisualStateManager.GoToState(this, "NotEdit", false);
    
            }
    
            void ClickToEditTextBox_LostFocus(object sender, RoutedEventArgs e)
            {
                VisualStateManager.GoToState(this, "NotEdit", false);
            }
    
            void ClickToEditTextBox_GotFocus(object sender, RoutedEventArgs e)
            {
                VisualStateManager.GoToState(this, "Edit", false);
            }
        }
    

    现在你可以在你的 UserControl 中使用它了:

    <SilverlightApplication1:ClickToEditTextBox Text="12345" Width="100" Height="22" />
    

    这只是一个快速实现,因此可以进行一些改进。但我希望这可以帮助您解决问题。

    BR,TJ

    【讨论】:

    • 非常感谢!这比我想出的要好!我想我唯一想要的就是如果我在外面点击就停止编辑,而不是真正关注其他控件。不过,我猜这可以使用一些点击处理程序来完成。 (我还必须在您的代码中为 ValidationToolTipTemplate 添加一个 ControlTemplate,但没什么大不了的,仅供参考!)另外,非常感谢您对发生的事情的解释 - 所有这些 XAML 有时可能非常密集:) 干杯!
    • 不客气(抱歉,我忘记了 ValidationTooltipTemplate)。您可以将 MouseLeftButtonUp 处理程序添加到 RootVisual。 (Application.Current.RootVisual.MouseLeftButtonUp += RootVisual_MouseLeftButtonUp; 或者可能是 AddHandler 方法。有了这个,你可以获得已经处理的事件。)在那里你可以检查鼠标事件是否在你的控件上。如果没有,请转到 NotEdit 状态。这应该可行,但它不是一个非常好的解决方案......
    • 我现在知道这是古老的历史了,但是 Silverlight 项目刚刚转换为 WPF 项目,其中一个问题是点击编辑文本框:/ 出于某种原因,我实际上无法使用它输入/显示任何文本......我知道这是一个冰雹,但你知道为什么会这样吗?再次感谢您!
    • 你能调试它吗?如果是,请尝试是否会调用事件处理程序。如果没有,get focus 事件最终会在它被调用之前被处理。如果是这种情况,您必须使用 .AddHandler 方法添加处理程序。在 SL 中,您也可以将最后一个参数设置为 true,以获取已处理的事件。但我不确定它是否在 WPF 中以这种方式工作。
    • 为您的提示干杯!在通过 ContentTemplates 盲目摸索大约 6 小时后,我终于想出了要更改的内容...显然,您示例中的 ScrollViewer 应该具有名称 PART_ContentHost 以获得文本属性!有时,所有这些幕后的魔法都很难调试...... :) 再次感谢,再次打扰您:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-04
    • 2018-05-25
    • 1970-01-01
    相关资源
    最近更新 更多