TextBlock with vertical scrollbar only - wpf

I have a TextBlock which may contain a long text so I want to add a vertical scroll bar to it. My initial attempt was to wrap a ScrollViewer around it. That works but the problem is that when I zoom in, the width is zoomed also. I tried disabling the horizontal scroll bar like this:
<ScrollViewer IsTabStop="True" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
But it didn't solve the problem. I also tried binding the width:
Width="{Binding ElementName=Scroller, Path=ViewportWidth}"
It didn't help either.
So, my question is, how can I add vertical scrollbar to it but have a fixed width and wrapped text for the TextBlock inside? Here's my full code:
<ScrollViewer Grid.Row="1" IsTabStop="True" VerticalScrollBarVisibility="Auto">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Top" TextWrapping="Wrap" TextAlignment="Center"/>
</ScrollViewer>

There are two parts to this answer... the first is to simply use a TextBox:
<TextBox ScrollViewer.VerticalScrollBarVisibility="Visible" Text="Something really
really really really really really really really really long"
Style="{StaticResource TextBlockStyle}" />
The second part is to simply Style the TextBox so that it looks like a TextBlock:
<Style x:Key="TextBlockStyle" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="TextWrapping" Value="Wrap" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
Feel free to remove any of these properties if they do not suit your situation.

<TextBox HorizontalAlignment="Center"
VerticalAlignment="Top"
TextWrapping="Wrap"
TextAlignment="Center"
VerticalScrollBarVisibility="Auto" Width="300" Style="{StaticResource TextBlockStyle}"/>
You don't need a ScrollViewer wrapped in the TextBox, the TextBox control has its own ScrollViewer. And you need to define the width of the TextBox so that the scrollbar will know its fixed width and will wrap the text.
Then, you have to style the TextBox to look like a TextBlock
A good reason why this ScrollViewer won't work according to to Ifeanyi Echeruo from Microsoft, from MSDN
ScrollViewer first asks its content how large it would like to be in
the absence of constraints, if the content requires more space than
the Viewer has then its time to kick in some ScrollBars
In the absence of constraints TextBlock will always opt to return a
size where all text fits on a single line.
A ScrollViewer with ScrollBars will never get a TextBlock to wrap.
However you may be able to come up with a Measure\Arrange combination
for a panel of your own that is almost like ScrollViewer but I cant
think of any logic that can satify both constraints without explicit
knowlege of the behaviour of said children

Related

WPF Textblock Tooltip does not wrap

I am trying to create a tooltip that wraps automatically (and also has an advanced mode that takes normal content, but that's later). Anyway, I'm setting the content as a string and making the content just a textblock with wrapping. However I can't figure out why this isn't working. Here is the style I'm working on:
<Style x:Key="StHelpLinkBase" TargetType="{x:Type graphicElements:MyHelpLink}">
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="{StaticResource BrHelpLinkBackground}" />
<Setter Property="Width" Value="12" />
<Setter Property="Height" Value="12" />
<Setter Property="Margin" Value="5" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type graphicElements:MyHelpLink}">
<Grid x:Name="templateRoot">
<Image Source="Images/Icon_16_Help.png" Stretch="UniformToFill" MaxHeight="16" MaxWidth="16"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
x:Name="PART_Image">
<Image.ToolTip>
<ToolTip Background="{TemplateBinding Background}" BorderThickness="0"
DataContext="{Binding DataContext, ElementName=PART_Image}"
TextElement.Foreground="{TemplateBinding Foreground}"
ContentTemplate="{StaticResource DtTooltipAdvanced}"
MaxWidth="150"
x:Name="PART_Tooltip">
<ContentPresenter />
</ToolTip>
</Image.ToolTip>
</Image>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is the basic template referenced:
<DataTemplate x:Key="DtTooltipBasic">
<Grid>
<TextBlock Text="{Binding Content, RelativeSource={RelativeSource AncestorType=ToolTip}}"
TextWrapping="Wrap"
Foreground="White"
Margin="15"
FontFamily="Resources/#Artifakt Element"
FontSize="9pt" />
</Grid>
</DataTemplate>
And here is the usage (MyHelpLink inherits from ContentControl):
<graphicElements:MyHelpLink Content="This is some help text that is long and is just set as straight string in content but it should wrap I hope." />
I've tried setting the MaxWidth on the tooltip as I have it now, I've tried setting it on the Grid that is in the DataTemplate, and I've tried setting it on the textblock itself and all just cut off the text. I also tried setting the Width property of the textblock directly and same thing...
So why doesn't this wrap?
Ok well I still don't know why this didn't work but I ended up with another solution. Through some experimenting I found that if I put the textblock directly inside the control template instead of a data template it worked and wrapped correctly. However in order to switch it I couldn't use it that way.
So what I did was make two control templates; one with a wrapping textblock for generic content and one with ContentPresenter for non-string content. I then made the style with a trigger on the content type (I made a custom readonly dependency property in my class denoting to trigger the change if the content is anything except a string). The trigger changes the template from the wrapping textblock to the content presenter depending on the type of content set.
If anyone knows why it doesn't work inside a DataTemplate I would love to know and will mark as the answer...

Why is my Fluent Window title staying centered?

I'm using the latest version of Fluent.Ribbon. I've been doing some styling, most of which requires completely replacing the Styles and ControlTemplates, but I've hit a snag. The title of my app is centered in the header bar and I can't get it to move to the left.
My visual tree looks like this:
MainWindow
Grid
Adorner
Grid
DockPanel
PART_Icon
PART_RibbonTitleBar
Grid
PART_HeaderHolder [ContentPresenter]
TextBlock
PART_ItemsContainer
PART_QuickAccessToolbarHolder
I copied the current version of the Fluent:RibbonTitleBar ControlTemplate and Style into my override xaml for modification, but nothing I do makes any difference (yes it is loading my overriding styles.)
When I use the inspector tool in the app, the only elements I can highlight are the innermost TextBlock, which fits the text exactly with no stretch, and the DockPanel several levels above, which stretches the full window width. In the original window ControlTemplate, which you can see here, The RibbonTitleBar is the last element of the DockPanel which has LastChildFill set. The RibbonTitleBar does have a RenderSize of the full width, but then the Grid below it has a RenderSize of 0,0. Then PART_HeaderHolder inside that has a RenderSize that exactly covers the title text.
It doesn't seem to matter if I set HorizontalAlignment on various elements to Left or Stretch. I also tried changing the innermost Grid to other container types such as DockPanel and StackPanel. Nothing changes anything about the layout.
Here's my style overrides for the RibbonTitleBar. The only change I've made is that I moved the QuickAccessToolbar to the end and permanently collapsed it (if I try deleting it, the app crashes looking for it) and I tried defining some columns on the inner Grid to no avail.
<Style TargetType="{x:Type Fluent:RibbonTitleBar}">
<Setter Property="Template"
Value="{DynamicResource RibbonTitleBarControlOverride}" />
<Setter Property="Focusable"
Value="False" />
<Setter Property="VerticalAlignment"
Value="Top" />
<Setter Property="HorizontalAlignment"
Value="Stretch" />
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Margin="-2,0"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
Text="{Binding}"
TextWrapping="NoWrap"
TextTrimming="CharacterEllipsis" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="RibbonTitleBarControlOverride"
TargetType="{x:Type Fluent:RibbonTitleBar}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0" x:Name="PART_HeaderHolder"
HorizontalAlignment="Left"
ContentSource="Header"
IsHitTestVisible="False" />
<Fluent:RibbonContextualGroupsContainer Grid.Column="1" x:Name="PART_ItemsContainer"
IsItemsHost="True" />
<ContentPresenter x:Name="PART_QuickAccessToolbarHolder"
ContentSource="QuickAccessToolBar" Visibility="Collapsed" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsCollapsed"
Value="True">
<Setter Property="Visibility"
Value="Collapsed"
TargetName="PART_ItemsContainer" />
</Trigger>
<Trigger Property="HideContextTabs"
Value="True">
<Setter Property="Visibility"
Value="Collapsed"
TargetName="PART_ItemsContainer" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>

WinRT horizontal menu autosize width of textblock so not truncated

I am trying (on WinRT Universal App) to have a top menu that doesn't have truncated text.
I get my items from my server and show them to my user, the problem is that I am showing items have 4 characters and some have 10 (or in-between). So some of them are truncated:
What i would like is to have a a textblock that can auto re-size it self, so that the word is not truncated, does anyone know how to do this?
here is my XAML code:
<GridView ItemsSource="{Binding Channels}"
SelectionMode="None"
IsRightTapEnabled="False"
IsSwipeEnabled="False"
IsItemClickEnabled="True"
Margin="5">
<GridView.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource ChannelMenuHyperButtonStyle}"
Text="{Binding Name}"
Margin="10,0,10,0">
</TextBlock>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Vertical" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
Style code:
<Style x:Key="ChannelMenuHyperButtonStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource DmBlueBrush}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="18" />
<Setter Property="Padding" Value="5,5,5,5" />
</Style>
thanks you for your help!
As you only need one row of items I would suggest to use a VirtualizingStackPanel as the ItemsPanel (Instead of a WrapGrid). Don't forget to set it's orientation to horizontal.
A Stackpanel would also work but it might become heavy in memory if you have a very high amount of categories.
Why? Because GridViews and Listviews use a WrapGrid as the default ItemsPanel, all items in an WrapGrid will have the same size (the size of the first one will be applied to all of them). They have this limitation because they are virtualized panels that can automatically layout their items in multiple rows/colums.
I believe the Issue is that you have both margin and padding applied. For Titles that have more than 6 Char's the overlapping padding/margin is "truncating" your data.
Consider removing the Padding value from the horizontal lines in the Style "ChannelMenuHyperButtonStyle".
<Style x:Key="ChannelMenuHyperButtonStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource DmBlueBrush}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="18" />
<Setter Property="Padding" Value="0,5,0,5" />
</Style>
Remember Margin is making space from the outside of the bounds, padding is making space from inside the bounds.

WPF ControlTemplate Height

I have the following style for validating input in my controls:
<Style x:Key="MyErrorTemplate" TargetType="Control">
<Style.Setters>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate x:Name="ControlErrorTemplate">
<StackPanel Orientation="Vertical" Height="Auto">
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="Red" FontSize="20">!</TextBlock>
<AdornedElementPlaceholder x:Name="Holder"/>
</StackPanel>
<Label Foreground="Red" Content="{Binding ElementName=Holder,
Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
If an error happens, the error message in the label appears under the control (e.g. textbox) and overlaps the control below. I made StackPanel's Height="Auto", but it didn't help. Each control is in a Grid cell, and the Grid's row Height is also Auto.
Could you please tell me what I am missing? I want the error message to push what is below down.
Thanks.
Validation.ErrorTemplate shows error feedback on an adorner layer. This means all controls in this template will not be considered when the layout system is measuring and arranging the controls on the adorned element layer.
I found this and thanks LPL, i did not know that about the adorner layer.
My solution was a margin "hack". I just used the trigger:
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="Margin" Value="0,0,0,28"/>
</Trigger>
</Style.Triggers>
To increase the bottom margin of the adorned textbox. I set the margin large enough to make room for a single string textblock/label and then the content below was moved down

Efficiently display text on image in WPF?

How to display text on an image, so it should always visible (because the image colors are mixed and unpredictable)?
I thought about two options:
Making the text border in white while the text itself will be black
Having the text displayed negatively to the picture
The 1st option would be preferred since it looks more solid.
Embedding the text is simple:
<Grid>
<Image Source="{Binding ImageLink}" Width="110" />
<TextBlock Text="{Binding Description}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
Update on answer:
Sounds like a great idea except it doesn't work.
I tried your code, and here are the results:
The left image is when I set the Color property to White and ShadowDepth to 10.
I did this and it helps:
<Style x:Key="AnnotationStyle" TargetType="TextBlock">
<Setter Property="Background" Value="#70FFFFFF" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="TextAlignment" Value="Center"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#CCFFFFFF" />
</Trigger>
</Style.Triggers>
</Style>
....
<TextBlock ... Style="{StaticResource AnnotationStyle}"/>
Here is what it looks like:
The best way to make the text more highlighted or contrasted is by using any effect, particularly the shader effects.
Microsoft is also make bitmap effect obsoleted since .NET 3.5 SP1, therefore your best bet is using any shader effect or create your own.
For example (from Karl Shifflett), you can use DropShadowEffect to "outline" your text but set the ShadowDepth to 0:
<Grid>
<Image Source="{Binding ImageLink}" Width="110" />
<TextBlock Text="{Binding Description}"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock.Effect>
<DropShadowEffect ShadowDepth="0" Color="Blue" BlurRadius="10" />
</TextBlock.Effect>
</TextBlock>
</Grid>
For more sample, you can google WPF effects.
UPDATE: You can also turn off antialiasing on text by using attached property of TextOptions.TextRenderingMode and set it to "Aliased", or you can also use TextOptions.TextFormattingMode and set to "Display".
Try and compare this and see if it will fit your needs:
<StackPanel>
<TextBlock>
Hello World ... Ideal text formatting
</TextBlock>
<TextBlock TextOptions.TextFormattingMode="Display">
Hello World ... Display text formatting
</TextBlock>
</StackPanel>
Hope this helps.

Resources