Browse Source

Add PlaceholderForeground property to TextBox, AutoCompleteBox, CalendarDatePicker, NumericUpDown (#20303)

* Added WatermarkForeground property

* Added samples

* Added unit tests

* Added render tests

* Fix merge issues

* Updated render tests

* Standardize watermark foreground naming

* Pending changes

* More changes

* Use UseFloatingPlaceholder

* Fix tests
pull/20531/merge
Javier Suárez 20 hours ago
committed by GitHub
parent
commit
424863d5ff
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 40
      samples/BindingDemo/MainWindow.xaml
  2. 8
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
  3. 11
      samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml
  4. 4
      samples/ControlCatalog/Pages/ClipboardPage.xaml
  5. 8
      samples/ControlCatalog/Pages/DialogsPage.xaml
  6. 20
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  7. 33
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  8. 6
      samples/ControlCatalog/Pages/ThemePage.axaml
  9. 14
      samples/Generators.Sandbox/Controls/SignUpView.xaml
  10. 14
      samples/IntegrationTestApp/Pages/ScreensPage.axaml
  11. 2
      samples/IntegrationTestApp/Pages/WindowDecorationsPage.axaml
  12. 2
      samples/IntegrationTestApp/Pages/WindowPage.axaml
  13. 2
      samples/SafeAreaDemo/Views/MainView.xaml
  14. 8
      samples/SingleProjectSandbox/MainView.axaml
  15. 75
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
  16. 91
      src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs
  17. 16
      src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
  18. 69
      src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
  19. 105
      src/Avalonia.Controls/TextBox.cs
  20. 5
      src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml
  21. 9
      src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml
  22. 4
      src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml
  23. 4
      src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml
  24. 25
      src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml
  25. 35
      src/Avalonia.Themes.Fluent/Controls/TextBox.xaml
  26. 2
      src/Avalonia.Themes.Fluent/Strings/InvariantResources.xaml
  27. 3
      src/Avalonia.Themes.Simple/Controls/AutoCompleteBox.xaml
  28. 5
      src/Avalonia.Themes.Simple/Controls/CalendarDatePicker.xaml
  29. 2
      src/Avalonia.Themes.Simple/Controls/ManagedFileChooser.xaml
  30. 3
      src/Avalonia.Themes.Simple/Controls/NumericUpDown.xaml
  31. 16
      src/Avalonia.Themes.Simple/Controls/TextBox.xaml
  32. 13
      tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs
  33. 13
      tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs
  34. 15
      tests/Avalonia.Controls.UnitTests/NumericUpDownTests.cs
  35. 55
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
  36. 4
      tests/Avalonia.Generators.Tests/Views/AttachedProps.xml
  37. 4
      tests/Avalonia.Generators.Tests/Views/ControlWithoutWindow.xml
  38. 8
      tests/Avalonia.Generators.Tests/Views/DataTemplates.xml
  39. 16
      tests/Avalonia.Generators.Tests/Views/FieldModifier.xml
  40. 4
      tests/Avalonia.Generators.Tests/Views/NamedControl.xml
  41. 8
      tests/Avalonia.Generators.Tests/Views/NamedControls.xml
  42. 4
      tests/Avalonia.Generators.Tests/Views/NoNamedControls.xml
  43. 12
      tests/Avalonia.Generators.Tests/Views/SignUpView.xml
  44. 4
      tests/Avalonia.Generators.Tests/Views/xNamedControl.xml
  45. 8
      tests/Avalonia.Generators.Tests/Views/xNamedControls.xml
  46. 4
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs
  47. 135
      tests/Avalonia.RenderTests/Controls/TextBoxTests.cs
  48. BIN
      tests/TestFiles/Skia/Controls/TextBox/Placeholder_With_Blue_Foreground.expected.png
  49. BIN
      tests/TestFiles/Skia/Controls/TextBox/Placeholder_With_Default_Foreground.expected.png
  50. BIN
      tests/TestFiles/Skia/Controls/TextBox/Placeholder_With_Red_Foreground.expected.png

40
samples/BindingDemo/MainWindow.xaml

@ -20,22 +20,22 @@
<StackPanel Orientation="Horizontal">
<StackPanel Margin="18" Spacing="4" Width="200">
<TextBlock FontSize="16" Text="Simple Bindings"/>
<TextBox Watermark="Two Way" UseFloatingWatermark="True" Text="{Binding Path=StringValue}" Name="first"/>
<TextBox Watermark="Two Way (LostFocus)" UseFloatingWatermark="True" Text="{Binding Path=StringValue, UpdateSourceTrigger=LostFocus}"/>
<TextBox Watermark="One Way" UseFloatingWatermark="True" Text="{Binding Path=StringValue, Mode=OneWay}"/>
<TextBox Watermark="One Time" UseFloatingWatermark="True" Text="{Binding Path=StringValue, Mode=OneTime}"/>
<TextBox PlaceholderText="Two Way" UseFloatingPlaceholder="True" Text="{Binding Path=StringValue}" Name="first"/>
<TextBox PlaceholderText="Two Way (LostFocus)" UseFloatingPlaceholder="True" Text="{Binding Path=StringValue, UpdateSourceTrigger=LostFocus}"/>
<TextBox PlaceholderText="One Way" UseFloatingPlaceholder="True" Text="{Binding Path=StringValue, Mode=OneWay}"/>
<TextBox PlaceholderText="One Time" UseFloatingPlaceholder="True" Text="{Binding Path=StringValue, Mode=OneTime}"/>
<!-- Removed due to #2983: reinstate when that's fixed.
<TextBox Watermark="One Way to Source" UseFloatingWatermark="True" Text="{Binding Path=StringValue, Mode=OneWayToSource}"/>
<TextBox Placeholder="One Way to Source" UseFloatingPlaceholder="True" Text="{Binding Path=StringValue, Mode=OneWayToSource}"/>
-->
</StackPanel>
<StackPanel Margin="18" Spacing="4" Width="200">
<TextBlock FontSize="16" Text="Collection Bindings"/>
<TextBox Watermark="Items[1].Value" UseFloatingWatermark="True" Text="{Binding Path=Items[1].Value}"/>
<TextBox PlaceholderText="Items[1].Value" UseFloatingPlaceholder="True" Text="{Binding Path=Items[1].Value}"/>
<Button Command="{Binding ShuffleItems}">Shuffle</Button>
</StackPanel>
<StackPanel Margin="18" Spacing="4" Width="200">
<TextBlock FontSize="16" Text="Negated Bindings"/>
<TextBox Watermark="Boolean String" UseFloatingWatermark="True" Text="{Binding Path=BooleanString}"/>
<TextBox PlaceholderText="Boolean String" UseFloatingPlaceholder="True" Text="{Binding Path=BooleanString}"/>
<CheckBox IsChecked="{Binding !BooleanString}">!BooleanString</CheckBox>
<CheckBox IsChecked="{Binding !!BooleanString}">!!BooleanString</CheckBox>
</StackPanel>
@ -43,24 +43,24 @@
<StackPanel Orientation="Horizontal">
<StackPanel Margin="18" Spacing="4" Width="200" HorizontalAlignment="Left">
<TextBlock FontSize="16" Text="Numeric Bindings"/>
<TextBox Watermark="Double" UseFloatingWatermark="True" Text="{Binding Path=DoubleValue, Mode=TwoWay}"/>
<TextBox PlaceholderText="Double" UseFloatingPlaceholder="True" Text="{Binding Path=DoubleValue, Mode=TwoWay}"/>
<TextBlock Text="{Binding Path=DoubleValue}"/>
<ProgressBar Maximum="10" Value="{Binding DoubleValue}"/>
</StackPanel>
<StackPanel Margin="18" Spacing="4" Width="200" HorizontalAlignment="Left">
<TextBlock FontSize="16" Text="Binding Sources"/>
<TextBox Watermark="Value of first TextBox" UseFloatingWatermark="True"
<TextBox PlaceholderText="Value of first TextBox" UseFloatingPlaceholder="True"
Text="{Binding #first.Text, Mode=TwoWay}"/>
<TextBox Watermark="Value of SharedItem.StringValue" UseFloatingWatermark="True"
<TextBox PlaceholderText="Value of SharedItem.StringValue" UseFloatingPlaceholder="True"
Text="{Binding Value, Source={StaticResource SharedItem}, Mode=TwoWay, DataType={x:Type vm:MainWindowViewModel+TestItem, x:TypeArguments=x:String}}"/>
<TextBox Watermark="Value of SharedItem.StringValue (duplicate)" UseFloatingWatermark="True"
<TextBox PlaceholderText="Value of SharedItem.StringValue (duplicate)" UseFloatingPlaceholder="True"
Text="{Binding Value, Source={StaticResource SharedItem}, Mode=TwoWay, DataType={x:Type vm:MainWindowViewModel+TestItem, x:TypeArguments=x:String}}"/>
</StackPanel>
<StackPanel Margin="18" Spacing="4" Width="200" HorizontalAlignment="Left">
<TextBlock FontSize="16" Text="Scheduler"/>
<TextBox Watermark="Background Thread" Text="{Binding CurrentTime, Mode=OneWay}"/>
<TextBox PlaceholderText="Background Thread" Text="{Binding CurrentTime, Mode=OneWay}"/>
<TextBlock FontSize="16" Text="Stream Operator"/>
<TextBox Watermark="StreamOperator" Text="{CompiledBinding CurrentTimeObservable^, Mode=OneWay}"/>
<TextBox PlaceholderText="StreamOperator" Text="{CompiledBinding CurrentTimeObservable^, Mode=OneWay}"/>
</StackPanel>
</StackPanel>
</StackPanel>
@ -91,19 +91,19 @@
</TabItem>
<TabItem Header="Property Validation">
<StackPanel Orientation="Horizontal">
<StackPanel Margin="18" Spacing="4" MinWidth="200" DataContext="{Binding ExceptionDataValidation}">
<StackPanel Margin="18" Spacing="4" MinWidth="200" DataContext="{Binding ExceptionDataValidation}">
<TextBlock FontSize="16" Text="Exception Validation"/>
<TextBox Watermark="Less Than 10" UseFloatingWatermark="True" Text="{Binding Path=LessThan10}"/>
<TextBox PlaceholderText="Less Than 10" UseFloatingPlaceholder="True" Text="{Binding Path=LessThan10}"/>
</StackPanel>
<StackPanel Margin="18" Spacing="4" MinWidth="200" DataContext="{Binding IndeiDataValidation}">
<TextBlock FontSize="16" Text="INotifyDataErrorInfo Validation"/>
<TextBox Watermark="Maximum" UseFloatingWatermark="True" Text="{Binding Path=Maximum}"/>
<TextBox Watermark="Value" UseFloatingWatermark="True" Text="{Binding Path=Value}"/>
<TextBox PlaceholderText="Maximum" UseFloatingPlaceholder="True" Text="{Binding Path=Maximum}"/>
<TextBox PlaceholderText="Value" UseFloatingPlaceholder="True" Text="{Binding Path=Value}"/>
</StackPanel>
<StackPanel Margin="18" Spacing="4" MinWidth="200" DataContext="{Binding DataAnnotationsValidation}">
<StackPanel Margin="18" Spacing="4" MinWidth="200" DataContext="{Binding DataAnnotationsValidation}">
<TextBlock FontSize="16" Text="Data Annotations Validation"/>
<TextBox Watermark="Phone #" UseFloatingWatermark="True" Text="{Binding PhoneNumber}"/>
<TextBox Watermark="Less Than 10" UseFloatingWatermark="True" Text="{Binding Path=LessThan10}"/>
<TextBox PlaceholderText="Phone #" UseFloatingPlaceholder="True" Text="{Binding PhoneNumber}"/>
<TextBox PlaceholderText="Less Than 10" UseFloatingPlaceholder="True" Text="{Binding Path=LessThan10}"/>
</StackPanel>
</StackPanel>
</TabItem>

8
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml

@ -39,8 +39,12 @@
<AutoCompleteBox MaxDropDownHeight="60" />
</StackPanel>
<StackPanel>
<TextBlock Text="Watermark" />
<AutoCompleteBox Watermark="Hello World" />
<TextBlock Text="PlaceholderText" />
<AutoCompleteBox PlaceholderText="Hello World" />
</StackPanel>
<StackPanel>
<TextBlock Text="PlaceholderText with custom color" />
<AutoCompleteBox PlaceholderText="Search here..." PlaceholderForeground="Green" />
</StackPanel>
<StackPanel>
<TextBlock Text="Disabled" />

11
samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml

@ -32,11 +32,16 @@
Margin="0,0,0,8"/>
<CalendarDatePicker Margin="0,0,0,8"
Watermark="Watermark"/>
PlaceholderText="Placeholder"/>
<CalendarDatePicker Margin="0,0,0,8"
Name="DatePicker5"
Watermark="Floating Watermark"
UseFloatingWatermark="True"/>
PlaceholderText="Floating Placeholder"
UseFloatingPlaceholder="True"/>
<TextBlock Text="PlaceholderText with custom color"/>
<CalendarDatePicker Margin="0,0,0,8"
PlaceholderText="Select a date"
PlaceholderForeground="Purple"/>
<TextBlock Text="Disabled"/>
<CalendarDatePicker IsEnabled="False"/>

4
samples/ControlCatalog/Pages/ClipboardPage.xaml

@ -1,4 +1,4 @@
<UserControl x:Class="ControlCatalog.Pages.ClipboardPage"
<UserControl x:Class="ControlCatalog.Pages.ClipboardPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Spacing="4">
@ -21,7 +21,7 @@
<TextBox x:Name="ClipboardContent"
MinHeight="100"
AcceptsReturn="True"
Watermark="Text to copy of file names per line" />
PlaceholderText="Text to copy of file names per line" />
<Viewbox Width="420" Height="360">
<Image x:Name="ClipboardImage"
/>

8
samples/ControlCatalog/Pages/DialogsPage.xaml

@ -61,14 +61,14 @@
<Expander Header="Launcher dialogs">
<StackPanel Spacing="4">
<TextBox Name="UriToLaunch" Watermark="Uri to launch" Text="https://avaloniaui.net/" />
<TextBox Name="UriToLaunch" PlaceholderText="Uri to launch" Text="https://avaloniaui.net/" />
<Button Name="LaunchUri">Launch Uri</Button>
<Button Name="LaunchFile">Launch File</Button>
<TextBlock Name="LaunchStatus" />
</StackPanel>
</Expander>
<AutoCompleteBox x:Name="CurrentFolderBox" Watermark="Write full path/uri or well known folder name">
<AutoCompleteBox x:Name="CurrentFolderBox" PlaceholderText="Write full path/uri or well known folder name">
<AutoCompleteBox.ItemsSource>
<generic:List x:TypeArguments="storage:WellKnownFolder">
<storage:WellKnownFolder>Desktop</storage:WellKnownFolder>
@ -87,10 +87,10 @@
Text="Last picker results:" />
<ItemsControl x:Name="PickerLastResults" />
<TextBox Name="BookmarkContainer" Watermark="Bookmark" />
<TextBox Name="BookmarkContainer" PlaceholderText="Bookmark" />
<TextBox Name="OpenedFileContent"
MaxLines="10"
Watermark="Picked file content" />
PlaceholderText="Picked file content" />
</StackPanel>
</UserControl>

20
samples/ControlCatalog/Pages/NumericUpDownPage.xaml

@ -1,4 +1,4 @@
<UserControl xmlns="https://github.com/avaloniaui"
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="using:System"
xmlns:converter="using:ControlCatalog.Converter"
@ -48,8 +48,8 @@
<ComboBox x:Name="CultureSelector" Grid.Row="2" Grid.Column="1" ItemsSource="{Binding Cultures}"
VerticalAlignment="Center" Margin="2"/>
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="2">Watermark:</TextBlock>
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding #upDown.Watermark}" VerticalAlignment="Center" Margin="2" />
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="2">PlaceholderText:</TextBlock>
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding #upDown.PlaceholderText}" VerticalAlignment="Center" Margin="2" />
<TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Margin="2">Text:</TextBlock>
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding #upDown.Text}" VerticalAlignment="Center" Margin="2" />
@ -81,21 +81,21 @@
<NumericUpDown Name="upDown" Minimum="0" Maximum="10" Increment="0.5"
NumberFormat="{Binding #CultureSelector.SelectedItem, Converter={x:Static pages:NumericUpDownPage.CultureConverter}}"
VerticalAlignment="Center" Value="{Binding DecimalValue}"
Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"/>
PlaceholderText="Enter text" FormatString="{Binding SelectedFormat.Value}"/>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="10">
<Label Target="DoubleUpDown" FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of double NumericUpDown:</Label>
<NumericUpDown Name="DoubleUpDown" Minimum="0" Maximum="10" Increment="0.5"
VerticalAlignment="Center" Value="{Binding DoubleValue}"
Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"/>
PlaceholderText="Enter text" FormatString="{Binding SelectedFormat.Value}"/>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="10">
<Label Target="ValidationUpDown" FontSize="14" FontWeight="Bold" VerticalAlignment="Center">NumericUpDown with Validation Errors:</Label>
<NumericUpDown x:Name="ValidationUpDown" Minimum="0" Maximum="10" Increment="0.5"
VerticalAlignment="Center"
Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}">
PlaceholderText="Enter text" FormatString="{Binding SelectedFormat.Value}">
<DataValidationErrors.Error>
<sys:Exception />
</DataValidationErrors.Error>
@ -114,6 +114,14 @@
</StackPanel>
</WrapPanel>
<StackPanel Orientation="Vertical" Margin="10">
<Label FontSize="14" FontWeight="Bold" Target="PlaceholderForegroundUpDown"
Content="Placeholder with custom foreground color:"/>
<NumericUpDown x:Name="PlaceholderForegroundUpDown"
PlaceholderText="Enter amount"
PlaceholderForeground="Orange"/>
</StackPanel>
<StackPanel Margin="10">
<Label FontSize="14" FontWeight="Bold" Target="InnerContent"
Content="Inner Contents"/>

33
samples/ControlCatalog/Pages/TextBoxPage.xaml

@ -17,12 +17,19 @@
</Flyout>
</TextBox.ContextFlyout>
</TextBox>
<TextBox Width="200" Watermark="ReadOnly" IsReadOnly="True" Text="This is read only"/>
<TextBox Width="200" Watermark="Numeric with watermark" TextInputOptions.ContentType="Number" />
<TextBox Width="200" PlaceholderText="ReadOnly" IsReadOnly="True" Text="This is read only"/>
<TextBox Width="200" PlaceholderText="Numeric with placeholder" TextInputOptions.ContentType="Number" />
<TextBox Width="200"
Watermark="Floating Watermark"
UseFloatingWatermark="True"
PlaceholderText="Floating Placeholder"
UseFloatingPlaceholder="True"
Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit."/>
<TextBox Width="200"
PlaceholderText="Custom Placeholder Color"
PlaceholderForeground="Red"/>
<TextBox Width="200"
PlaceholderText="Floating Placeholder Color"
UseFloatingPlaceholder="True"
PlaceholderForeground="Purple"/>
<MaskedTextBox Width="200" ResetOnSpace="False" Mask="(LLL) 999-0000"/>
<TextBox Width="200" Text="Validation Error">
@ -32,19 +39,19 @@
</TextBox>
<TextBox Width="200"
Watermark="Password Box"
PlaceholderText="Password Box"
Classes="revealPasswordButton"
TextInputOptions.ContentType="Password"
UseFloatingWatermark="True"
UseFloatingPlaceholder="True"
PasswordChar="*"
Text="Password" />
<TextBox Width="200" Watermark="Suggestions are hidden" TextInputOptions.ShowSuggestions="False" />
<TextBox Width="200" PlaceholderText="Suggestions are hidden" TextInputOptions.ShowSuggestions="False" />
<TextBox Width="200" Text="Left aligned text" TextAlignment="Left" AcceptsTab="True" />
<TextBox Width="200" Text="Center aligned text" TextAlignment="Center" />
<TextBox Width="200" Text="Right aligned text" TextAlignment="Right" />
<TextBox Width="200" Text="Custom selection brush"
SelectionStart="5" SelectionEnd="22"
SelectionBrush="Green" SelectionForegroundBrush="Yellow" ClearSelectionOnLostFocus="False"/>
SelectionStart="5" SelectionEnd="22"
SelectionBrush="Green" SelectionForegroundBrush="Yellow" ClearSelectionOnLostFocus="False"/>
<TextBox Width="200" Text="Custom caret brush" CaretBrush="DarkOrange"/>
</StackPanel>
@ -54,11 +61,11 @@
<TextBox AcceptsReturn="True" Width="200" Height="125"
Text="Multiline TextBox with no TextWrapping.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." />
<TextBox Classes="clearButton" Text="Clear Content" Width="200" FontWeight="Normal" FontStyle="Normal" Watermark="Watermark" FontFamily="avares://ControlCatalog/Assets/Fonts#Source Sans Pro"/>
<TextBox Classes="clearButton" Text="Clear Content" Width="200" FontWeight="Normal" FontStyle="Normal" PlaceholderText="Placeholder" FontFamily="avares://ControlCatalog/Assets/Fonts#Source Sans Pro"/>
<TextBox Text="IME small font" Width="200"
FontFamily="Comic Sans MS"
FontSize="10"
Foreground="Red"/>
FontFamily="Comic Sans MS"
FontSize="10"
Foreground="Red"/>
<TextBox Text="IME large font" Width="200"
FontFamily="Comic Sans MS"
FontSize="22"

6
samples/ControlCatalog/Pages/ThemePage.axaml

@ -1,4 +1,4 @@
<UserControl x:Class="ControlCatalog.Pages.ThemePage"
<UserControl x:Class="ControlCatalog.Pages.ThemePage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@ -70,8 +70,8 @@
</ComboBox>
<TextBlock Grid.Column="0" Grid.Row="2" Text="Username:" VerticalAlignment="Center" />
<TextBlock Grid.Column="0" Grid.Row="4" Text="Password:" VerticalAlignment="Center" />
<TextBox Grid.Column="1" Grid.Row="2" Watermark="Input here" HorizontalAlignment="Stretch" />
<TextBox Grid.Column="1" Grid.Row="4" Watermark="Input here" HorizontalAlignment="Stretch" />
<TextBox Grid.Column="1" Grid.Row="2" PlaceholderText="Input here" HorizontalAlignment="Stretch" />
<TextBox Grid.Column="1" Grid.Row="4" PlaceholderText="Input here" HorizontalAlignment="Stretch" />
<Button Grid.Column="1" Grid.Row="6" Content="Login" HorizontalAlignment="Stretch" />
</Grid>
</Border>

14
samples/Generators.Sandbox/Controls/SignUpView.xaml

@ -1,4 +1,4 @@
<UserControl xmlns="https://github.com/avaloniaui"
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Generators.Sandbox.Controls"
xmlns:vm="clr-namespace:Generators.Sandbox.ViewModels"
@ -17,8 +17,8 @@
<controls:CustomTextBox Margin="0 10 0 0"
x:Name="UserNameTextBox"
Text="{Binding UserName}"
Watermark="Please, enter user name..."
UseFloatingWatermark="True" />
PlaceholderText="Please, enter user name..."
UseFloatingPlaceholder="True" />
<TextBlock x:Name="UserNameValidation"
Text="{Binding UserNameValidation}"
Foreground="Green"
@ -26,8 +26,8 @@
<TextBox Margin="0 10 0 0"
x:Name="PasswordTextBox"
Text="{Binding Password}"
Watermark="Please, enter your password..."
UseFloatingWatermark="True"
PlaceholderText="Please, enter your password..."
UseFloatingPlaceholder="True"
PasswordChar="*" />
<TextBlock x:Name="PasswordValidation"
Text="{Binding PasswordValidation}"
@ -36,8 +36,8 @@
<TextBox Margin="0 10 0 0"
x:Name="ConfirmPasswordTextBox"
Text="{Binding ConfirmPassword}"
Watermark="Please, confirm the password..."
UseFloatingWatermark="True"
PlaceholderText="Please, confirm the password..."
UseFloatingPlaceholder="True"
PasswordChar="*" />
<TextBlock x:Name="ConfirmPasswordValidation"
Text="{Binding ConfirmPasswordValidation}"

14
samples/IntegrationTestApp/Pages/ScreensPage.axaml

@ -8,12 +8,12 @@
<Button Name="ScreenRefresh"
Content="Refresh"
Click="ScreenRefresh_Click"/>
<TextBox Name="ScreenName" Watermark="DisplayName" UseFloatingWatermark="true" />
<TextBox Name="ScreenHandle" Watermark="Handle" UseFloatingWatermark="true" />
<TextBox Name="ScreenScaling" Watermark="Scaling" UseFloatingWatermark="true" />
<TextBox Name="ScreenBounds" Watermark="Bounds" UseFloatingWatermark="true" />
<TextBox Name="ScreenWorkArea" Watermark="WorkArea" UseFloatingWatermark="true" />
<TextBox Name="ScreenOrientation" Watermark="Orientation" UseFloatingWatermark="true" />
<TextBox Name="ScreenSameReference" Watermark="Is same reference" UseFloatingWatermark="true" />
<TextBox Name="ScreenName" PlaceholderText="DisplayName" UseFloatingPlaceholder="true" />
<TextBox Name="ScreenHandle" PlaceholderText="Handle" UseFloatingPlaceholder="true" />
<TextBox Name="ScreenScaling" PlaceholderText="Scaling" UseFloatingPlaceholder="true" />
<TextBox Name="ScreenBounds" PlaceholderText="Bounds" UseFloatingPlaceholder="true" />
<TextBox Name="ScreenWorkArea" PlaceholderText="WorkArea" UseFloatingPlaceholder="true" />
<TextBox Name="ScreenOrientation" PlaceholderText="Orientation" UseFloatingPlaceholder="true" />
<TextBox Name="ScreenSameReference" PlaceholderText="Is same reference" UseFloatingPlaceholder="true" />
</StackPanel>
</UserControl>

2
samples/IntegrationTestApp/Pages/WindowDecorationsPage.axaml

@ -10,7 +10,7 @@
<CheckBox Name="WindowPreferSystemChrome" Content="Prefer SystemChrome" />
<CheckBox Name="WindowMacThickSystemChrome" Content="Mac Thick SystemChrome" />
<CheckBox Name="WindowShowTitleAreaControl" Content="Show Title Area Control" />
<TextBox Name="WindowTitleBarHeightHint" Text="-1" Watermark="In dips" />
<TextBox Name="WindowTitleBarHeightHint" Text="-1" PlaceholderText="In dips" />
<Button Name="ApplyWindowDecorations"
Content="Apply decorations on this Window"
Click="ApplyWindowDecorations_Click"/>

2
samples/IntegrationTestApp/Pages/WindowPage.axaml

@ -6,7 +6,7 @@
x:Class="IntegrationTestApp.Pages.WindowPage">
<Grid ColumnDefinitions="*,8,*">
<StackPanel Grid.Column="0">
<TextBox Name="ShowWindowSize" Watermark="Window Size"/>
<TextBox Name="ShowWindowSize" PlaceholderText="Window Size"/>
<ComboBox Name="ShowWindowMode" SelectedIndex="0">
<ComboBoxItem>NonOwned</ComboBoxItem>
<ComboBoxItem>Owned</ComboBoxItem>

2
samples/SafeAreaDemo/Views/MainView.xaml

@ -47,7 +47,7 @@
<CheckBox IsChecked="{Binding UseSafeArea}" IsEnabled="{Binding !AutoSafeAreaPadding}">Use Safe Area</CheckBox>
<CheckBox IsChecked="{Binding AutoSafeAreaPadding}">Automatic Paddings</CheckBox>
<CheckBox IsChecked="{Binding HideSystemBars}">Hide System Bars</CheckBox>
<TextBox Width="200" Watermark="Tap to Show Keyboard"/>
<TextBox Width="200" PlaceholderText="Tap to Show Keyboard"/>
</StackPanel>
</Grid>
</DockPanel>

8
samples/SingleProjectSandbox/MainView.axaml

@ -5,10 +5,10 @@
x:DataType="local:MainView">
<StackPanel Margin="100 50" Spacing="50">
<TextBlock Text="Login" Foreground="White" />
<TextBox TextInputOptions.Multiline="True" AcceptsReturn="True" Watermark="Text" Height="200" TextWrapping="Wrap"/>
<TextBox Watermark="Username" TextInputOptions.ContentType="Email" TextInputOptions.ReturnKeyType="Done" />
<TextBox Watermark="Password" PasswordChar="*" TextInputOptions.ContentType="Password" />
<TextBox Watermark="Pin" PasswordChar="*" TextInputOptions.ContentType="Digits" TextInputOptions.ReturnKeyType="Next" />
<TextBox TextInputOptions.Multiline="True" AcceptsReturn="True" PlaceholderText="Text" Height="200" TextWrapping="Wrap"/>
<TextBox PlaceholderText="Username" TextInputOptions.ContentType="Email" TextInputOptions.ReturnKeyType="Done" />
<TextBox PlaceholderText="Password" PasswordChar="*" TextInputOptions.ContentType="Password" />
<TextBox PlaceholderText="Pin" PasswordChar="*" TextInputOptions.ContentType="Digits" TextInputOptions.ReturnKeyType="Next" />
<Button Content="Login" Command="{Binding ButtonCommand}" />
</StackPanel>
</UserControl>

75
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs

@ -1,4 +1,4 @@
// (c) Copyright Microsoft Corporation.
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
@ -24,8 +24,37 @@ namespace Avalonia.Controls
defaultValue: 0,
defaultBindingMode: BindingMode.TwoWay));
public static readonly StyledProperty<string?> WatermarkProperty =
TextBox.WatermarkProperty.AddOwner<AutoCompleteBox>();
/// <summary>
/// Defines the <see cref="PlaceholderText"/> property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1013",
Justification = "We keep WatermarkProperty for backward compatibility.")]
public static readonly StyledProperty<string?> PlaceholderTextProperty =
TextBox.PlaceholderTextProperty.AddOwner<AutoCompleteBox>();
/// <summary>
/// Defines the <see cref="Watermark"/> property.
/// </summary>
[Obsolete("Use PlaceholderTextProperty instead.", false)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1022",
Justification = "Obsolete property alias for backward compatibility.")]
public static readonly StyledProperty<string?> WatermarkProperty = PlaceholderTextProperty;
/// <summary>
/// Defines the <see cref="PlaceholderForeground"/> property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1013",
Justification = "We keep WatermarkForegroundProperty for backward compatibility.")]
public static readonly StyledProperty<Media.IBrush?> PlaceholderForegroundProperty =
TextBox.PlaceholderForegroundProperty.AddOwner<AutoCompleteBox>();
/// <summary>
/// Defines the <see cref="WatermarkForeground"/> property.
/// </summary>
[Obsolete("Use PlaceholderForegroundProperty instead.", false)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1022",
Justification = "Obsolete property alias for backward compatibility.")]
public static readonly StyledProperty<Media.IBrush?> WatermarkForegroundProperty = PlaceholderForegroundProperty;
/// <summary>
/// Identifies the <see cref="MinimumPrefixLength" /> property.
@ -406,10 +435,46 @@ namespace Avalonia.Controls
set => SetValue(FilterModeProperty, value);
}
/// <summary>
/// Gets or sets the placeholder or descriptive text that is displayed even if the text is not yet set.
/// </summary>
public string? PlaceholderText
{
get => GetValue(PlaceholderTextProperty);
set => SetValue(PlaceholderTextProperty, value);
}
/// <summary>
/// Gets or sets the placeholder or descriptive text that is displayed even if the text is not yet set.
/// </summary>
[Obsolete("Use PlaceholderText instead.", false)]
public string? Watermark
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
get => PlaceholderText;
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Obsolete property setter for backward compatibility.")]
set => PlaceholderText = value;
}
/// <summary>
/// Gets or sets the brush used for the foreground color of the placeholder text.
/// </summary>
public Media.IBrush? PlaceholderForeground
{
get => GetValue(PlaceholderForegroundProperty);
set => SetValue(PlaceholderForegroundProperty, value);
}
/// <summary>
/// Gets or sets the brush used for the foreground color of the placeholder text.
/// </summary>
[Obsolete("Use PlaceholderForeground instead.", false)]
public Media.IBrush? WatermarkForeground
{
get => PlaceholderForeground;
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Obsolete property setter for backward compatibility.")]
set => PlaceholderForeground = value;
}
/// <summary>

91
src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs

@ -1,4 +1,4 @@
using System;
using System;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Layout;
@ -80,17 +80,53 @@ namespace Avalonia.Controls
public static readonly StyledProperty<string?> TextProperty =
AvaloniaProperty.Register<CalendarDatePicker, string?>(nameof(Text));
/// <summary>
/// Defines the <see cref="PlaceholderText"/> property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1013",
Justification = "We keep WatermarkProperty for backward compatibility.")]
public static readonly StyledProperty<string?> PlaceholderTextProperty =
TextBox.PlaceholderTextProperty.AddOwner<CalendarDatePicker>();
/// <summary>
/// Defines the <see cref="Watermark"/> property.
/// </summary>
public static readonly StyledProperty<string?> WatermarkProperty =
TextBox.WatermarkProperty.AddOwner<CalendarDatePicker>();
[Obsolete("Use PlaceholderTextProperty instead.", false)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1022",
Justification = "Obsolete property alias for backward compatibility.")]
public static readonly StyledProperty<string?> WatermarkProperty = PlaceholderTextProperty;
/// <summary>
/// Defines the <see cref="UseFloatingPlaceholder"/> property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1013",
Justification = "We keep UseFloatingWatermarkProperty for backward compatibility.")]
public static readonly StyledProperty<bool> UseFloatingPlaceholderProperty =
TextBox.UseFloatingPlaceholderProperty.AddOwner<CalendarDatePicker>();
/// <summary>
/// Defines the <see cref="UseFloatingWatermark"/> property.
/// </summary>
public static readonly StyledProperty<bool> UseFloatingWatermarkProperty =
TextBox.UseFloatingWatermarkProperty.AddOwner<CalendarDatePicker>();
[Obsolete("Use UseFloatingPlaceholderProperty instead.", false)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1022",
Justification = "Obsolete property alias for backward compatibility.")]
public static readonly StyledProperty<bool> UseFloatingWatermarkProperty = UseFloatingPlaceholderProperty;
/// <summary>
/// Defines the <see cref="PlaceholderForeground"/> property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1013",
Justification = "We keep WatermarkForegroundProperty for backward compatibility.")]
public static readonly StyledProperty<Media.IBrush?> PlaceholderForegroundProperty =
TextBox.PlaceholderForegroundProperty.AddOwner<CalendarDatePicker>();
/// <summary>
/// Defines the <see cref="WatermarkForeground"/> property.
/// </summary>
[Obsolete("Use PlaceholderForegroundProperty instead.", false)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1022",
Justification = "Obsolete property alias for backward compatibility.")]
public static readonly StyledProperty<Media.IBrush?> WatermarkForegroundProperty = PlaceholderForegroundProperty;
/// <summary>
/// Defines the <see cref="HorizontalContentAlignment"/> property.
@ -254,18 +290,55 @@ namespace Avalonia.Controls
set => SetValue(TextProperty, value);
}
/// <inheritdoc cref="TextBox.PlaceholderText"/>
public string? PlaceholderText
{
get => GetValue(PlaceholderTextProperty);
set => SetValue(PlaceholderTextProperty, value);
}
/// <inheritdoc cref="TextBox.Watermark"/>
[Obsolete("Use PlaceholderText instead.", false)]
public string? Watermark
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
get => PlaceholderText;
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Obsolete property setter for backward compatibility.")]
set => PlaceholderText = value;
}
/// <inheritdoc cref="TextBox.UseFloatingPlaceholder"/>
public bool UseFloatingPlaceholder
{
get => GetValue(UseFloatingPlaceholderProperty);
set => SetValue(UseFloatingPlaceholderProperty, value);
}
/// <inheritdoc cref="TextBox.UseFloatingWatermark"/>
[Obsolete("Use UseFloatingPlaceholder instead.", false)]
public bool UseFloatingWatermark
{
get => GetValue(UseFloatingWatermarkProperty);
set => SetValue(UseFloatingWatermarkProperty, value);
get => UseFloatingPlaceholder;
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Obsolete property setter for backward compatibility.")]
set => UseFloatingPlaceholder = value;
}
/// <inheritdoc cref="TextBox.PlaceholderForeground"/>
public Media.IBrush? PlaceholderForeground
{
get => GetValue(PlaceholderForegroundProperty);
set => SetValue(PlaceholderForegroundProperty, value);
}
/// <inheritdoc cref="TextBox.WatermarkForeground"/>
[Obsolete("Use PlaceholderForeground instead.", false)]
public Media.IBrush? WatermarkForeground
{
get => PlaceholderForeground;
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Obsolete property setter for backward compatibility.")]
set => PlaceholderForeground = value;
}
/// <summary>

16
src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs

@ -881,38 +881,38 @@ namespace Avalonia.Controls
{
SetCurrentValue(TextProperty, String.Empty);
if (string.IsNullOrEmpty(Watermark) && !UseFloatingWatermark)
if (string.IsNullOrEmpty(PlaceholderText) && !UseFloatingPlaceholder)
{
DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat();
_defaultText = string.Empty;
var watermarkFormat = "<{0}>";
string watermarkText;
var placeholderFormat = "<{0}>";
string placeholderText;
switch (SelectedDateFormat)
{
case CalendarDatePickerFormat.Custom:
{
watermarkText = string.Format(CultureInfo.CurrentCulture, watermarkFormat, CustomDateFormatString);
placeholderText = string.Format(CultureInfo.CurrentCulture, placeholderFormat, CustomDateFormatString);
break;
}
case CalendarDatePickerFormat.Long:
{
watermarkText = string.Format(CultureInfo.CurrentCulture, watermarkFormat, dtfi.LongDatePattern.ToString());
placeholderText = string.Format(CultureInfo.CurrentCulture, placeholderFormat, dtfi.LongDatePattern.ToString());
break;
}
case CalendarDatePickerFormat.Short:
default:
{
watermarkText = string.Format(CultureInfo.CurrentCulture, watermarkFormat, dtfi.ShortDatePattern.ToString());
placeholderText = string.Format(CultureInfo.CurrentCulture, placeholderFormat, dtfi.ShortDatePattern.ToString());
break;
}
}
_textBox.Watermark = watermarkText;
_textBox.PlaceholderText = placeholderText;
}
else
{
_textBox.ClearValue(TextBox.WatermarkProperty);
_textBox.ClearValue(TextBox.PlaceholderTextProperty);
}
}
}

69
src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Globalization;
using System.IO;
using System.Linq;
@ -108,11 +108,35 @@ namespace Avalonia.Controls
AvaloniaProperty.Register<NumericUpDown, decimal?>(nameof(Value), coerce: (s,v) => ((NumericUpDown)s).OnCoerceValue(v),
defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true);
/// <summary>
/// Defines the <see cref="PlaceholderText"/> property.
/// </summary>
public static readonly StyledProperty<string?> PlaceholderTextProperty =
AvaloniaProperty.Register<NumericUpDown, string?>(nameof(PlaceholderText));
/// <summary>
/// Defines the <see cref="Watermark"/> property.
/// </summary>
public static readonly StyledProperty<string?> WatermarkProperty =
AvaloniaProperty.Register<NumericUpDown, string?>(nameof(Watermark));
[Obsolete("Use PlaceholderTextProperty instead.", false)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1022",
Justification = "Obsolete property alias for backward compatibility.")]
public static readonly StyledProperty<string?> WatermarkProperty = PlaceholderTextProperty;
/// <summary>
/// Defines the <see cref="PlaceholderForeground"/> property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1013",
Justification = "We keep WatermarkForegroundProperty for backward compatibility.")]
public static readonly StyledProperty<Media.IBrush?> PlaceholderForegroundProperty =
TextBox.PlaceholderForegroundProperty.AddOwner<NumericUpDown>();
/// <summary>
/// Defines the <see cref="WatermarkForeground"/> property.
/// </summary>
[Obsolete("Use PlaceholderForegroundProperty instead.", false)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1022",
Justification = "Obsolete property alias for backward compatibility.")]
public static readonly StyledProperty<Media.IBrush?> WatermarkForegroundProperty = PlaceholderForegroundProperty;
/// <summary>
/// Defines the <see cref="HorizontalContentAlignment"/> property.
@ -292,12 +316,45 @@ namespace Avalonia.Controls
}
/// <summary>
/// Gets or sets the object to use as a watermark if the <see cref="Value"/> is null.
/// Gets or sets the object to use as a placeholder if the <see cref="Value"/> is null.
/// </summary>
public string? PlaceholderText
{
get => GetValue(PlaceholderTextProperty);
set => SetValue(PlaceholderTextProperty, value);
}
/// <summary>
/// Gets or sets the object to use as a placeholder if the <see cref="Value"/> is null.
/// </summary>
[Obsolete("Use PlaceholderText instead.", false)]
public string? Watermark
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
get => PlaceholderText;
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Obsolete property setter for backward compatibility.")]
set => PlaceholderText = value;
}
/// <summary>
/// Gets or sets the brush used for the foreground color of the placeholder text.
/// </summary>
public Media.IBrush? PlaceholderForeground
{
get => GetValue(PlaceholderForegroundProperty);
set => SetValue(PlaceholderForegroundProperty, value);
}
/// <summary>
/// Gets or sets the brush used for the foreground color of the placeholder text.
/// </summary>
[Obsolete("Use PlaceholderForeground instead.", false)]
public Media.IBrush? WatermarkForeground
{
get => PlaceholderForeground;
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Obsolete property setter for backward compatibility.")]
set => PlaceholderForeground = value;
}
/// <summary>

105
src/Avalonia.Controls/TextBox.cs

@ -181,16 +181,46 @@ namespace Avalonia.Controls
TextBlock.LineHeightProperty.AddOwner<TextBox>(new(defaultValue: double.NaN));
/// <summary>
/// Defines the <see cref="Watermark"/> property
/// Defines the <see cref="PlaceholderText"/> property.
/// </summary>
public static readonly StyledProperty<string?> WatermarkProperty =
AvaloniaProperty.Register<TextBox, string?>(nameof(Watermark));
public static readonly StyledProperty<string?> PlaceholderTextProperty =
AvaloniaProperty.Register<TextBox, string?>(nameof(PlaceholderText));
/// <summary>
/// Defines the <see cref="UseFloatingWatermark"/> property
/// Defines the <see cref="Watermark"/> property.
/// </summary>
public static readonly StyledProperty<bool> UseFloatingWatermarkProperty =
AvaloniaProperty.Register<TextBox, bool>(nameof(UseFloatingWatermark));
[Obsolete("Use PlaceholderTextProperty instead.", false)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1022",
Justification = "Obsolete property alias for backward compatibility.")]
public static readonly StyledProperty<string?> WatermarkProperty = PlaceholderTextProperty;
/// <summary>
/// Defines the <see cref="UseFloatingPlaceholder"/> property.
/// </summary>
public static readonly StyledProperty<bool> UseFloatingPlaceholderProperty =
AvaloniaProperty.Register<TextBox, bool>(nameof(UseFloatingPlaceholder));
/// <summary>
/// Defines the <see cref="UseFloatingWatermark"/> property.
/// </summary>
[Obsolete("Use UseFloatingPlaceholderProperty instead.", false)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1022",
Justification = "Obsolete property alias for backward compatibility.")]
public static readonly StyledProperty<bool> UseFloatingWatermarkProperty = UseFloatingPlaceholderProperty;
/// <summary>
/// Defines the <see cref="PlaceholderForeground"/> property.
/// </summary>
public static readonly StyledProperty<IBrush?> PlaceholderForegroundProperty =
AvaloniaProperty.Register<TextBox, IBrush?>(nameof(PlaceholderForeground));
/// <summary>
/// Defines the <see cref="WatermarkForeground"/> property.
/// </summary>
[Obsolete("Use PlaceholderForegroundProperty instead.", false)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1022",
Justification = "Obsolete property alias for backward compatibility.")]
public static readonly StyledProperty<IBrush?> WatermarkForegroundProperty = PlaceholderForegroundProperty;
/// <summary>
/// Defines the <see cref="NewLine"/> property
@ -394,8 +424,8 @@ namespace Avalonia.Controls
/// </summary>
public bool ClearSelectionOnLostFocus
{
get=> GetValue(ClearSelectionOnLostFocusProperty);
set=> SetValue(ClearSelectionOnLostFocusProperty, value);
get => GetValue(ClearSelectionOnLostFocusProperty);
set => SetValue(ClearSelectionOnLostFocusProperty, value);
}
/// <summary>
@ -408,7 +438,7 @@ namespace Avalonia.Controls
}
/// <summary>
/// Gets or sets a value that determins whether the TextBox allows and displays tabs
/// Gets or sets a value that determines whether the TextBox allows and displays tabs
/// </summary>
public bool AcceptsTab
{
@ -665,20 +695,67 @@ namespace Avalonia.Controls
/// Gets or sets the placeholder or descriptive text that is displayed even if the <see cref="Text"/>
/// property is not yet set.
/// </summary>
public string? PlaceholderText
{
get => GetValue(PlaceholderTextProperty);
set => SetValue(PlaceholderTextProperty, value);
}
/// <summary>
/// Gets or sets the placeholder or descriptive text that is displayed even if the <see cref="Text"/>
/// property is not yet set.
/// </summary>
[Obsolete("Use PlaceholderText instead.", false)]
public string? Watermark
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
get => PlaceholderText;
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Obsolete property setter for backward compatibility.")]
set => PlaceholderText = value;
}
/// <summary>
/// Gets or sets a value indicating whether the <see cref="Watermark"/> will still be shown above the
/// Gets or sets a value indicating whether the <see cref="PlaceholderText"/> will still be shown above the
/// <see cref="Text"/> even after a text value is set.
/// </summary>
public bool UseFloatingPlaceholder
{
get => GetValue(UseFloatingPlaceholderProperty);
set => SetValue(UseFloatingPlaceholderProperty, value);
}
/// <summary>
/// Gets or sets a value indicating whether the <see cref="PlaceholderText"/> will still be shown above the
/// <see cref="Text"/> even after a text value is set.
/// </summary>
[Obsolete("Use UseFloatingPlaceholder instead.", false)]
public bool UseFloatingWatermark
{
get => GetValue(UseFloatingWatermarkProperty);
set => SetValue(UseFloatingWatermarkProperty, value);
get => UseFloatingPlaceholder;
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Obsolete property setter for backward compatibility.")]
set => UseFloatingPlaceholder = value;
}
/// <summary>
/// Gets or sets the brush used for the foreground color of the placeholder text.
/// </summary>
public IBrush? PlaceholderForeground
{
get => GetValue(PlaceholderForegroundProperty);
set => SetValue(PlaceholderForegroundProperty, value);
}
/// <summary>
/// Gets or sets the brush used for the foreground color of the placeholder text.
/// </summary>
[Obsolete("Use PlaceholderForeground instead.", false)]
public IBrush? WatermarkForeground
{
get => PlaceholderForeground;
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Obsolete property setter for backward compatibility.")]
set => PlaceholderForeground = value;
}
/// <summary>

5
src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml

@ -1,4 +1,4 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:generic="using:System.Collections.Generic"
x:ClassModifier="internal">
@ -34,7 +34,8 @@
<ControlTemplate>
<Grid Name="PART_LayoutRoot">
<TextBox Name="PART_TextBox"
Watermark="{TemplateBinding Watermark}"
PlaceholderText="{TemplateBinding PlaceholderText}"
PlaceholderForeground="{TemplateBinding PlaceholderForeground}"
Width="{TemplateBinding Width}"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"

9
src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml

@ -1,4 +1,4 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="using:System"
x:ClassModifier="internal">
@ -92,8 +92,9 @@
CornerRadius="{TemplateBinding CornerRadius}"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
Watermark="{TemplateBinding Watermark}"
UseFloatingWatermark="{TemplateBinding UseFloatingWatermark}"
PlaceholderText="{TemplateBinding PlaceholderText}"
PlaceholderForeground="{TemplateBinding PlaceholderForeground}"
UseFloatingPlaceholder="{TemplateBinding UseFloatingPlaceholder}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
@ -112,7 +113,7 @@
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
<Style Selector="^ /template/ TextBlock#PART_Watermark, ^ TextBlock#PART_FloatingWatermark">
<Style Selector="^ /template/ TextBlock#PART_Placeholder, ^ TextBlock#PART_FloatingPlaceholder">
<Setter Property="TextElement.Foreground" Value="{DynamicResource CalendarDatePickerTextForegroundDisabled}" />
</Style>
</Style>

4
src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml

@ -1,4 +1,4 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="using:System"
x:ClassModifier="internal">
@ -126,7 +126,7 @@
Foreground="{TemplateBinding Foreground}"
Background="Transparent"
Text="{TemplateBinding Text, Mode=TwoWay}"
Watermark="{TemplateBinding PlaceholderText}"
PlaceholderText="{TemplateBinding PlaceholderText}"
BorderThickness="0"
IsVisible="{TemplateBinding IsEditable}">
<TextBox.Resources>

4
src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml

@ -1,4 +1,4 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dialogs="using:Avalonia.Dialogs"
xmlns:internal="using:Avalonia.Dialogs.Internal"
@ -184,7 +184,7 @@
IsVisible="{Binding ShowFilters}"
ItemsSource="{Binding Filters}"
SelectedItem="{Binding SelectedFilter}" />
<TextBox Text="{Binding FileName}" Watermark="{DynamicResource StringManagedFileChooserFileNameWatermark}" IsVisible="{Binding !SelectingFolder}" />
<TextBox Text="{Binding FileName}" PlaceholderText="{DynamicResource StringManagedFileChooserFileNamePlaceholder}" IsVisible="{Binding !SelectingFolder}" />
</DockPanel>
<CheckBox IsChecked="{Binding ShowHiddenFiles}" Content="{DynamicResource StringManagedFileChooserShowHiddenFilesText}" DockPanel.Dock="Left"/>
<UniformGrid x:Name="Finalize" HorizontalAlignment="Right" Rows="1">

25
src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml

@ -1,21 +1,21 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=System.Runtime"
x:ClassModifier="internal">
<Design.PreviewWith>
<Border Padding="20">
<StackPanel Spacing="20">
<NumericUpDown Minimum="0"
Maximum="10"
Increment="0.5"
PlaceholderText="Enter text" />
<NumericUpDown Minimum="0"
Maximum="10"
Increment="0.5"
Watermark="Enter text" />
<NumericUpDown Minimum="0"
Maximum="10"
Increment="0.5"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Center"
ButtonSpinnerLocation="Left"
Watermark="Enter text" />
Maximum="10"
Increment="0.5"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Center"
ButtonSpinnerLocation="Left"
PlaceholderText="Enter text" />
<NumericUpDown ButtonSpinnerLocation="Left">
<NumericUpDown.InnerLeftContent>
<TextBlock Text="m"
@ -81,7 +81,8 @@
Padding="{TemplateBinding Padding}"
MinWidth="0"
Foreground="{TemplateBinding Foreground}"
Watermark="{TemplateBinding Watermark}"
PlaceholderText="{TemplateBinding PlaceholderText}"
PlaceholderForeground="{TemplateBinding PlaceholderForeground}"
IsReadOnly="{TemplateBinding IsReadOnly}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"

35
src/Avalonia.Themes.Fluent/Controls/TextBox.xaml

@ -9,9 +9,9 @@
<TextBox Classes="clearButton">Clear</TextBox>
<TextBox PasswordChar="*" Classes="revealPasswordButton">Reveal Password</TextBox>
<TextBox PasswordChar="*" Classes="revealPasswordButton" RevealPassword="True">Password Revealed</TextBox>
<TextBox Watermark="Watermark"/>
<TextBox Watermark="Floating Watermark" UseFloatingWatermark="True"/>
<TextBox Watermark="Floating Watermark" UseFloatingWatermark="True">Content</TextBox>
<TextBox PlaceholderText="Placeholder"/>
<TextBox PlaceholderText="Floating Placeholder" UseFloatingPlaceholder="True"/>
<TextBox PlaceholderText="Floating Placeholder" UseFloatingPlaceholder="True">Content</TextBox>
</StackPanel>
</Border>
</Design.PreviewWith>
@ -99,6 +99,7 @@
<Setter Property="CaretBrush" Value="{DynamicResource TextControlForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource TextControlBorderBrush}" />
<Setter Property="SelectionBrush" Value="{DynamicResource TextControlSelectionHighlightColor}" />
<Setter Property="PlaceholderForeground" Value="{DynamicResource TextControlPlaceholderForeground}" />
<Setter Property="BorderThickness" Value="{DynamicResource TextControlBorderThemeThickness}" />
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
<Setter Property="MinHeight" Value="{DynamicResource TextControlThemeMinHeight}" />
@ -132,11 +133,12 @@
Grid.Column="1"
Grid.ColumnSpan="1"
Margin="{TemplateBinding Padding}">
<TextBlock Name="PART_FloatingWatermark"
Foreground="{DynamicResource SystemAccentColor}"
<TextBlock Name="PART_FloatingPlaceholder"
Foreground="{TemplateBinding PlaceholderForeground}"
IsVisible="False"
Text="{TemplateBinding Watermark}"
Text="{TemplateBinding PlaceholderText}"
DockPanel.Dock="Top" />
<ScrollViewer Name="PART_ScrollViewer"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"
@ -144,9 +146,10 @@
AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}"
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}">
<Panel>
<TextBlock Name="PART_Watermark"
<TextBlock Name="PART_Placeholder"
Foreground="{TemplateBinding PlaceholderForeground}"
Opacity="{DynamicResource TextControlPlaceholderOpacity}"
Text="{TemplateBinding Watermark}"
Text="{TemplateBinding PlaceholderText}"
TextAlignment="{TemplateBinding TextAlignment}"
TextWrapping="{TemplateBinding TextWrapping}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
@ -158,6 +161,7 @@
</MultiBinding>
</TextBlock.IsVisible>
</TextBlock>
<TextPresenter Name="PART_TextPresenter"
Text="{TemplateBinding Text, Mode=TwoWay}"
CaretBlinkInterval="{TemplateBinding CaretBlinkInterval}"
@ -203,9 +207,6 @@
<Setter Property="BorderBrush" Value="{DynamicResource TextControlBorderBrushDisabled}" />
</Style>
<Style Selector="^ /template/ TextBlock#PART_Watermark, ^ /template/ TextBlock#PART_FloatingWatermark">
<Setter Property="Foreground" Value="{DynamicResource TextControlPlaceholderForegroundDisabled}" />
</Style>
</Style>
<!-- PointerOver State-->
@ -217,19 +218,12 @@
<Setter Property="Background" Value="{DynamicResource TextControlBackgroundPointerOver}" />
</Style>
<Style Selector="^ /template/ TextBlock#PART_Watermark">
<Setter Property="Foreground" Value="{DynamicResource TextControlPlaceholderForegroundPointerOver}" />
</Style>
</Style>
<!-- Focused State -->
<Style Selector="^:focus">
<Setter Property="Foreground" Value="{DynamicResource TextControlForegroundFocused}" />
<Style Selector="^ /template/ TextBlock#PART_Watermark">
<Setter Property="Foreground" Value="{DynamicResource TextControlPlaceholderForegroundFocused}" />
</Style>
<Style Selector="^ /template/ Border#PART_BorderElement">
<Setter Property="Background" Value="{DynamicResource TextControlBackgroundFocused}"/>
<Setter Property="BorderBrush" Value="{DynamicResource TextControlBorderBrushFocused}"/>
@ -241,14 +235,15 @@
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlErrorTextForegroundBrush}"/>
</Style>
<Style Selector="^ /template/ TextBlock#PART_FloatingWatermark">
<Style Selector="^ /template/ TextBlock#PART_FloatingPlaceholder">
<Setter Property="Cursor" Value="IBeam"/>
</Style>
<Style Selector="^[UseFloatingWatermark=true]:not(:empty) /template/ TextBlock#PART_FloatingWatermark">
<Style Selector="^[UseFloatingPlaceholder=true]:not(:empty) /template/ TextBlock#PART_FloatingPlaceholder">
<Setter Property="IsVisible" Value="True" />
</Style>
<Style Selector="^.revealPasswordButton[AcceptsReturn=False][IsReadOnly=False]:not(TextBox:empty)">
<Setter Property="InnerRightContent">
<Template>

2
src/Avalonia.Themes.Fluent/Strings/InvariantResources.xaml

@ -14,7 +14,7 @@
<x:String x:Key="StringTextFlyoutCopyText">Copy</x:String>
<x:String x:Key="StringTextFlyoutPasteText">Paste</x:String>
<!-- ManagedFileChooser -->
<x:String x:Key="StringManagedFileChooserFileNameWatermark">File name</x:String>
<x:String x:Key="StringManagedFileChooserFileNamePlaceholder">File name</x:String>
<x:String x:Key="StringManagedFileChooserShowHiddenFilesText">Show hidden files</x:String>
<x:String x:Key="StringManagedFileChooserOkText">OK</x:String>
<x:String x:Key="StringManagedFileChooserCancelText">Cancel</x:String>

3
src/Avalonia.Themes.Simple/Controls/AutoCompleteBox.xaml

@ -19,7 +19,8 @@
CaretIndex="{TemplateBinding CaretIndex, Mode=TwoWay}"
ClearSelectionOnLostFocus="{TemplateBinding ClearSelectionOnLostFocus}"
DataValidationErrors.Errors="{TemplateBinding (DataValidationErrors.Errors)}"
Watermark="{TemplateBinding Watermark}"
PlaceholderText="{TemplateBinding PlaceholderText}"
PlaceholderForeground="{TemplateBinding PlaceholderForeground}"
MaxLength="{TemplateBinding MaxLength}"
InnerLeftContent="{TemplateBinding InnerLeftContent}"
InnerRightContent="{TemplateBinding InnerRightContent}"

5
src/Avalonia.Themes.Simple/Controls/CalendarDatePicker.xaml

@ -101,8 +101,9 @@
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
DataValidationErrors.Errors="{TemplateBinding (DataValidationErrors.Errors)}"
UseFloatingWatermark="{TemplateBinding UseFloatingWatermark}"
Watermark="{TemplateBinding Watermark}" />
UseFloatingPlaceholder="{TemplateBinding UseFloatingPlaceholder}"
PlaceholderText="{TemplateBinding PlaceholderText}"
PlaceholderForeground="{TemplateBinding PlaceholderForeground}" />
<Button Name="PART_Button"
Grid.Column="1"

2
src/Avalonia.Themes.Simple/Controls/ManagedFileChooser.xaml

@ -115,7 +115,7 @@
<TextBox DockPanel.Dock="Bottom"
IsVisible="{Binding !SelectingFolder}"
Text="{Binding FileName}"
Watermark="{DynamicResource StringManagedFileChooserFileNameWatermark}" />
PlaceholderText="{DynamicResource StringManagedFileChooserFileNamePlaceholder}" />
<ListBox x:Name="PART_QuickLinks"
MaxWidth="200"

3
src/Avalonia.Themes.Simple/Controls/NumericUpDown.xaml

@ -37,7 +37,8 @@
Text="{TemplateBinding Text}"
TextAlignment="{TemplateBinding TextAlignment}"
TextWrapping="NoWrap"
Watermark="{TemplateBinding Watermark}"
PlaceholderText="{TemplateBinding PlaceholderText}"
PlaceholderForeground="{TemplateBinding PlaceholderForeground}"
InnerLeftContent="{Binding InnerLeftContent, RelativeSource={RelativeSource TemplatedParent}}"
InnerRightContent="{Binding InnerRightContent, RelativeSource={RelativeSource TemplatedParent}}"/>
</ButtonSpinner>

16
src/Avalonia.Themes.Simple/Controls/TextBox.xaml

@ -85,6 +85,7 @@
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}" />
<Setter Property="PlaceholderForeground" Value="{DynamicResource ThemeForegroundLowBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}" />
<Setter Property="SelectionBrush" Value="{DynamicResource HighlightBrush}" />
<Setter Property="SelectionForegroundBrush" Value="{DynamicResource HighlightForegroundBrush}" />
@ -102,14 +103,14 @@
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<TextBlock Name="floatingWatermark"
<TextBlock Name="floatingPlaceholder"
DockPanel.Dock="Top"
FontSize="{DynamicResource FontSizeSmall}"
Foreground="{DynamicResource ThemeAccentBrush}"
Text="{TemplateBinding Watermark}">
Foreground="{TemplateBinding PlaceholderForeground}"
Text="{TemplateBinding PlaceholderText}">
<TextBlock.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="UseFloatingWatermark"
<Binding Path="UseFloatingPlaceholder"
RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Converter="{x:Static StringConverters.IsNotNullOrEmpty}"
Path="Text"
@ -133,11 +134,12 @@
IsScrollChainingEnabled="{TemplateBinding (ScrollViewer.IsScrollChainingEnabled)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
<Panel>
<TextBlock Name="watermark"
<TextBlock Name="placeholder"
Foreground="{TemplateBinding PlaceholderForeground}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Opacity="{DynamicResource TextControlPlaceholderOpacity}"
Text="{TemplateBinding Watermark}"
Text="{TemplateBinding PlaceholderText}"
TextAlignment="{TemplateBinding TextAlignment}"
TextWrapping="{TemplateBinding TextWrapping}">
<TextBlock.IsVisible>
@ -194,7 +196,7 @@
<Style Selector="^:disabled /template/ Border#border">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}" />
</Style>
<Style Selector="^ /template/ TextBlock#floatingWatermark">
<Style Selector="^ /template/ TextBlock#floatingPlaceholder">
<Setter Property="Cursor" Value="IBeam"/>
</Style>

13
tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs

@ -1283,5 +1283,18 @@ namespace Avalonia.Controls.UnitTests
IsOpen = true;
}
}
[Fact]
public void PlaceholderForeground_Can_Be_Set()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var control = CreateControl();
control.PlaceholderText = "Search...";
control.PlaceholderForeground = Media.Brushes.Green;
Assert.Equal(Media.Brushes.Green, control.PlaceholderForeground);
}
}
}
}

13
tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs

@ -179,5 +179,18 @@ namespace Avalonia.Controls.UnitTests
});
}
[Fact]
public void PlaceholderForeground_Can_Be_Set()
{
using (UnitTestApplication.Start(Services))
{
var control = CreateControl();
control.PlaceholderText = "Select date";
control.PlaceholderForeground = Media.Brushes.Purple;
Assert.Equal(Media.Brushes.Purple, control.PlaceholderForeground);
}
}
}
}

15
tests/Avalonia.Controls.UnitTests/NumericUpDownTests.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@ -189,5 +189,18 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(10, textbox.TabIndex);
});
}
[Fact]
public void PlaceholderForeground_Can_Be_Set()
{
using (UnitTestApplication.Start(Services))
{
var control = CreateControl();
control.PlaceholderText = "Enter value";
control.PlaceholderForeground = Media.Brushes.Red;
Assert.Equal(Media.Brushes.Red, control.PlaceholderForeground);
}
}
}
}

55
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@ -2149,6 +2149,61 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal("FirstSecond", target.Text);
}
[Fact]
public void PlaceholderForeground_Can_Be_Set()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
PlaceholderText = "Enter text",
PlaceholderForeground = Brushes.Red
};
target.ApplyTemplate();
Assert.Equal(Brushes.Red, target.PlaceholderForeground);
}
}
[Fact]
public void PlaceholderForeground_Defaults_To_Null()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
PlaceholderText = "Enter text"
};
target.ApplyTemplate();
Assert.Null(target.PlaceholderForeground);
}
}
[Fact]
public void PlaceholderForeground_Can_Be_Set_To_Null()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
PlaceholderText = "Enter text",
PlaceholderForeground = Brushes.Blue
};
target.ApplyTemplate();
target.PlaceholderForeground = null;
Assert.Null(target.PlaceholderForeground);
}
}
private static TestServices FocusServices => TestServices.MockThreadingInterface.With(
keyboardDevice: () => new KeyboardDevice(),
keyboardNavigation: () => new KeyboardNavigationHandler(),

4
tests/Avalonia.Generators.Tests/Views/AttachedProps.xml

@ -3,6 +3,6 @@
x:Class="Sample.App.AttachedProps"
Design.Width="300">
<TextBox Name="UserNameTextBox"
Watermark="Username input"
UseFloatingWatermark="True" />
Placeholder="Username input"
UseFloatingPlaceholder="True" />
</Window>

4
tests/Avalonia.Generators.Tests/Views/ControlWithoutWindow.xml

@ -3,6 +3,6 @@
x:Class="Sample.App.ControlWithoutWindow"
Design.Width="300">
<TextBox Name="UserNameTextBox"
Watermark="Username input"
UseFloatingWatermark="True" />
Placeholder="Username input"
UseFloatingPlaceholder="True" />
</UserControl>

8
tests/Avalonia.Generators.Tests/Views/DataTemplates.xml

@ -3,14 +3,14 @@
x:Class="Sample.App.DataTemplates">
<StackPanel>
<TextBox x:Name="UserNameTextBox"
Watermark="Username input"
UseFloatingWatermark="True" />
Placeholder="Username input"
UseFloatingPlaceholder="True" />
<ListBox Name="NamedListBox">
<ListBox.ItemTemplate>
<DataTemplate x:Name="NamedDataTemplate">
<TextBox x:Name="TemplatedTextBox"
Watermark="Templated input"
UseFloatingWatermark="True" />
Placeholder="Templated input"
UseFloatingPlaceholder="True" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

16
tests/Avalonia.Generators.Tests/Views/FieldModifier.xml

@ -4,20 +4,20 @@
<StackPanel>
<TextBox Name="FirstNameTextBox"
x:FieldModifier="Public"
Watermark="Username input"
UseFloatingWatermark="True" />
Placeholder="Username input"
UseFloatingPlaceholder="True" />
<TextBox Name="LastNameTextBox"
x:FieldModifier="public"
Watermark="Username input"
UseFloatingWatermark="True" />
Placeholder="Username input"
UseFloatingPlaceholder="True" />
<TextBox Name="PasswordTextBox"
x:FieldModifier="protected"
Watermark="Password input"
UseFloatingWatermark="True" />
Placeholder="Password input"
UseFloatingPlaceholder="True" />
<TextBox Name="ConfirmPasswordTextBox"
x:FieldModifier="private"
Watermark="Password input"
UseFloatingWatermark="True" />
Placeholder="Password input"
UseFloatingPlaceholder="True" />
<Button Name="SignUpButton"
x:FieldModifier="NotPublic"
Content="Sign up" />

4
tests/Avalonia.Generators.Tests/Views/NamedControl.xml

@ -3,6 +3,6 @@
x:Class="Sample.App.NamedControl">
<TextBox Name="UserNameTextBox"
AutomationProperties.Name="The user name"
Watermark="Username input"
UseFloatingWatermark="True" />
Placeholder="Username input"
UseFloatingPlaceholder="True" />
</Window>

8
tests/Avalonia.Generators.Tests/Views/NamedControls.xml

@ -3,11 +3,11 @@
x:Class="Sample.App.NamedControls">
<StackPanel>
<TextBox Name="UserNameTextBox"
Watermark="Username input"
UseFloatingWatermark="True" />
Placeholder="Username input"
UseFloatingPlaceholder="True" />
<TextBox Name="PasswordTextBox"
Watermark="Password input"
UseFloatingWatermark="True" />
Placeholder="Password input"
UseFloatingPlaceholder="True" />
<Button Name="SignUpButton"
Content="Sign up" />
</StackPanel>

4
tests/Avalonia.Generators.Tests/Views/NoNamedControls.xml

@ -1,6 +1,6 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sample.App.NoNamedControls">
<TextBox Watermark="Username input"
UseFloatingWatermark="True" />
<TextBox Placeholder="Username input"
UseFloatingPlaceholder="True" />
</Window>

12
tests/Avalonia.Generators.Tests/Views/SignUpView.xml

@ -5,15 +5,15 @@
<StackPanel>
<controls:CustomTextBox Margin="0 10 0 0"
Name="UserNameTextBox"
Watermark="Please, enter user name..."
UseFloatingWatermark="True" />
Placeholder="Please, enter user name..."
UseFloatingPlaceholder="True" />
<TextBlock Name="UserNameValidation"
Foreground="Red"
FontSize="12" />
<TextBox Margin="0 10 0 0"
Name="PasswordTextBox"
Watermark="Please, enter your password..."
UseFloatingWatermark="True"
Placeholder="Please, enter your password..."
UseFloatingPlaceholder="True"
PasswordChar="*" />
<TextBlock Name="PasswordValidation"
Foreground="Red"
@ -27,8 +27,8 @@
</ListBox>
<TextBox Margin="0 10 0 0"
x:Name="ConfirmPasswordTextBox"
Watermark="Please, confirm the password..."
UseFloatingWatermark="True"
Placeholder="Please, confirm the password..."
UseFloatingPlaceholder="True"
PasswordChar="*" />
<TextBlock x:Name="ConfirmPasswordValidation"
TextWrapping="Wrap"

4
tests/Avalonia.Generators.Tests/Views/xNamedControl.xml

@ -3,6 +3,6 @@
x:Class="Sample.App.xNamedControl">
<TextBox x:Name="UserNameTextBox"
AutomationProperties.Name="The user name"
Watermark="Username input"
UseFloatingWatermark="True" />
Placeholder="Username input"
UseFloatingPlaceholder="True" />
</Window>

8
tests/Avalonia.Generators.Tests/Views/xNamedControls.xml

@ -3,11 +3,11 @@
x:Class="Sample.App.xNamedControls">
<StackPanel>
<TextBox x:Name="UserNameTextBox"
Watermark="Username input"
UseFloatingWatermark="True" />
Placeholder="Username input"
UseFloatingPlaceholder="True" />
<TextBox x:Name="PasswordTextBox"
Watermark="Password input"
UseFloatingWatermark="True" />
Placeholder="Password input"
UseFloatingPlaceholder="True" />
<!-- Name generator should still generate members, even if XAML is invalid -->
<Button x:Name="SignUpButton"
Content="{x:Static NonExistent.Member}" />

4
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs

@ -71,14 +71,14 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Data;assembly=Avalonia.Markup.Xaml.UnitTests'>
<TextBox Name='textBox' Text='Foo' Watermark='Bar'>
<TextBox Name='textBox' Text='Foo' PlaceholderText='Bar'>
<TextBox.Template>
<ControlTemplate>
<TextPresenter Name='PART_TextPresenter'>
<TextPresenter.Text>
<MultiBinding Converter='{x:Static local:ConcatConverter.Instance}'>
<Binding RelativeSource='{RelativeSource TemplatedParent}' Path='Text'/>
<Binding RelativeSource='{RelativeSource TemplatedParent}' Path='Watermark'/>
<Binding RelativeSource='{RelativeSource TemplatedParent}' Path='PlaceholderText'/>
</MultiBinding>
</TextPresenter.Text>
</TextPresenter>

135
tests/Avalonia.RenderTests/Controls/TextBoxTests.cs

@ -0,0 +1,135 @@
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
using Avalonia.Media;
using Xunit;
namespace Avalonia.Skia.RenderTests
{
public class TextBoxTests : TestBase
{
public TextBoxTests()
: base(@"Controls\TextBox")
{
}
private static IControlTemplate CreateTextBoxTemplate()
{
return new FuncControlTemplate<TextBox>((textBox, scope) =>
{
var border = new Border
{
Background = textBox.Background,
BorderBrush = Brushes.Gray,
BorderThickness = new Thickness(1),
Padding = new Thickness(4),
};
var panel = new Panel();
// Use Antialias mode
TextOptions.SetTextRenderingMode(panel, TextRenderingMode.Antialias);
var placeholder = new TextBlock
{
Name = "PART_Placeholder",
[!TextBlock.TextProperty] = textBox[!TextBox.PlaceholderTextProperty],
[!TextBlock.ForegroundProperty] = textBox[!TextBox.PlaceholderForegroundProperty],
FontFamily = textBox.FontFamily,
FontSize = textBox.FontSize,
VerticalAlignment = VerticalAlignment.Center,
Opacity = 0.5,
}.RegisterInNameScope(scope);
var presenter = new TextPresenter
{
Name = "PART_TextPresenter",
[!TextPresenter.TextProperty] = textBox[!TextBox.TextProperty],
[!TextPresenter.CaretIndexProperty] = textBox[!TextBox.CaretIndexProperty],
FontFamily = textBox.FontFamily,
FontSize = textBox.FontSize,
}.RegisterInNameScope(scope);
panel.Children.Add(placeholder);
panel.Children.Add(presenter);
border.Child = panel;
return border;
});
}
[Fact]
public async Task Placeholder_With_Red_Foreground()
{
var target = new Border
{
Padding = new Thickness(8),
Width = 200,
Height = 50,
Background = Brushes.White,
Child = new TextBox
{
Template = CreateTextBoxTemplate(),
FontFamily = TestFontFamily,
FontSize = 12,
Background = Brushes.White,
PlaceholderText = "Red placeholder",
PlaceholderForeground = Brushes.Red,
}
};
await RenderToFile(target);
CompareImages();
}
[Fact]
public async Task Placeholder_With_Blue_Foreground()
{
var target = new Border
{
Padding = new Thickness(8),
Width = 200,
Height = 50,
Background = Brushes.White,
Child = new TextBox
{
Template = CreateTextBoxTemplate(),
FontFamily = TestFontFamily,
FontSize = 12,
Background = Brushes.White,
PlaceholderText = "Blue placeholder",
PlaceholderForeground = Brushes.Blue,
}
};
await RenderToFile(target);
CompareImages();
}
[Fact]
public async Task Placeholder_With_Default_Foreground()
{
var target = new Border
{
Padding = new Thickness(8),
Width = 200,
Height = 50,
Background = Brushes.White,
Child = new TextBox
{
Template = CreateTextBoxTemplate(),
FontFamily = TestFontFamily,
FontSize = 12,
Background = Brushes.White,
PlaceholderText = "Default placeholder",
PlaceholderForeground = Brushes.Gray,
}
};
await RenderToFile(target);
CompareImages();
}
}
}

BIN
tests/TestFiles/Skia/Controls/TextBox/Placeholder_With_Blue_Foreground.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
tests/TestFiles/Skia/Controls/TextBox/Placeholder_With_Default_Foreground.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
tests/TestFiles/Skia/Controls/TextBox/Placeholder_With_Red_Foreground.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Loading…
Cancel
Save