How do I make WPF TextBox acted as a compact form?
This means that "label text" inside TextBox is hiding on the click or when there is "real text".
See working prototype
You can use a data trigger in the style for the TextBox, and set the background to something that includes your text. A sample that works simply would be this:
<TextBox>
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Text, RelativeSource={RelativeSource Self}}" Value="">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None" AlignmentX="Left">
<VisualBrush.Visual>
<TextBlock Text="This is the label text" Foreground="Silver" />
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Tested in Kaxaml; the "label text" alignment isn't quite right, and you'd maybe want to refactor this into something more reusable for production code.
EDIT: to cover the "Hide on focus" scenario you'd need a more complicated trigger. Replace the datatrigger with this:
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Text, RelativeSource={RelativeSource Self}}" Value="" />
<Condition Binding="{Binding IsKeyboardFocused, RelativeSource={RelativeSource Self}}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None" AlignmentX="Left">
<VisualBrush.Visual>
<TextBlock Text="This is the label text" Foreground="Silver" />
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</MultiDataTrigger>
Related
I am new to wpf. Actully i want to change button icon(play.ico/stop.ico).
when i click the button it changes status(status is database column) value 'Online' to Offline, or Offline to Online.
It updating in database also but
I want to show when status is online buttton should show stop.ico and when I status is offline button should show play.ico.
How to achieve this? I tried with below code but its not working. please help and suggest what i am missing something.
//Xaml code
<StackPanel Margin="0 0 0 0" Grid.Column="5" Grid.Row="0" Grid.RowSpan="2">
<Button Name="Ignition_Button1" Click="Ignition_Button1_Click_1" Width="35" Height="35" Margin="16,5,16,0"
Style="{DynamicResource CircleButton}"
Command="{Binding StartStopCommand}">
<Button.ToolTip>
<ToolTip>
<StackPanel>
<TextBlock FontWeight="Bold"
Text="Start or Stop Control Center" />
</StackPanel>
</ToolTip>
</Button.ToolTip>
<StackPanel Orientation="Vertical">
<Rectangle Width="15" Height="15" StrokeThickness="0">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Fill" Value="{StaticResource play.ico}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Status, UpdateSourceTrigger=PropertyChanged}" Value="Online">
<Setter Property="Fill" Value="{StaticResource stop.ico }" />
</DataTrigger>
<DataTrigger Binding="{Binding Status, UpdateSourceTrigger=PropertyChanged}" Value="Offline">
<Setter Property="Fill" Value="{StaticResource play.ico}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</StackPanel>
</Button>
</StackPanel>
Several things I would suggest here.
Rather than ico which are bitmap and will have jaggies, I would recommend geometries and paths.
With a button which has two states you toggle between then a togglebutton is a good candidate.
Below I put my two geometries in resources of my togglebutton.
These are more usually put into a resource dictionary (along with the many other geometries used for iconography) and merged in app.xaml.
<ToggleButton IsChecked="True"
IsThreeState="False"
>
<ToggleButton.Resources>
<Geometry x:Key="StopGeom">
M0,0L32,0 32,32 0,32z
</Geometry>
<Geometry x:Key="PlayGeom">
M0,0L16,8 32,16 16,24 0,32 0,16z
</Geometry>
</ToggleButton.Resources>
<Path Fill="Black"
Stretch="Uniform"
Margin="4">
<Path.Style>
<Style TargetType="Path">
<Setter Property="Data" Value="{StaticResource PlayGeom}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType={x:Type ToggleButton}}}" Value="True">
<Setter Property="Data" Value="{StaticResource StopGeom}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</ToggleButton>
This needs more work but should give you the idea of one way to do this.
For more about layout and geometries:
https://social.technet.microsoft.com/wiki/contents/articles/32610.wpf-layout-lab.aspx
I don't know how you're styling the shape of your button, but I use this one to emphasise to wpf newbies what lookless controls means:
https://social.technet.microsoft.com/wiki/contents/articles/29866.aspx
I'm using framework 4.0.
How to display grayed text in editable combobox when it is empty and not focused like in picture
UPDATE:
There is no direct way to implement in WPF except using a TextBlock and ComboBox and laying them over each other.
<Grid HorizontalAlignment="Left" Height="21.545" VerticalAlignment="Top" Width="120.964" Margin="56.958,108.962,0,0">
<ComboBox IsEditable="True" Name="myComboBox" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120">
<!--Items Here-->
</ComboBox>
<TextBlock Text="Please Select" IsHitTestVisible="False" Foreground="Gray" x:Name="textBlock" HorizontalAlignment="Left" TextWrapping="Wrap" Margin="3.993,2.664,0,0">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Setters>
<Setter Property="Visibility" Value="Hidden" />
</Style.Setters>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Text, ElementName=myComboBox, Mode=OneWay}" Value=""/>
<Condition Binding="{Binding IsKeyboardFocusWithin, ElementName=myComboBox, Mode=OneWay}" Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Visibility" Value="Visible" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
Sorry about this. I misunderstood the question previously. This answer is for UWP.
I think placeholder text is what you are looking for. Check this out ComboBox.PlaceholderText property in MSDN Docs.
I have search hint textbox
<TextBox
TextChanged="textboxsearch_TextChanged"
Grid.Column="4" Margin="0,0,10,10" Height="22" >
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Background" Value="{StaticResource SearchHint}" />
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Background" Value="White" />
</Trigger>
</Style.Triggers>
<Setter Property="VerticalAlignment" Value="Bottom"/>
</Style>
</TextBox.Style>
</TextBox>
here is SearchHint style
<VisualBrush x:Key="SearchHint" Stretch="None">
<VisualBrush.Visual>
<TextBox FontStyle="Italic" Background="White" Foreground="Gray" Text="Enter search text…" />
</VisualBrush.Visual>
</VisualBrush>
The search box back ground is filled by the searchhint style. The problem I have now is how can I make the width of the visual brush fill the size of the textbox. Right now it fills only a portion of the textbox.
The Text="Enter search text…" has a white background but the rest of the textbox is gray. I wanted to have a white background with gray hint text.
Give the TextBox in the VisualBrush a large padding (to the right), and give the VisualBrush a left alignment:
<VisualBrush x:Key="SearchHint" Stretch="None" AlignmentX="Left">
<VisualBrush.Visual>
<TextBox FontStyle="Italic" Background="White" Foreground="Gray" Text="Enter search text…"
Padding="0,0,1000,0" />
</VisualBrush.Visual>
</VisualBrush>
Well for what you're trying you can bind the Width of the control in the VisualBrush to your actual target's ActualWidth
something like:
<VisualBrush x:Key="SearchHint"
Stretch="None">
<VisualBrush.Visual>
<TextBox Background="White"
FontStyle="Italic"
Foreground="Gray"
Width="{Binding ElementName=tb, Path=ActualWidth}"
Height="{Binding ElementName=tb, Path=ActualHeight}"
Text="Enter search text…" />
</VisualBrush.Visual>
</VisualBrush>
...
<TextBox Grid.Column="4"
Height="22"
TextChanged="textboxsearch_TextChanged"
x:Name="tb"
Margin="0,0,10,10">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text"
Value="">
<Setter Property="Background"
Value="{StaticResource SearchHint}" />
</Trigger>
<Trigger Property="IsKeyboardFocused"
Value="True">
<Setter Property="Background"
Value="White" />
</Trigger>
</Style.Triggers>
<Setter Property="VerticalAlignment"
Value="Bottom" />
</Style>
</TextBox.Style>
</TextBox>
However
What you're "functionally" trying to do is referred to commonly as "Watermark" / "Placeholder text" and it's much simpler to use that approach than have a complicated VisualBrush with an actual control being turned into a Brush. Just my opinion.
If you want to try the Watermark approach This Answer gives a good example for you to work with.
Why is there no tooltip text on errors?
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<Border ...>
<AdornedElementPlaceholder ...
ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Border>
...
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I also noticed that
<AdornedElementPlaceholder ...
ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
fails but the below suceeds, even with the same binding, why is this so? Doesn't AdornedElementPlaceholder refer to the text box? Even if it doesn't, shouldn't a tooltip appear somewhere?
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
I know I'm late, but let me share a solution I found studying this question: WPF custom validator with tooltip.
In it's simplest form this ErrorTemplate shows only a Tooltip with the ErrorContent for the whole AdornedElement.
<ControlTemplate x:Key="validationTemplate">
<Grid Background="Transparent"
ToolTip="{Binding Path=/ErrorContent}">
<AdornedElementPlaceholder />
</Grid>
</ControlTemplate>
But of course you can decorate it as desired e.g. with a Tooltip for just a marker.
<ControlTemplate x:Key="validationTemplate">
<Grid>
<Ellipse Fill="Red" Opacity="0.8" Width="10" Height="10"
HorizontalAlignment="Right" VerticalAlignment="Top"
ToolTip="{Binding Path=/ErrorContent}" />
<AdornedElementPlaceholder />
</Grid>
</ControlTemplate>
Put this Template in Resources and all you have to do is setting the Validation.ErrorTemplate.
Validation.ErrorTemplate="{StaticResource validationTemplate}"
Even this annoying Trigger is no longer needed.
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
You can't place a tooltip on the AdornedElementPlaceholder, I don't think it's visible at all, it's just reserving space for whoever uses it (in your case a TextBox). Looking at the Visual Tree with Snoop we can see that the TemplatedAdorner ends up in a different place in the VisualTree than the TextBox so there will be now way for us to find the TextBox from the VisualTree. We can find it through AdornedElement, but we still won't be able to set a tooltip.
The only thing visible here in the TemplatedAdorner is the Border. The Border knows its Child - the TemplatedAdorner - which in turn knows its AdornedElement - the TextBox. So we could set the ToolTip for the Border with this. (However, this Binding seems to fail to update the Tooltip for the Border. It works when I look at it with Snoop and after that it displays.)
<Border BorderBrush="Red"
BorderThickness="4"
ToolTip="{Binding RelativeSource={RelativeSource self},
Path=Child.AdornedElement.(Validation.Errors)[0].ErrorContent}">
So, the TextBox has its AttachedProperty Validation where we can find the ErrorContent so it must set its own ToolTip like you did at your last example, otherwise it won't work.
I found a way to implement ToolTip with the returned error message from the validation class that you might create to validate your input.
First: Binding the error message
Adding <Style> for the TextBox with Style.Trigger as followed:
<Style TargetType="{x:Type TextBox}" x:Key="ToolTipError">
<!-- Some style setters -->
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
Second: Add the style to TextBox
<TextBox
Style="{StaticResource ToolTipError}"
Validation.ErrorTemplate="{StaticResource validationTemplate}">
<TextBox.Text>
<Binding
Path="YourViewModelProperty"
UpdateSourceTrigger="PropertyChanged"
ValidatesOnNotifyDataErrors="True"
ValidatesOnDataErrors="True"
NotifyOnValidationError="True">
<Binding.ValidationRules>
<ExceptionValidationRule:DateValidationRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Bonus!
You can change the ugly red border with other thing.
For example you can change it to red exclamation mark:
<Window.Resources>
<ControlTemplate x:Key="validationTemplate">
<StackPanel>
<TextBlock Text="!" FontSize="26" Foreground="Red"/>
<AdornedElementPlaceholder/>
</StackPanel>
</ControlTemplate>
<Window.Resources>
Is there a way to display the error contents in a TextBlock below the control similar to how the following sets the Tooltip to contain the error text?
<Style x:Key="textBoxInError" TargetType="Control">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel>
<TextBlock DockPanel.Dock="Left" Foreground="Red" FontWeight="Bold">*</TextBlock>
<TextBlock Text="WOULD LIKE TO SHOW WHAT TOOLTIP IS SHOWING" DockPanel.Dock="Bottom" Foreground="Red"/>
<Border BorderBrush="Red" BorderThickness="2">
<AdornedElementPlaceholder/>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
In other words, I rather show the error message in a TextBlock below the control instead of a Tool Tip.
The DataContext of the ErrorTemplate is already the value of Validation.Errors, so you can just do:
<TextBlock Text="{Binding [0].ErrorContent}" DockPanel.Dock="Bottom" Foreground="Red"/>
or
<TextBlock Text="{Binding ErrorContent}" DockPanel.Dock="Bottom" Foreground="Red"/>
I struggled with this exact problem, and all the SO answers I could find to this or to similar questions were either using the "Validation.ErrorTemplate" property which is unfortunately rendered on a separate UI layer which means the parent control won't resize its content like #statikuz mentionned and hide the following controls, or just not generic enough.
I finally ended up with this solution :
<Style x:Key="hiddenTextblockErrorText" TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="Foreground" Value="DarkRed"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Self}, Path=Tag.(Validation.Errors)/ErrorContent}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Tag.(Validation.HasError)}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
And you use it like this :
<TextBox Name="someField" Height="20">
<TextBox.Text>
<Binding Path="SomeProperty" Mode="TwoWay"UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:IsRequired/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBlock Tag="{Binding ElementName=someField}" Style="{StaticResource hiddenTextblockErrorText}"/>
So you basically just bind your field to the Tag property of your error TextBlock, and you can use it from there to get the (Validation.Errors) attached property.
Note that you will get an harmless warning "Property Errors is not attachable to element of type Object" but it is working perfectly fine (I wasn't able to find how to do a cast here).
Alternatively you can use a Label and the Target property instead of Tag and you won't get any warning, but you lose the TextWrapping feature unless you also override the template so it's a bit more verbose :
<Style x:Key="hiddenLabelErrorText" TargetType="Label">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="Foreground" Value="DarkRed"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<TextBlock TextWrapping="Wrap" Text="{Binding RelativeSource={RelativeSource AncestorLevel=1, AncestorType={x:Type Label}},
Path=Target.(Validation.Errors)/ErrorContent}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Target.(Validation.HasError)}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
<Label Target="{Binding ElementName=someField}" Style="{StaticResource hiddenLabelErrorText}"/>