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