Фокус-зависимое изменение текста для текстовых полей в WPF - PullRequest
4 голосов
/ 12 июня 2010

Я пишу приложение в WPF с использованием MVVM-шаблона и очень часто буду использовать TextBox es.Я не хочу использовать ярлыки, чтобы пользователь знал, для чего предназначено текстовое поле, то есть я не хочу что-то вроде этого:

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

Вместо этого я бынапример, TextBox содержит свой собственный ярлык.Статически это можно выразить так:

<TextBox>Name</TextBox>

Если курсор отображается в текстовом поле, то есть TextBox получает фокус, я хочу, чтобы текст описания исчез.Если TextBox оставлено пустым и оно теряет фокус, текст описания должен отображаться снова.Это похоже на текстовое поле поиска StackOverflow или Firefox.(пожалуйста, скажите мне, если вы не уверены, что я имею в виду).

Одна метка TextBox может измениться во время выполнения, в зависимости, например, от выбранного элемента ComboBox или значения в моей ViewModel.(Это похоже на поиск TextBox в Firefox, если вы выбираете Google из меню поисковых систем, ярлык TextBox изменится на «Google», если вы выберете «Yahoo» и его значение будет «Yahoo»).Таким образом, я хочу иметь возможность связывать содержимое метки.

Учтите, что у меня уже может быть Привязка к Text -Свойству TextBox.

Как реализовать такое поведение и сделать его многоразовым для любого из моих TextBox?Код приветствуется, но не нужен;описания того, что нужно сделать, достаточно.

Заранее спасибо.

Ответы [ 3 ]

7 голосов
/ 13 июня 2010

Вот стиль, который я думаю, именно то, что вы ищете, и это чистый 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 голос
/ 13 июня 2010

Чтобы усилить моё предложение об использовании рекламного элемента.

Рекламный элемент - это элемент, отображаемый на собственном слое, который появляется поверх или вокруг другого элемента.Например, если вы реализуете валидацию в привязке, красное поле, которое украшает недопустимый элемент управления, является украшением - оно не является частью элемента управления и может (и применяется) ко всем видам элементов управления.См. раздел Adorners документации WPF для простого, но ясного примера.

Я подумал об Adorner по нескольким причинам.Основным является то, что описываемое вами поведение не обязательно должно быть ограничено TextBox.Например, вы можете захотеть, чтобы ComboBox демонстрировал такое же поведение.Внедрение Adorner даст вам согласованный способ реализации этой функциональности в нескольких элементах управления (хотя это не имеет смысла, например, в CheckBox или ProgressBar).Во-вторых, вам не нужно ничего делать с базовым элементом управления более тщательно, чем реализовывать триггеры для отображения и скрытия Adorner в ответ на события фокуса.Украсить это немного сложно, но стоит знать, как это сделать.

Все это говорит, что мне нравится ответ mattjf гораздо больше, чем мой.Единственные недостатки, которые я вижу при таком подходе: 1) Он работает только с TextBox;вам нужно внедрять новую версию стиля каждый раз, когда вы хотите использовать подход на другом элементе управления, 2) Возможно, я просто участвую в магическом мышлении, но каждый раз, когда я использовал свойство Tag в WinForms, оно сообщало мнеоднажды я научился слушать) что я строил что-то хрупкое.Я не знаю наверняка, что это также верно и для WPF, но держу пари, что так оно и есть.

Мой комментарий по использованию свойства bound Text, вероятно, нуждается в усилении.Если вы используете свойство Text для хранения метки поля, то у вас есть ряд трудно решаемых проблем.Во-первых, поскольку это связанное свойство, изменение его значения в TextBox изменит его в источнике.Итак, теперь вашему источнику нужно знать много информации о состоянии пользовательского интерфейса - в данный момент фокус находится на элементе управления?Если значение свойства Text равно Foo, означает ли это, что метка имеет значение Foo или пользователь ввел Foo?Вероятно, есть способы, которыми вы можете управлять этим, но лучший способ управлять этим - не делать этого.

(Еще одна проблема с этой парадигмой: каким должно быть поведение, если пользователь хочет значение TextBox, которое будет пустой строкой?)

1 голос
/ 12 июня 2010

Вы можете получить от TextBox и реализовать свое поведение.TextBox предлагает события GotFocus / LostFocus (или методы OnGotFocus / OnLostFocus соответственно), которые должны помочь.Вам также следует рассмотреть возможность предложения нового DepedencyProperty, чтобы вы могли определить текст по умолчанию в xaml и связать его с другими элементами управления / ресурсами и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...