<Style TargetType="TextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Border BorderBrush="Red" CornerRadius="2" />
<ScrollViewer x:Name="PART_ContentHost" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Label x:Name="watermarklabel" Height="40" Content="{TemplateBinding Tag}" Foreground="Gray"/>
</Grid>
<ControlTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Value="True">
<Condition.Binding>
<MultiBinding Converter="{StaticResource Multi}">
<!--<Binding Path="Text" ElementName="txt1"/>-->
<Binding RelativeSource="{RelativeSource Mode=TemplatedParent}" Path="Text"/>
</MultiBinding>
</Condition.Binding>
</Condition>
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" TargetName="watermarklabel" Value="Collapsed"/>
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here i want to enable/disable the label (which is water mark) upon the condition when the textbox text is empty and not empty. In the multi binding i am trying to access the text property using the TemplatedParent. But it is not hitting convertor when text changed.
When i use the element name to access it everything is fine.
But i want to make generic this one..
How to make this to work?
Thanks in advance..
This is actually surprisingly tricky to get right. There are a dozen different almost-right answers on the internet, but few or no completely correct ones.
The closest I've ever come to getting this to work correctly is to dynamically inject a textblock into the standard textbox's ControlTemplate at runtime based on the conditions that you're evaluating.
Dynamic injection avoids having to re-implement the entire controltemplate and, assuming the control template doesn't transform too much between versions, also avoids the maintenance point of having to create new ControlTemplates every time built-in theming changes.
Injecting an element instead of modifying the existing text element avoids all kinds of problems where users can select/manipulate the watermark text in undesirable ways.
Avoid an overlaying solution: the z-order and clipping issues aren't worth the effort (I once tried to do this with a decorator, not the right call)
Don't forget to verify that Drag/Drop and Copy/Paste operations on the text box work as expected with the watermark.
IIRC, I did it with an attached property so that a watermark didn't require a new control. Rather, it searched the visual tree for the first viable watermark target it could find and applied the watermark there. This let it work in a combobox as well, e.g., without add'l work.
Related
The most common approach to hide a WPF control is setting Visibility attribute to Hidden or Collapsed. When I did this for sample application, and I was trying to find these elements by the automation framework, I got the items with IsOffscreen flag set to true.
For instance:
<Label Content="Hidden label" Visibility="Hidden"/>
<Label Content="Collapsed label" Visibility="Collapsed"/>
From my perspective, it looks reasonable but automation framework documentation:
https://learn.microsoft.com/en-us/windows/desktop/winauto/uiauto-automation-element-propids
says:
Objects that the end-user does not perceive at all, or that are
"programmatically hidden" (for example, a dialogue box that has been
dismissed, but the underlying object is still cached by the
application) should not be in the automation element tree in the first
place (instead of setting the state of IsOffscreen to TRUE).
How can I achieve the result which is pointed out in the documentation?
Faced similar issue, Setting Visibilty to hidden/Collapsed does not remove the element from the visual tree, even though the control is not visible visually. And currently as per my knowledge there is no other property that can remove the element from the visual tree.
As a workaround, you can make use of ContentPresenter with triggers to include or exclude controls at runtime.
For example, in the below snippet I am deciding which label to include based on a condition. it is decided at runtime and only one is included in the visual tree.
<ContentPresenter Content="{Binding}">
<ContentPresenter.Resources>
<DataTemplate x:Key="LabelContent1">
<Label Content="Dummy" />
</DataTemplate>
<DataTemplate x:Key="LabelContent2">
<Label Content="Dummy" />
</DataTemplate>
</ContentPresenter.Resources>
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="ContentTemplate" Value="{StaticResource LabelContent1}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={Binding Path=LabelText}}" Value="{x:Null}">
<Setter Property="ContentTemplate" Value="{StaticResource LabelContent2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
Reference:
https://tyrrrz.me/blog/conditional-content-presenting-via-wpf-contentpresenter
My global (App.xaml) ControlTemplate for Button doesn't want to override the Foreground property from its default black colouring. This seems to be a relatively common problem, but I've tried various solutions including setting the BasedOn to null as described in this question.
My code is below, and you can see I've tried to explicitly state the foreground colour on both the Grid and the ContentPresenter. Yet the colour stays black. Snoop tells me it's inherited as default, though doesn't seem to say from where, and shows the parent ContentPresenter as having TextElement.Foreground set to the correct colour.
Is there something in this code that I should be setting? Have I missed an element or property?
<Style TargetType="Button">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid x:Name="grid" TextBlock.Foreground="#FFD3D3D2">
<Border x:Name="border" Background="Transparent" BorderBrush="#FF5C7999" BorderThickness="2" CornerRadius="{Binding RelativeSource={RelativeSource TemplatedParent},Path=ActualHeight,Converter={StaticResource HalfConverter}}" Padding="10">
<ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,0" TextBlock.Foreground="#FFD3D3D2">
</ContentPresenter>
</Border>
</Grid>
...
Turns out that the buttons using this style were setting their content to things other than text, or explicitly adding a style-less TextBlock inside, and this was overriding anything set by the Foreground attributes in the original code.
Ensuring that only text was set as the button contents, or ensuring that the wrappers thereof were styled appropriately, fixed the problem.
It seems that rubber duck debugging works after all.
Testing this, your Style works as you expect if Button.Content is a string, but not if it's a control:
<StackPanel>
<Button Content="Gray Text As Specified in the ControlTemplate" />
<Button><Label>Default Black Text</Label></Button>
</StackPanel>
I can add an implicit Label style to ControlTemplate.Resources which overrides the Label's Foreground, but it would be ridiculous to try to have local implicit styles for every possible control somebody could put in there.
But if you just stick with plain strings for the Content, it'll work. Now I'm going to spend some time researching the inheritance rules for attached properties, because I think I'm about 51% semi-confident that this isn't the behavior I'd usually want.
So I've got a style, which does some fancy things to a wpf TabControl, among which it sets a HeaderTemplate that allows users to rename the tab. I've got a style which is based on this template that colors the text based on some parameter, and I would like to add a polygon to the header of each tab depending on some conditions.
So the base style looks like:
<Style TargetType="TabItem" x:Key="EditableTabItemStyle"
BasedOn="{StaticResource BaseTabItemStyle}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<controls:EditableTabContent Template="{StaticResource EditableTabItemTemplate}"
Content="{Binding Path=., Mode=TwoWay}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
and my derived looks like:
<Style x:Key="DerivedTabItem"
BasedOn="{StaticResource EditableTabItemStyle}"
TargetType="TabItem">
<Setter Property="Foreground">
<Setter.Value>
<MultiBinding Converter="{StaticResource StateToColorConverter}">
<Binding Path="States.State1" />
<Binding Path="States.State2" />
</MultiBinding>
</Setter.Value>
</Setter>
I'm looking to add a polygon to the derived style, preferably without changing the base style at all, so my initial thought was to add a header template:
<Setter Property="HeaderTemplate">
<Setter.Value>
<!-- Old header template -->
<!-- Polygon -->
</Setter.Value>
</Setter>
But that overrides the editable styling. I'm not sure if what I want is even possible, so I'll take that for an answer if need be, but if it can be done, how can I do it?
You can't amend an existing template. You can either:
Copy it and add anything you like.
Modify the original template to include another control that's only displayed under certain conditions (e.g. using a DataTrigger or a binding to its Visibility property).
This control could be something very flexible, such as a ContentPresenter that could display all sorts of content by binding it to some placeholder property.
In this case, where the template is so simple, I'd go with the former.
I wrote a sample to see if binding could be used within a Style in a blank Windows Store app - it compiled but did not work exactly as I'd hoped. I'm relatively new to XAML and binding so may have missed something.
In the sample below there are two rectangles, both bound to the slider control and both should change at the same time as the slider is moved, but it seems that only the first one changes; the first one is bound directly, the second is bound via a style.
Is binding in a Style supposed to be possible in a Win Store app?
(My aim is to have a slider that changes the settings on a large number of elements at once, it seemed like this would be a better approach than copy/pasting bindings to all of them)
<Grid Background="#FF87873D">
<StackPanel>
<StackPanel.Resources>
<Style x:Key="myTestRectangleStyle" TargetType="Rectangle">
<Setter Property="Fill" Value="DarkBlue" />
<Setter Property="Margin" Value="10,10" />
<Setter Property="Height" Value="30" />
<Setter Property="Width" Value="{Binding ElementName=slider1, Path=Value}" />
</Style>
</StackPanel.Resources>
<Rectangle Width="{Binding ElementName=slider1, Path=Value}" Fill="Black" Margin="10,10" Height="30"/>
<Rectangle Style="{StaticResource myTestRectangleStyle}"/>
<Slider Name="slider1" Minimum="20" Maximum="200" Margin="20,0"/>
</StackPanel>
</Grid>
Answering my own question...it seems this isn't possible on a Windows Store App.
I had a clarification from a user on a MSDN forum that
[Bindings] are not supported on Style setters in Windows Store Apps like
they are in WPF, i.e. you cannot bind to the Value property of the
Slider in the Style
So the workaround is just to set the binding directly outside of the Style (a long winded option if you have a lot of elements to bind unfortunately)
I'm trying to label text boxes with the name of the field which is to be put into them, and currently I have everything working in a static manner (I have to copy and paste the style into each new textbox and change the text to the appropriate field name). Is there a way to do this dynamically, so that I write a style that accesses the TextBox's name, replaces the "_"s with " "s and then puts that in my visualbrush's textblock? I'd appreciate research links or the names of relevant topics, I'm just not sure what to be looking for.
Here is my style:
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock FontSize="32" Foreground="Gray" >First Name</TextBlock>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
If you find yourself copying and pasting lots of the same code, then you probably want to create a Custom Control. That would probably be the simplest solution (and, generally speaking, the simplest solution is the best solution).
Here is one of many tutorials I found by Googling WPF Custom Control.
Another possibility (which could be used in conjunction with or instead of the custom control) is the use of Attached Properties.