【问题标题】:Focus-dependent text change for TextBoxes in WPFWPF 中文本框的焦点相关文本更改
【发布时间】:2010-06-12 11:30:34
【问题描述】:

我正在使用 MVVM 模式在 WPF 中编写一个应用程序,并且确实经常使用TextBoxes。 我不想为用户使用标签来了解用户文本框的用途,即我想要这样的东西:

<TextBlock> Name: </TextBlock>
<TextBox />

相反,我希望TextBox 包含自己的标签。静态地,你可以这样表达:

<TextBox>Name</TextBox>

如果光标显示在文本框中,即TextBox 获得焦点,我希望描述文本消失。如果TextBox 留空并失去焦点,则应再次显示描述文本。它类似于 StackOverflow 或 Firefox 的搜索文本框。 (如果您不确定我的意思,请告诉我)。

一个 TextBox 的标签可能会在运行时更改,具体取决于例如ComboBox 的选定元素或我的 ViewModel 中的值。 (就像在 Firefox 的搜索 TextBox 中,如果您从搜索引擎菜单中选择 google,则 TextBox 的标签将更改为“Google”,如果您选择“Yahoo”,则其设置为“Yahoo”)。因此我希望能够绑定标签的内容。

考虑到我可能已经在Text 属性上绑定了TextBox

如何实现这样的行为并使其可重用用于我的任何TextBox?代码是受欢迎的,但不是必需的;对要做什么的描述就足够了。

提前谢谢你。

【问题讨论】:

    标签: c# .net wpf mvvm textbox


    【解决方案1】:

    我认为这正是您正在寻找的风格,它是纯 XAML。

    <Style x:Key="WatermarkTextBox" TargetType="{x:Type TextBox}">
         <Setter Property="Template">
              <Setter.Value>
                   <ControlTemplate TargetType="{x:Type TextBox}">
                        <Grid>
                             <Border x:Name="BorderBase" Background="White" BorderThickness="1.4,1.4,1,1" BorderBrush="Silver"> 
                                 <Label x:Name="TextPrompt" 
                                    Content="{Binding RelativeSource={RelativeSource  Mode=TemplatedParent}, Path=Tag}" 
                                    Background="{TemplateBinding Background}" Visibility="Collapsed" 
                                    Focusable="False" Foreground="Silver"/>
                             </Border>
                             <ScrollViewer Margin="0" x:Name="PART_ContentHost" Foreground="Black"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                             <MultiTrigger>
                                  <MultiTrigger.Conditions>
                                       <Condition Property="IsFocused" Value="False"/>
                                       <Condition Property="Text" Value=""/>
                                  </MultiTrigger.Conditions>
                                  <Setter Property="Visibility" TargetName="TextPrompt" Value="Visible"/>
                             </MultiTrigger>
                             <Trigger Property="IsFocused" Value="True">
                                  <Setter Property="BorderBrush" TargetName="BorderBase" Value="Black"/>
                             </Trigger>
                             <Trigger Property="IsEnabled" Value="False">
                                  <Setter Property="Foreground" Value="DimGray" />
                             </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
               </Setter.Value>
          </Setter>
     </Style>
    

    用法是:

    <TextBox Style="{StaticResource WatermarkTextBox}" Tag="Full Name"/>
    

    其中 Tag 是您要显示的帮助消息。

    您可以清理这种样式以供自己使用,但最重要的部分是控制隐藏/显示帮助文本的部分。

    同样值得注意的是,已经有一个 DependencyObject 可用于存储帮助文本,因此您无需使用此方法创建自己的。

    FrameworkElement.Tag 可用于保存有关此元素的任意信息。这就是我们设置 Tag 属性的原因:

    http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.tag.aspx

    【讨论】:

    • 谢谢!这有很大帮助。我不知道这种文本框被称为水印文本框。在谷歌中输入此内容后,我发现了更多信息。只需将 Label 设置为 Border 子级,您的示例就可以正常工作。
    • 很高兴它有帮助。我花了很多时间寻找类似的东西。
    • 还有一个问题:如果我在 TextBox 的 Tag 属性上设置绑定,则标签的值不会在绑定的 tag 属性更改时立即更新。有什么想法吗?
    • 经过几次测试后,我注意到绑定 Tag 更改了 TemplateBinding 和 Binding 的 DataContext。您必须明确告诉在哪里搜索 Tag 属性。我修改了您的答案,现在它适用于所有情况。
    【解决方案2】:

    您可以从TextBox 派生并实施您的行为。 TextBox 提供了事件GotFocus/LostFocus(或方法OnGotFocus/OnLostFocus),应该会有所帮助。您还应该考虑提供一个新的 DepedencyProperty,以便您可以在 xaml 中定义默认文本并将其绑定到其他控件/资源等。

    【讨论】:

    • 此外,是否应该将其实现为装饰器似乎值得调查,这样您就不会对绑定的 Text 属性感到困惑。
    • @Robert:你能解释一下 Adorner 是什么,做什么以及它如何适应这种情况吗? Text 属性会出现什么问题?也许你可以写一个答案......我将不胜感激。
    【解决方案3】:

    为了详细说明我关于使用装饰器的建议。

    装饰器基本上是一个元素,呈现在自己的层上,出现在另一个元素的上方/周围。例如,如果您在绑定中实现验证,则装饰无效控件的红色框是装饰器 - 它不是控件的一部分,它可以(并且现在)应用于各种控件。请参阅 WPF 文档的 the Adorners 部分,了解一个简单但清晰的示例。

    出于几个原因,我想到了 Adorner。主要的一个是您所描述的行为可能不一定限于TextBox。例如,您可能希望ComboBox 表现出相同的行为。实现 Adorner 将为您提供一种跨多个控件实现此功能的一致方式(尽管它在 CheckBoxProgressBar 中没有意义)。第二个是你不必对底层控件做任何比实现触发器来显示和隐藏 Adorner 以响应焦点事件更复杂的事情。装饰器实施起来有点麻烦,但值得知道如何实施。

    说了这么多,我喜欢 mattjf 的回答比我喜欢我的要多得多。我看到这种方法的唯一缺点是 1) 它只适用于TextBox;每次您想在另一个控件上使用该方法时,您都需要实施新版本的样式,2)我可能只是在从事神奇的思考,但每次我在 WinForms 中使用 Tag 属性时它告诉我(一旦我学会倾听)我正在建造一些脆弱的东西。我不确定 WPF 中是否也是如此,但我敢打赌。

    我对使用绑定Text 属性的评论可能需要放大。如果您使用 Text 属性来存储字段标签,那么您将遇到许多难以解决的问题。首先,由于它是一个绑定属性,因此在TextBox 中更改其值将在源中更改它。所以现在你的源需要知道很多关于 UI 状态的信息——控件当前是否有焦点?如果Text 属性的值为Foo,这是否意味着标签为Foo,或者用户键入了Foo?您可能有多种方法可以管理它,但最好的方法是不必这样做。

    (此范例的另一个问题:如果用户希望 TextBox 的值是空字符串,应该是什么行为?)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-14
      • 1970-01-01
      • 2020-09-26
      • 1970-01-01
      • 1970-01-01
      • 2010-10-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多