ฉันจะเพิ่มข้อความคำใบ้ในกล่องข้อความ WPF ได้อย่างไร


108

ตัวอย่างเช่น Facebook มีข้อความคำใบ้ "ค้นหา" ในกล่องข้อความค้นหาเมื่อกล่องข้อความว่างเปล่า

จะบรรลุสิ่งนี้ด้วยกล่องข้อความ WPF ได้อย่างไร ??

ช่องข้อความค้นหาของ Facebook


2
ลองค้นหาคำว่า "cue banner"
ภาษาเริ่มต้น

@MAKKAM บทความ MSDN นี้กล่าวถึง แต่ไม่ได้แสดงให้เห็นว่ามันทำอย่างไร
Louis Rhys


1
ฉันจะไม่เรียกสิ่งที่คุณกำลังขอ 'ข้อความใบ้' ข้อความคำใบ้ถึงฉันคือหน้าต่างป๊อปอัป ไม่มีเลยฉันพบคำถามนี้เมื่อต้องการตั้งค่าข้อความตัวยึดตำแหน่ง และคำตอบด้านล่างช่วยฉัน
สตีฟ

1
ที่เรียกว่าลายน้ำ
Blechdose

คำตอบ:


157

คุณสามารถทำสิ่งนี้ได้ง่ายขึ้นด้วย a VisualBrushและทริกเกอร์บางอย่างในStyle:

<TextBox>
    <TextBox.Style>
        <Style TargetType="TextBox" xmlns:sys="clr-namespace:System;assembly=mscorlib">
            <Style.Resources>
                <VisualBrush x:Key="CueBannerBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
                    <VisualBrush.Visual>
                        <Label Content="Search" Foreground="LightGray" />
                    </VisualBrush.Visual>
                </VisualBrush>
            </Style.Resources>
            <Style.Triggers>
                <Trigger Property="Text" Value="{x:Static sys:String.Empty}">
                    <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                </Trigger>
                <Trigger Property="Text" Value="{x:Null}">
                    <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                </Trigger>
                <Trigger Property="IsKeyboardFocused" Value="True">
                    <Setter Property="Background" Value="White" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>

เพื่อเพิ่มความสามารถในการใช้ซ้ำได้Styleคุณยังสามารถสร้างชุดคุณสมบัติที่แนบมาเพื่อควบคุมข้อความแบนเนอร์คิวสีการวางแนว ฯลฯ


1
ใช้ IsMouseCaptured แทน IsKeyboardFocused นั่นคือวิธีที่แบนเนอร์คิวจริงตอบสนอง
Monstieur

5
หากใครสงสัยว่าจะใช้คุณสมบัติที่แนบมาเพื่อเพิ่มความสามารถในการนำสไตล์กลับมาใช้ใหม่ได้อย่างไรโปรดดู: stackoverflow.com/a/650620/724944
ท่อง

@Kurian IsMouseCaptured จะทำให้คิวหายไปก็ต่อเมื่อคุณคลิกเมาส์ แต่จะปรากฏขึ้นอีกครั้งเมื่อคุณปล่อยปุ่มเมาส์ มันดูไม่ดีเลย IsMouseOver ก็ไม่ดีเช่นกัน (แป้นพิมพ์มีโฟกัส แต่ตัวชี้เมาส์อยู่ที่อื่น => แสดงคิว) แบนเนอร์คิวส่วนใหญ่ใช้ IsKeyboardFocused (เช่น Facebook) และฉันคิดว่ามันใช้ได้ ทางเลือกอื่นคือการใช้ทริกเกอร์ทั้งสอง: (IsMouseOver หรือ IsKeyboardFocused)
เล่น

23
วิธีแก้ปัญหาควรเป็นคำแนะนำ = "โปรดป้อนข้อความของคุณ" ไม่ใช่ 20 องค์ประกอบ ... อนิจจาสิ่งนี้ไม่ได้รับการสนับสนุนโดยกล่องข้อความที่สร้างขึ้นในตำนาน ...
Mzn

8
แม้ว่าวิธีนี้อาจใช้ได้สำหรับเงื่อนไขเริ่มต้น แต่ก็ไม่ได้ผลเมื่อกล่องข้อความของคุณมีแปรงพื้นหลังอยู่แล้วหรือพื้นหลังแบบฟอร์มไม่ได้มีสีเดียวกันเช่นกล่องข้อความ
LWChris

56

นี่เป็นวิธีง่ายๆของฉันซึ่งดัดแปลงมาจาก Microsoft ( https://code.msdn.microsoft.com/windowsapps/How-to-add-a-hint-text-to-ed66a3c6 )

    <Grid Background="White" HorizontalAlignment="Right" VerticalAlignment="Top"  >
        <!-- overlay with hint text -->
        <TextBlock Margin="5,2" MinWidth="50" Text="Suche..." 
                   Foreground="LightSteelBlue" Visibility="{Binding ElementName=txtSearchBox, Path=Text.IsEmpty, Converter={StaticResource MyBoolToVisibilityConverter}}" IsHitTestVisible="False"/>
        <!-- enter term here -->
        <TextBox MinWidth="50" Name="txtSearchBox" Background="Transparent" />
    </Grid>

แนวทางที่น่าสนใจฉันคงไม่ได้คิดไปเองในทันที
itsmatt

ดีและเรียบง่าย :)
shmulik.r

4
วิธีนี้ใช้งานได้ดีเป็นพิเศษหากคุณตั้งค่า TextBlock เป็นIsHitTestVisible="False"
Mage Xy

1
@MageXy หมายถึง TextBlock ตัวแรก (คำใบ้)
เฟลิกซ์

วิธีนี้ใช้งานได้ดีหากคุณต้องการผูกข้อความคำใบ้กับคุณสมบัติ
PeterB

11

แล้วการใช้MaterialDesign HintAssist ล่ะ? ฉันกำลังใช้สิ่งนี้ซึ่งคุณสามารถเพิ่มคำใบ้ลอยได้ด้วย:

<TextBox Width="150" Height="40" Text="hello" materialDesign:HintAssist.Hint="address"  materialDesign:HintAssist.IsFloating="True"></TextBox>

ฉันติดตั้ง Material Design ด้วย Nuget Package มีคู่มือการติดตั้งในลิงค์เอกสาร


2
ห้องสมุดที่มีประโยชน์มาก
AlexF11

9

ทำในโค้ดหลังโดยตั้งค่าสีข้อความในตอนแรกเป็นสีเทาและเพิ่มตัวจัดการเหตุการณ์เพื่อให้ได้มาซึ่งและสูญเสียโฟกัสของแป้นพิมพ์

TextBox tb = new TextBox();
tb.Foreground = Brushes.Gray;
tb.Text = "Text";
tb.GotKeyboardFocus += new KeyboardFocusChangedEventHandler(tb_GotKeyboardFocus);
tb.LostKeyboardFocus += new KeyboardFocusChangedEventHandler(tb_LostKeyboardFocus);

จากนั้นตัวจัดการเหตุการณ์:

private void tb_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    if(sender is TextBox)
    {
        //If nothing has been entered yet.
        if(((TextBox)sender).Foreground == Brushes.Gray)
        {
            ((TextBox)sender).Text = "";
            ((TextBox)sender).Foreground = Brushes.Black;
        }
    }
}


private void tb_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    //Make sure sender is the correct Control.
    if(sender is TextBox)
    {
        //If nothing was entered, reset default text.
        if(((TextBox)sender).Text.Trim().Equals(""))
        {
            ((TextBox)sender).Foreground = Brushes.Gray;
            ((TextBox)sender).Text = "Text";
        }
    }
}

7
-1 สำหรับการทำในโค้ดด้านหลัง: ถ่วงการควบคุมและมีโอกาสสูงที่สิ่งนี้จะรบกวนตรรกะการควบคุมอื่น ๆ หากไม่เป็นเช่นนั้นในอนาคต
Alexei

5

คุณต้องสร้างตัวควบคุมแบบกำหนดเองโดยการสืบทอดกล่องข้อความ ลิงค์ด้านล่างมีตัวอย่างที่ยอดเยี่ยมเกี่ยวกับตัวอย่างช่องข้อความค้นหา โปรดดูที่นี่

http://davidowens.wordpress.com/2009/02/18/wpf-search-text-box/


2
คำตอบแบบลิงก์อย่างเดียวเป็นสิ่งที่ท้อใจ
Josh Noe

4

คุณสามารถทำได้ด้วยวิธีง่ายๆ แนวคิดคือการวางป้ายกำกับไว้ที่เดียวกับกล่องข้อความของคุณ ป้ายกำกับของคุณจะมองเห็นได้หากกล่องข้อความไม่มีข้อความและไม่มีโฟกัส

 <Label Name="PalceHolder"  HorizontalAlignment="Left" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Height="40" VerticalAlignment="Top" Width="239" FontStyle="Italic"  Foreground="BurlyWood">PlaceHolder Text Here
  <Label.Style>
    <Style TargetType="{x:Type Label}">
      <Setter Property="Visibility" Value="Hidden"/>
      <Style.Triggers>
        <MultiDataTrigger>
          <MultiDataTrigger.Conditions>
            <Condition Binding ="{Binding ElementName=PalceHolder, Path=Text.Length}" Value="0"/>
            <Condition Binding ="{Binding ElementName=PalceHolder, Path=IsFocused}" Value="False"/>
          </MultiDataTrigger.Conditions>
          <Setter Property="Visibility" Value="Visible"/>
        </MultiDataTrigger>
      </Style.Triggers>
    </Style>
  </Label.Style>
</Label>
<TextBox  Background="Transparent" Name="TextBox1" HorizontalAlignment="Left" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Height="40"TextWrapping="Wrap" Text="{Binding InputText,Mode=TwoWay}" VerticalAlignment="Top" Width="239" />

โบนัส: หากคุณต้องการมีค่าเริ่มต้นสำหรับกล่องข้อความของคุณโปรดตรวจสอบให้แน่ใจว่าได้ตั้งค่าหลังจากส่งข้อมูลแล้ว (ตัวอย่างเช่น "InputText" = "PlaceHolder Text Here" หากว่างเปล่า)


3

ครั้งหนึ่งฉันตกอยู่ในสถานการณ์เดียวกันฉันแก้ไขด้วยวิธีต่อไปนี้ ฉันทำตามข้อกำหนดของกล่องคำใบ้เท่านั้นคุณสามารถทำให้มันโต้ตอบได้มากขึ้นโดยการเพิ่มเอฟเฟกต์และสิ่งอื่น ๆ ในกิจกรรมอื่น ๆ เช่นการโฟกัสเป็นต้น

WPF CODE (ฉันได้ลบรูปแบบเพื่อให้อ่านได้)

<Grid Margin="0,0,0,0"  Background="White">
    <Label Name="adminEmailHint" Foreground="LightGray" Padding="6"  FontSize="14">Admin Email</Label>
    <TextBox Padding="4,7,4,8" Background="Transparent" TextChanged="adminEmail_TextChanged" Height="31" x:Name="adminEmail" Width="180" />
</Grid>
<Grid Margin="10,0,10,0" Background="White" >
    <Label Name="adminPasswordHint" Foreground="LightGray" Padding="6"  FontSize="14">Admin Password</Label>
    <PasswordBox Padding="4,6,4,8" Background="Transparent" PasswordChanged="adminPassword_PasswordChanged" Height="31" x:Name="adminPassword" VerticalContentAlignment="Center" VerticalAlignment="Center" Width="180" FontFamily="Helvetica" FontWeight="Light" FontSize="14" Controls:TextBoxHelper.Watermark="Admin Password"  FontStyle="Normal" />
</Grid>

รหัส C #

private void adminEmail_TextChanged(object sender, TextChangedEventArgs e)
    {
        if(adminEmail.Text.Length == 0)
        {
            adminEmailHint.Visibility = Visibility.Visible;
        }
        else
        {
            adminEmailHint.Visibility = Visibility.Hidden;
        }
    }

private void adminPassword_PasswordChanged(object sender, RoutedEventArgs e)
    {
        if (adminPassword.Password.Length == 0)
        {
            adminPasswordHint.Visibility = Visibility.Visible;
        }
        else
        {
            adminPasswordHint.Visibility = Visibility.Hidden;
        }
    }

2

อีกแนวทาง ;-)

สิ่งนี้ใช้ได้กับPasswordBox. หากคุณต้องการที่จะใช้กับTextBoxเพียงแค่แลกเปลี่ยนกับPasswordChangedTextChanged

XAML:

<Grid>
    <!-- overlay with hint text -->
    <TextBlock Margin="5,2"
                Text="Password"
                Foreground="Gray"
                Name="txtHintPassword"/>
    <!-- enter user here -->
    <PasswordBox Name="txtPassword"
                Background="Transparent"
                PasswordChanged="txtPassword_PasswordChanged"/>
</Grid>

รหัสหลัง:

private void txtPassword_PasswordChanged(object sender, RoutedEventArgs e)
{
    txtHintPassword.Visibility = Visibility.Visible;
    if (txtPassword.Password.Length > 0)
    {
        txtHintPassword.Visibility = Visibility.Hidden;
    }
}

ทางออกที่ดีที่สุดและง่ายที่สุดที่ฉันพบ !! ขอบคุณ !!
สตีฟ

คุณควรเพิ่มหรือไม่IsHitTestVisible="False"เพื่อให้แน่ใจว่าผู้ใช้คลิกที่ใดก็ตามจะถูกส่งต่อไปยังการควบคุมที่ถูกต้อง?
JTIM

@JTIM แน่นอนคุณสามารถทำได้ แต่เนื่องจาก PasswordBox เป็นตัวควบคุมสุดท้ายใน XAM L จึงควร "อยู่ด้านบน" ของดัชนี Z -> การคลิกจะเน้นที่รหัสผ่านเสมอเช่นกัน AFAIK TextBlock จึงไม่สามารถโฟกัสได้ - > กล่องข้อความ (หรือกล่องรหัสผ่าน) จะได้รับโฟกัส เนื่องจากคำตอบคือ 3 ขวบฉันไม่แน่ใจ 100% ว่ามันทำงานอย่างไร แต่มันก็เป็นเช่นนั้น ;-)
Mat

1

อีกวิธีหนึ่งคือการใช้ชุดเครื่องมือ WPF เช่น MahApps.Metro มีคุณสมบัติที่ดีมากมายเช่นลายน้ำกล่องข้อความ:

Controls:TextBoxHelper.Watermark="Search..."

โปรดดูhttp://mahapps.com/controls/textbox.html


0

ฉันใช้เหตุการณ์โฟกัสได้และหายไป:

Private Sub txtSearchBox_GotFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles txtSearchBox.GotFocus
    If txtSearchBox.Text = "Search" Then
        txtSearchBox.Text = ""
    Else

    End If

End Sub

Private Sub txtSearchBox_LostFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles txtSearchBox.LostFocus
    If txtSearchBox.Text = "" Then
        txtSearchBox.Text = "Search"
    Else

    End If
End Sub

ใช้งานได้ดี แต่ข้อความยังคงเป็นสีเทา ต้องการทำความสะอาด ฉันใช้ VB.NET


0
  <Grid>
    <TextBox Name="myTextBox"/>
    <TextBlock>
        <TextBlock.Style>
            <Style TargetType="TextBlock">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding ElementName=myTextBox, Path=Text.IsEmpty}" Value="True">
                        <Setter Property="Text" Value="Prompt..."/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
    </TextBlock>
</Grid>

0

นั่นคือสิ่งที่ฉันใช้:

<ControlTemplate>
    <Grid>
        <Grid.Resources>
            <!--Define look / layout for both TextBoxes here. I applied custom Padding and BorderThickness for my application-->
            <Style TargetType="TextBox">
                <Setter Property="Padding" Value="4"/>
                <Setter Property="BorderThickness" Value="2"/>
            </Style>
        </Grid.Resources>

        <TextBox x:Name="TbSearch"/>
        <TextBox x:Name="TbHint" Text="Suche" Foreground="LightGray"
                 Visibility="Hidden" IsHitTestVisible="False" Focusable="False"/>
    </Grid>

    <ControlTemplate.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition SourceName="TbSearch" Property="Text" Value="{x:Static sys:String.Empty}"/>
                <Condition SourceName="TbSearch" Property="IsKeyboardFocused" Value="False"/>
            </MultiTrigger.Conditions>
            <MultiTrigger.Setters>
                <Setter TargetName="TbHint" Property="Visibility" Value="Visible"/>
            </MultiTrigger.Setters>
        </MultiTrigger>

        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition SourceName="TbSearch" Property="Text" Value="{x:Null}"/>
                <Condition SourceName="TbSearch" Property="IsKeyboardFocused" Value="False"/>
            </MultiTrigger.Conditions>
            <MultiTrigger.Setters>
                <Setter TargetName="TbHint" Property="Visibility" Value="Visible"/>
            </MultiTrigger.Setters>
        </MultiTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

คำตอบอื่น ๆ ส่วนใหญ่รวมถึงคำตอบด้านบนมีข้อบกพร่องในความคิดของฉัน

โซลูชันนี้ใช้ได้กับทุกสถานการณ์ XAML บริสุทธิ์สามารถนำกลับมาใช้ใหม่ได้อย่างง่ายดาย


-1

ที่ผมทำสำเร็จนี้ด้วยVisualBrushและเรียกบางอย่างในการแนะนำโดย:Stylesellmeadog

<TextBox>
        <TextBox.Style>
            <Style TargetType="TextBox" xmlns:sys="clr-namespace:System;assembly=mscorlib">
                <Style.Resources>
                    <VisualBrush x:Key="CueBannerBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
                        <VisualBrush.Visual>
                            <Label Content="Search" Foreground="LightGray" />
                        </VisualBrush.Visual>
                    </VisualBrush>
                </Style.Resources>
                <Style.Triggers>
                    <Trigger Property="Text" Value="{x:Static sys:String.Empty}">
                        <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                    </Trigger>
                    <Trigger Property="Text" Value="{x:Null}">
                        <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                    </Trigger>
                    <Trigger Property="IsKeyboardFocused" Value="True">
                        <Setter Property="Background" Value="White" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>

@sellmeadog: แอปพลิเคชันทำงานอยู่ bt Design ไม่โหลด ... เกิดข้อผิดพลาดต่อไปนี้: การอ้างอิงประเภทไม่ชัดเจน ประเภทที่ชื่อว่า 'StaticExtension' เกิดขึ้นในอย่างน้อยสองเนมสเปซ 'MS.Internal.Metadata.ExposedTypes.Xaml' และ 'System.Windows.Markup' พิจารณาปรับแอ็ตทริบิวต์ XmlnsDefinition แอสเซมบลี 'm โดยใช้. net 3.5


แก้ไขปัญหาโดยเปลี่ยน<Trigger Property="Text" Value="{x:Static sys:String.Empty}">เป็น<Trigger Property="Text" Value="">
SUHAIL AG

6
ดูเหมือนจะเป็นการตอบกลับโพสต์อื่น โปรดโพสต์เฉพาะคำตอบที่สมบูรณ์สำหรับคำถามเป็นคำตอบ
Drew Gaynor

4
หากคุณคิดว่าคำตอบของ @ sellmeadog นั้นเกือบถูกต้องให้ลองแก้ไขแทนการโพสต์คำตอบใหม่โดยแทบไม่แตกต่างกัน
0xBADF00D

-11

สำหรับ WPF ไม่มีทาง คุณต้องเลียนแบบมัน ดูตัวอย่างนี้ รอง (โซลูชันที่ไม่สม่ำเสมอ) คือการโฮสต์ตัวควบคุมผู้ใช้ WinForms ที่สืบทอดมาจาก TextBox และส่งข้อความ EM_SETCUEBANNER ไปยังตัวควบคุมการแก้ไข กล่าวคือ.

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam);

private const Int32 ECM_FIRST = 0x1500;
private const Int32 EM_SETCUEBANNER = ECM_FIRST + 1;

private void SetCueText(IntPtr handle, string cueText) {
    SendMessage(handle, EM_SETCUEBANNER, IntPtr.Zero, Marshal.StringToBSTR(cueText));
}

public string CueText {
    get {
        return m_CueText;
    } 
    set {
        m_CueText = value;
        SetCueText(this.Handle, m_CueText);
}

นอกจากนี้หากคุณต้องการโฮสต์แนวทางการควบคุม WinForm ฉันมีกรอบงานที่รวมการใช้งานนี้ไว้แล้วที่เรียกว่า BitFlex Framework ซึ่งคุณสามารถดาวน์โหลดได้ฟรีที่นี่ดาวน์โหลดได้ฟรีที่นี่

นี่คือบทความเกี่ยวกับ BitFlex หากคุณต้องการข้อมูลเพิ่มเติม คุณจะเริ่มพบว่าหากคุณต้องการมีตัวควบคุมสไตล์ Windows Explorer ที่โดยทั่วไปแล้วสิ่งนี้จะไม่หลุดออกจากกรอบและเนื่องจาก WPF ไม่ทำงานกับที่จับโดยทั่วไปคุณจึงไม่สามารถเขียน wrapper แบบง่ายรอบ ๆ Win32 หรือตัวควบคุมที่มีอยู่อย่างที่คุณทำได้ ด้วย WinForms

ภาพหน้าจอ: ป้อนคำอธิบายภาพที่นี่


1
ว้าวดูแฮ็คไปหน่อย .. ฉันจะทำสิ่งนี้ได้อย่างไรเมื่อสร้างการควบคุมผู้ใช้ด้วย XAML
Louis Rhys

คุณไม่ทำ นี่คือวิธีการทำ ถ้าคุณต้องการห่อหุ้มสิ่งนี้ให้ทำการควบคุมผู้ใช้และคุณสมบัติ CueText แล้วเรียก SetCueText ใน setter
David Anderson

ฉันเดาว่า OP ควรโฮสต์การควบคุม winforms เพื่อใช้แนวทางนี้ หรือมีวิธีรับ textbox handle หรือไม่?
ภาษาเริ่มต้น

ดูเหมือนว่าสิ่งที่สามารถทำได้อย่างเปิดเผยด้วย WPF โดยการผูกกับว่ากล่องข้อความมีโฟกัสหรือไม่เป็นต้น - ตัวอย่างข้างต้นเป็นแนวทางของ WinForms มากกว่าซึ่งจะทำงานใน WPF แต่ไม่ใช่ทางที่ถูกต้อง ทาง.
BrainSlugs83

2
ขออภัยคำตอบนี้ไม่ถูกต้อง ลิงค์ทั้งหมดก็เสียด้วย
Forest Kunecke
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.