ContentControl not aligning with text within a TextBlock - c#

I have a Hyperlink with various bindings that I have put into a DataTemplate to ensure the code isn't repeated. To use this I specify a ContentControl with a ContentTemplate. However, using a ContentControl inline with TextBlock text causes the link to be offset. I've got down to the following test case where this occurs:
<TextBlock>Text with a <ContentControl>Inline content control</ContentControl> in it.</TextBlock>
The only way I've found to get around this is to specify a negative margin on the ContentControl, but obviously this is not ideal as it will not work when the font size changes.

I don't think it's possible to get a ContentControl to behave the same as an inline element, and the only way to get a ContentControl to stay inline with the text would be to modify padding/baseline etc. which can't respond to DPI changes and is obviously a bit of a hack.
I used Grx70's suggestion and instead moved my bindings into a style which I then set on the Hyperlink as needed:
<Style x:Key="CustomHyperlink" TargetType="{x:Type Hyperlink}" BasedOn="{StaticResource {x:Type Hyperlink}}">
<Setter Property="NavigateUri" Value="{Binding TheUri}"/>
<Setter Property="Command" Value="{Binding TheCommand}"/>
<Setter Property="CommandParameter" Value="{Binding NavigateUri, RelativeSource={RelativeSource Self}}"/>
</Style>
<TextBlock TextWrapping="Wrap">
...please <Hyperlink Style="{StaticResource CaseHyperlink}">view in browser</Hyperlink> and...
</TextBlock>

Related

Bind foreground attribute for child text of textblock element

I've discovered that I cannot set the foreground of text that is contained within a textblock as a child (as opposed to the text being set via the Text attribute on the textblock), by simply using the Foreground attribute on the parent textblock. I've tried setting the attributes TextElement.Foreground and Run.Foreground, but neither work. Does anyone know the preferred method in this case?
Here is my code thus far:
<TextBlock Foreground="{Binding ForegroundColor}">
Sample<LineBreak/>Text
</TextBlock>
edit: Here's an interesting twist: if I take the <LineBreak/> out, the foreground sets just fine
Make sure that you don't have an implicit Run style defined somewhere in your application:
<Style TargetType="Run">
<Setter Property="Foreground" Value="Gray" />
</Style>
This one will override the Foreground setting of the parent TextBlock.
If you do you could add an implicit Run style to the TextBlock:
<TextBlock>
<TextBlock.Resources>
<Style TargetType="Run">
<Setter Property="Foreground" Value="Orange" />
</Style>
</TextBlock.Resources>
Sample<LineBreak/>Text
</TextBlock>

WPF Button ControlTemplate won't override Inherited Foreground

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.

WPF using style that contains another style

I'm trying to make a progress bar style that is re-usable.
So the idea here is very simple. I have a style already with target type ProgressBar, and it's just a spinny circle that fills as it goes from 0-100%. However, in order to make it re-usable and modular, I do not want to hard-code the text that goes along with it - it should be optional.
So I want to create another style that DOES include text "Downloading... X/Y MB". For this I take Value for X, Maximum for Y, and Tag for the unit. I want to include the same spinny circle thingy for the graphical part on the left. How can I do this? With BasedOn property, I think you can only set something already there to be different. What if I want to add additional elements (like textblocks in this case)?
If only text value differentiates then within style you can bind propertie's value to parent's property like Tag where at every single either style or element you adjust it to specific requirement.
<Window.Resources>
<Style x:Key="FirstButtonStyle" TargetType="Button">
<Setter Property="Content" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SecondButtonStyle" TargetType="Button" BasedOn="{StaticResource FirstButtonStyle}">
<Setter Property="Content" Value="2"/>
</Style>
</Window.Resources>
<StackPanel>
<Button Style="{StaticResource FirstButtonStyle}"/>
<Button Style="{StaticResource SecondButtonStyle}"/>
</StackPanel>
The outcome is 1 and 2. If your intent is to inject some UI element within style then there is no such an option, alas. Style needs to be rewritten once again.

WPF: TextTrimming on a ContentPresenter

Is there a simple way to just get TextTrimming to work with a ContentPresenter?
I have implict styles for TextBlock and AccessText that have TextTrimming set to CharacterEllipsis, but it's not picked up by the ContentPresenter. I can change the ContentPresenter to an AccessText or TextBlock and set it there, but then the template only handles text content.
Any suggestions?
Thanks!
Implicit Styles for elements that derive from UIElement, but not Control, are not applied if the element is defined in a control's Template unless the implict Style is defined in the application Resources. The same holds true for TextBlocks used by ContentPresenter.
For example, in the following XAML the TextBlock that is ultimately used to present the button's content will not get the implicit Style:
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Red" />
</Style>
</Window.Resources>
<StackPanel>
<Button Content="Will not be red" />
<TextBlock Text="Will be red" />
</StackPanel>
If you take that exact same Style and move it to the application's Resources, then both will be red:
<Application.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Red" />
</Style>
</Application.Resources>
So you can either move your implicit Style to application resources, which is generally not a good idea. Or you can customize the display for the specific scenario you have. This can include adding an implicit DataTemplate, or customizing a control's Template.
If you can provide more information, then it would be easier to know which is the best approach.
Thanks to this Gist by James Nugent: "WPF style which puts character ellipsis on button contents without replacing the ContentPresenter with a TextBlock and thus losing the ability to support access keys."
This worked for me:
<ContentPresenter.Resources>
<Style TargetType="TextBlock">
<Setter Property="TextTrimming" Value="CharacterEllipsis"></Setter>
</Style>
</ContentPresenter.Resources>

Setting a style on Control type not working

I'm writing a very basic WPF dialog and want to apply a simple style to all objects that inherit from the Control class. The code I'm using:
<Window.Resources>
<Style TargetType="{x:Type Control}">
<Setter Property="Margin" Value="20"/>
</Style>
</Window.Resources>
<StackPanel>
<TextBlock Text="some text"/>
<TextBox x:Name="x_NameTextBox"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="x_CancelButton" Click="x_CancelButton_Click" Content="Cancel"/>
<Button x:Name="x_OkButton" Click="x_OkButton_Click" Content="OK"/>
</StackPanel>
</StackPanel>
</Window>
The Style defined above doesn't change the layout of the window at all unless I specify a key and set the style on each individual object, which is exactly what I'm trying to avoid. It also works for more specific types (setting the TargetType to Button, for example.)
Any ideas why this isn't working?
Every control when it gets instantiated it gets its Style from the explicitly defined resource or look for the immediate parent where it can get a default style. In your case the Button control will get its default Style from the platform because your App haven't defined one. Now that platform Button Style has no way to know about your custom defined Control base style. Because styles will look for a base style only when you explicitly define BasedOn
So you got only two ways
1. Define Style for every control - which you don't want I think.
2. Define Styles for the controls you are interested and set the BasedOn
<Style TargetType="{x:Type Control}">
<Setter Property="Margin" Value="20"/>
</Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Control}}">
</Style>

Categories