2010-06-12 137 views
4

我正在使用MVVM模式在WPF中编写应用程序,并且实际上通常会使用TextBox es。 我不想使用标签的用户知道用户的文本框是什么,也就是我想是这样的:WPF中文本框的焦点相关文本更改

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

相反,我想TextBox遏制它自己的标签。如果将光标显示在文本框中,即TextBox获得焦点,我想说明文字消失

<TextBox>Name</TextBox> 

:静态,你会表达出来是这样的。如果TextBox留空并且失去焦点,则应再次显示说明文字。它类似于StackOverflow的搜索文本框或Firefox的搜索文本框。 (如果你不确定我的意思,请告诉我)。

One TextBox的标签可能在运行时发生变化,依赖于例如一个ComboBox的选定元素或我的ViewModel中的值。 (就像在Firefox的搜索文本框中一样,如果您从搜索引擎菜单中选择谷歌,那么如果您选择“雅虎”将其设置为“雅虎”,那么文本框的标签将更改为“Google”)。因此我希望能够绑定标签的内容。

请考虑我可能已经在Text-属性TextBox上拥有Binding。

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

预先感谢您。

回答

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"/> 

其中标签是要显示帮助消息。

你可以清理这个风格供自己使用,但最重要的部分是哪个控件隐藏/显示帮助文本。

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

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

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

+1

谢谢!这帮了很多。我不知道这种TextBox被称为水印文本框。在google中输入后,我发现了更多信息。只需制作边界的标签子项,并且您的示例工作得很好。 – Simon 2010-06-12 21:25:18

+1

很高兴帮助。我花了很多时间寻找类似的东西。 – mfanto 2010-06-12 21:48:33

+1

剩下的一个问题:如果我在TextBox的Tag属性上设置了绑定,则只要绑定的标签属性发生更改,标签的值就不会更新。有任何想法吗? – Simon 2010-06-12 22:12:13

1

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

+0

此外,现在看来似乎会是值得调查是否或者不这应该作为一个装饰者来实现,这样你就不会被绑定的'Text'属性破坏。 – 2010-06-12 17:33:22

+0

@Robert:你能解释一下Adorner是什么,是否适合这种情况? Text属性可能会发生什么问题?也许你可以写一个答案......我会很感激。 – Simon 2010-06-12 18:24:02

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的值是空字符串)