WPF中TextBoxes的焦点相关文本更改

时间:2010-06-12 11:30:34

标签: c# .net wpf mvvm textbox

我正在使用MVVM模式在WPF中编写应用程序,并且经常使用TextBox es。 我不想使用标签让用户知道文本框的用途,即我想要这样的内容:

<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的任何一个实现此类行为并使其可重复使用?代码是受欢迎的但不是必需的;对做什么的描述就足够了。

提前谢谢。

3 个答案:

答案 0 :(得分:7)

这是一种我认为正是您正在寻找的风格,而且它是纯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

答案 1 :(得分:1)

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

答案 2 :(得分:1)

放大我对使用装饰品的建议。

Adorner基本上是一个在自己的图层上渲染的元素,它出现在另一个元素上/周围。例如,如果在绑定中实现验证,则装饰无效控件的红色框是一个装饰器 - 它不是控件的一部分,它可以(并且)应用于所有类型的控件。有关简单但明确的示例,请参阅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的值为空字符串,那么应该是什么行为?)