Wrapping sequence of tiles in FlowDocument - wpf

Is it possible to have a FlowDocument contain a sequence of fixed-size tiles that wraps?
I have tried using Floater but can only get the following effect. Notice that the tiles are fixed-size except for the ones on the right edge (which have collapsed instead of falling to the next line):
<Window>
<Window.Resources>
<Style TargetType="Floater" BasedOn="{StaticResource {x:Type Floater}}">
<Setter
Property="HorizontalAlignment"
Value="Left"/>
<Setter
Property="LineHeight"
Value="1in"/>
<Setter
Property="Margin"
Value="0"/>
<Setter
Property="Padding"
Value="0"/>
<Setter
Property="Width"
Value="1in"/>
</Style>
</Window.Resources>
<FlowDocument>
<Paragraph>
<TextBlock>
Hello, world!
</TextBlock>
<Floater>
<BlockUIContainer>
<Grid
Width="1in"
Height="1in"
Background="Blue">
<TextBlock
Foreground="Red">
Tile
</TextBlock>
</Grid>
</BlockUIContainer>
</Floater>
<Floater>
...
</Floater>
...etc
</Paragraph>
</FlowDocument>
</Window>

I've found two possible solutions:
InlineUIContainer
InlineUIContainer by itself appears to drop to the next line as necessary. So when you use a bunch of them together:
<FlowDocument>
<Paragraph >
<InlineUIContainer>
...
</InlineUIContainer>
...
</Paragraph>
</FlowDocument>
...then you get something like this:
If you put the InlineUIContainers inside a TextBlock inside the Paragraph (and set TextBlock.TextWrapping="Wrap") then the tiles will no longer evenly distribute themselves horizontally but will stack up on the side like you might expect. And you can get zero space between them by ensuring that there is no line break or other whitespace between any closing and opening tags of InlineUIContainer. Then it looks like this:
Figure
Figure appears to be intended more for positioning images, but by setting its Margin and Padding to zero and giving it a fixed Width and setting its HorizontalAnchor:
<Window>
<Window.Resources>
<Style TargetType="Figure">
<Setter
Property="HorizontalAnchor"
Value="ColumnLeft"/>
<Setter
Property="Margin"
Value="0"/>
<Setter
Property="Padding"
Value="0"/>
<Setter
Property="Width"
Value="1in"/>
</Style>
</Window.Resources>
<FlowDocument>
<Paragraph>
<Figure>
<BlockUIContainer>
...
</BlockUIContainer>
</Figure>
...
</Paragraph>
</FlowDocument>
</Window>
...then you get something like this in Scroll Mode:
HOWEVER: in Page Mode then you get something entirely different (notice how eagerly it stacks):
Another caveat is in how the Figures interact with other inlines in the Paragraph. They can only snap to one side or the other (according to the HorizontalAnchor property) instead of flowing inline.
For these reasons I think InlineUIContainer might be the best solution.

Related

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.

TextBlock with vertical scrollbar only

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

Why does data binding my chart series color fail?

I am drawing simple line charts using the WPF toolkit. My goal is to set the line color of my series via Data Binding. This succeeds only partially. The question is: why?
Setup
Namespaces:
xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit" x:Class="WpfApplication3.MainWindow"
xmlns:media="clr-namespace:System.Windows.Media;assembly=PresentationCore"
Chart:
<chartingToolkit:Chart x:Name="chart">
<chartingToolkit:LineSeries x:Name="seriesEntries" IndependentValueBinding="{Binding Key}" DependentValueBinding="{Binding Value}" DataPointStyle="{StaticResource CommonLineSeriesDataPoint}">
<chartingToolkit:LineSeries.Tag>
<media:Brush>Green</media:Brush>
</chartingToolkit:LineSeries.Tag>
</chartingToolkit:LineSeries>
</chartingToolkit:Chart>
Ignore the Tag for now, it will be relevant later.
Notice the chart has a custom data point style, CommonLineSeriesDataPoint:
<Style x:Key="CommonLineSeriesDataPoint" TargetType="chartingToolkit:LineDataPoint">
<Setter Property="Background">
<Setter.Value>
<media:Brush>Red</media:Brush>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="chartingToolkit:LineSeries">
<Setter Property="DataPointStyle" Value="{StaticResource CommonLineSeriesDataPoint}" />
</Style>
As expected, this colors my line series red:
Breaking Change
Now I want to data bind my data point background. I make only one change. Instead of specifying the background brush directly, I bind it to the Tag property of my LineSeries, which is also a brush (see previous LineSeries declaration, it's a green one).
<Style x:Key="CommonLineSeriesDataPoint" TargetType="chartingToolkit:LineDataPoint">
<Setter Property="Background">
<Setter.Value>
<Binding Path="Tag" RelativeSource="{RelativeSource AncestorType={x:Type chartingToolkit:LineSeries}}" />
</Setter.Value>
</Setter>
</Style>
<Style TargetType="chartingToolkit:LineSeries">
<Setter Property="DataPointStyle" Value="{StaticResource CommonLineSeriesDataPoint}" />
</Style>
The result is this:
So the dots are green. But the line is gone.
My expectation is to see a green line as well! Where is it?
I found a solution after digging in the WPF Toolkit Sources.
Turns out the Stroke property of the series' Polyline is bound to a Background property via TemplateBinding. I suspect this doesn't go well with my try binding the Background property itself.
This answer on SO suggests that TemplateBinding is evaluated at compile time. So let's get rid of the TemplateBinding and bind the Stroke property directly to the Tag of my LineSeries (remember: the Tag contains the green brush).
From the WPF Toolkit Source \Source\DataVisualization\Themes\generic.xaml I copied part of the style definition for the LineSeries and added it to my ResourceDictionary:
<Style x:Key="CommonLineSeries" TargetType="chartingToolkit:LineSeries" BasedOn="{StaticResource {x:Type chartingToolkit:LineSeries}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chartingToolkit:LineSeries">
<Canvas x:Name="PlotArea">
<Polyline Points="{TemplateBinding Points}" Stroke="{Binding Tag, RelativeSource={RelativeSource AncestorType={x:Type chartingToolkit:LineSeries}}}" Style="{TemplateBinding PolylineStyle}"/>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
(If interested you can search for <!-- charting:LineSeries --> in generic.xaml to find the source I copied from.)
The only thing I modified is the binding of Stroke. Here I use the same binding I used for my data points.
Last thing to do: tell the LineSeries to use this style:
<chartingToolkit:LineSeries x:Name="seriesEntries" IndependentValueBinding="{Binding Key}" DependentValueBinding="{Binding Value}" DataPointStyle="{StaticResource CommonLineSeriesDataPoint}" Style="{StaticResource CommonLineSeries}">
And lo and behold, it works. The line is back and it's green:
(If you look closely you see that the legend entry for the series still has the wrong color. But I assume the solution will be quite similar to the above.)

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.

In WPF/XAML how do I change the size of a paragraph of text using a scroll bar?

I'm new to WPF/XAML & I'm just doing a training exercise at the moment.
I've got a noddy application and I want to change the size of the text in a tag based on the position of a scroll bar.
The text is defined by this code:
<FlowDocumentScrollViewer Grid.Row="1">
<FlowDocument>
<Paragraph>
Text goes here
</Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
I'm trying to define a Setter and I've got as far as this:
<Style TargetType="{x:Type Paragraph}">
<Setter Property="FontSize" Value="???" />
</Style>
But I can't find out what needs to go in place "???". I've tried Googling for the answer to this, but I think I must be using the wrong search terms because I haven't found the answer yet.
I'm guessing that it's going to be really obvious, but I've got to admit I'm stumped.
You can just set the font size with a binding expression like this:
<Paragraph FontSize="{Binding ElementName=scroll1, Path=Value}" />
<ScrollBar x:Name="scroll1"></ScrollBar>
What you want to look into is the binding expression syntax, because currently intellisense isn't supported there.
The code that I implemented is this:
<Style TargetType="{x:Type Paragraph}">
<Setter Property="FontSize" Value="{Binding ElementName=FontSizeScroll, Path=Value}" />
</Style>
Which works a treat.
The value of FontSize is just a number that describes the size (in points I think):
<Style TargetType="{x:Type Paragraph}">
<Setter Property="FontSize" Value="12"/>
</Style>
I don't know if this is the answer you want cos it feels really obvious.

Resources