Canvas overrides the TextAlignment value of TextBlock inside it - c#

I am trying to configure the TextAlignment of a TextBlock that is a child of a Canvas. I realized that in a grid the TextAlignment works, but when in a Canvas you can't configure it as Center.
I really hope someone could help and shed some light on this topic.

A relatively easy solution is to wrap your TextBlock in a StackPanel with a width, and set the TextBlock's HorizontalAlignment to Center. This will center the text in the StackPanel.
The Width is the important part. Without the Width, the TextBlock wouldn't know how much room it has, and therefore how to center itself.

The Canvas will always give it's child elements their "desired size". So if your TextBlock needs to be 100 pixels wide to display it's text, then the Canvas will give it exactly 100 pixels. A Grid on the other hand, can give the TextBlock more or less space depending on what column/row the TextBlock is in.
For single lines of text (i.e. no new lines), the only time TextAlignment comes into play is when the TextBlock is given more (or less if TextWrapping is enabled) space than it needs.
For multiple lines of text (or in the case of text wrapping a single line), then the TextAlignment will align it's text withing the bounds of the TextBlock.
Since a Canvas will never force a TextBlock to wrap, the only time TextAlignment comes into play there is with multiple lines of text.
<Canvas Width="300" Height="50">
<TextBlock Canvas.Top="0" TextAlignment="Center">
This will not center
</TextBlock>
<TextBlock Canvas.Top="25" TextAlignment="Center">
This will center the shorter<LineBreak />lines of text
</TextBlock>
</Canvas>

Related

WPF Calculated FontSize Inside ViewBox

I have faced a problem and tried the internet and threads here to find a solution but haven't found yet.
I am a beginner in programming any help will be appreciated.
I have a window which will be displayed on the secondary screen and a TextBlock wrapped in a viewbox and the text is binded and will be populated dymnamically on runtime on the second screen. The fontsize is also Binded and can be changed by the user.
This is working perfectly well.
The Problem Is That when the user increases fontsize to a higher value the text looks good if the text is short but if the text is very very large the text looks ugly sometimes becomes unreadable.
I have tried the ViewBox's Sretch direction and stretch but haven't found a better way to solve my problem but manually if there is very very long text and the text is unreadable reducing the fontsize makes text readable.
How may i be helped. One solution I have in mind is to calculate the fontsize maybe on height and width of parent grid such that when the text is very long the calculation reduces the fontsize to where it becomes readable.
here is my sample Xaml
<Grid>
<Viewbox Stretch="Fill"
StretchDirection="DownOnly">
<TextBlock Text="{Binding}"
Width="{Binding}"
FontSize="{Binding}"
TextWrapping="Wrap">
</TextBlock>
</Viewbox>
</Grid>
Here is the Ugly Behavior when text is very long and fontsize is set highly
Expected behavior. Here the fontsize is manually set lower
Okay I found a work around the problem. If anyone has another solution it will be welcomed.
I wrapped the textblock inside a border and removed binding of the width of the textblock and I will be increasing and decreasing the width. If font size increases I will decrease the width by a ratio in the ViewModel and vice versa.
<Grid>
<Viewbox >
<Border>
<TextBlock Text="{Binding}"
Width="{Binding}"
FontSize="{Binding}"
TextWrapping="Wrap">
</TextBlock>
</Border>
</Viewbox>

How to clip textblock inside proportional grid?

In a UserControl that's inside a ListBox, I've got a TextBlock with proportional width (Width="*") inside a Grid that I want to take the remaining width of the grid, but whenever I resize the ListBox to a size that would clip that TextBlock's content I get a scroll bar. How can I clip the TextBlock width so that I don't get an horizontal scroll bar? Ideally clipping it with ellipsis.
EDIT: Forgot to mention the ListBox.
The listbox is scrolling because it's default behavior is to have HorizontalScrollBarVisibility to Auto.
Try setting this property to disabled:
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">
As for clipping to an ellipse, this is more complex. You can clip to an ellipsis quite easily using the Clip property:
<TextBlock Text="Some very long thing that I'm putting in here to clip" Background="Blue">
<TextBlock.Clip>
<EllipseGeometry Center="100,8" RadiusX="100" RadiusY="8" />
</TextBlock.Clip>
</TextBlock>
However, to keep the Center, RadiusX and RadiusY in order you'll have to bind it to the height and width of the text box (perhaps using a converter to half these values).

Why StackPanel does not stretch its children vertically?

(new to WPF) I am looking over the WPF example:
<Window x:Class="Attempt_XAML.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Label HorizontalAlignment="Center">A Button Stack</Label>
<Button HorizontalAlignment="Left">Button 1</Button>
<Button HorizontalAlignment="Right">Button 2</Button>
<Button Background="#FFA29494">Button 3</Button>
<Button>Button 4</Button>
</StackPanel>
</Window>
Remark in MS notes that:
The default value is stretch for both HorizontalAlignment and
VerticalAlignment of content that is contained in a StackPanel.
However, result looks different from what I am expecting. Button and Label are not stretched out vertically, but only horizontally(i.e they don't fill the entire space of Window in both directions) Why ?
Button and Label are not stretched out vertically, but only horizontally(i.e they don't fill the entire space of Window in both directions) Why ?
To understand the reason, you really need at least a basic understanding of Panels. Any Panel such as a StackPanel uses a measure-arrange cycle to decide on a layout for its child elements:
in the "measure" cycle, it assigns a size to each child
in the "arrange" cycle, it positions each child in its view
The key feature of a StackPanel is that it has infinite space -- infinite horizontal space if its orientation is Horizontal, and infinite vertical space if Vertical. In other words, it does not actually pay attention to the size available to it (in the direction of its orientation), but claims an infinite space. So therefore, coming back to your example, even though the VerticalAlignment of the children may be Stretch, they cannot actually be stretched out to an infinite size.
If you need a panel that stretches out its children to fill the available space, then Grid is a good example (by default the Grid will assign an equal share of the total height to each child -- or you can use the star sizing to adjust the proportions). You could also consider creating your own custom Panel, if you need a specialized layout behavior.
Edit
To clarify "infinite space": what I mean is that the StackPanel tells its children that there is infinite space available. What do you do if you are a Button, Label, etc, and there is infinite space available? You probably just take up the minimum space you need, even if your VerticalAlignment is "Stretch", right? That's what happens. Contrast to a Grid, which tells the child controls that they have x (finite) amount of space -- in that case, a Button, Label, etc, will fill up all that space (if their VerticalAlignment is "Stretch").
To illustrate the above, take this custom control as an example:
public class TestControl : ContentControl
{
public string Description { get; set; }
protected override Size MeasureOverride(Size availableSize)
{
System.Diagnostics.Debug.WriteLine("Size available for '" + Description + "': " + availableSize.Height);
return base.MeasureOverride(availableSize);
}
}
This doesn't actually do anything, just reports how much space has been allocated to it. Now, place the test control in a Grid and a StackPanel, and compare:
<Grid Height="50">
<Grid.RowDefinition />
<Grid.RowDefinition />
<local:TestControl Description="in Grid" />
<StackPanel Grid.Row="1" Height="10">
<local:TestControl Description="in StackPanel" />
</StackPanel>
</Grid>
You'll see that the Grid assigns its CustomPanel (the first one above) a height of 25 (half its height). The StackPanel, though, assigns its CustomPanel a height of Infinity.
Although default value is stretch for both HorizontalAlignment and VerticalAlignment of Content that is contained in a StackPanel. But in which direction to stretch is controlled by Orientation Property.
If Orientation is set to Vertical then all the items of the stack with no defined width value are stretched only.

Canvas improperly overlaying a TextBlock component inside a DockPanel

In the image here, each block with a number in it represents a laser. These blocks are laid out on a canvas inside a DockPanel. Also inside the DockPanel docked to the top is the red TextBlock that you can see is hiding behind the laser map canvas. Why is this happening? The TextBlock is docked to the top of the DockPanel and canvas has no dock setting, therefore it should fill the rest of space. Also of note: I had to put the DockPanel inside a ViewBox in order for the whole center screen space to scale properly on window resizes. Then I had to put that ViewBox inside a ScrollViewer to allow scroll bars to appear when needed.
Here is the XAML Code for the center screen (Note: Child of the Window is a DockPanel. Menu is docked to the top, left-hand button panel is docked to the left, right-hand button panel is docked to the right, the status bar is docked to the bottom and everything you see in the center screen is defined by the following XAML code)
<ScrollViewer
Name="centerScreenScrollViewer"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="{Binding IsScrollbarsVisible, Converter={StaticResource BoolToScrollbarVisConverter}, FallbackValue=Hidden}">
<Viewbox>
<DockPanel
LastChildFill="True">
<TextBlock
DockPanel.Dock="Top"
Name="tbkFullVisual"
Style="{StaticResource tbkStyleBlue}"
Foreground="Red"
IsEnabled="{Binding FullVisual}"
HorizontalAlignment="Center"
VerticalAlignment="Top"
FontSize="24">
*** This Print Requires Full Visual Inspection! ***
</TextBlock>
<Canvas x:Name="mapCanvas">
<ContentPresenter Content="{Binding MapCanvas}"/>
</Canvas>
</DockPanel>
</Viewbox>
</ScrollViewer>
Any help in solving this issue will be greatly appreciated.
Regards,
Kyle
This has to do with the way that a ViewBox works, in particular with the Canvas element. The ViewBox is used to resize child elements, as I'm sure you're aware. There are 2 issues with the Canvas element:
The default Height and Width are 0, which means that the TextBlock will get all the space.
The Canvas element lets you draw outside of its own boundaries, so even if your canvas is tiny or not even visible, you would be allowed to render your grid of numbers.
The quickest solution is to set VerticalAlignment on the ViewBox:
<Viewbox VerticalAlignment="Top">
...
</Viewbox>
You could set a Height on the Canvas, but I think this is less ideal because you don't want to change this dynamically with window resize.

WPF - Drawing image to canvas and erasing and redrawing repeatedly

I currently have a canvas with an image background in a WPF application. Above this canvas, I have a slider control. I need to, as users slide the value of the slider back and forth, draw a red line straight down across the canvas. I need to do this every time the slider value is changed such that the red line is always aligned with the slider's thumb. The big problem I'm having here is trying to figure out how to efficiently draw the line, and then "erase" the previously drawn line and drawing a new line at the new thumb value when user's change the slider's value. If I simply redraw the canvas's background image, the application lags a lot and doesn't work well (plus, this just straight out doesn't completely solve the problem as you can still see the previously drawn lines anyway).
Any help would be absolutely appreciated, particularly with examples as I this is my first WPF application in C# and I'm stilling getting a feel for it's uniqueness (as opposed to Windows Forms). Thanks a lot!
Rather than trying to draw the line yourself, a more WPF way to approach it would be to use a Line object. See Shapes and Basic Drawing in WPF Overview. Since you want the line to be aligned with the Slider, you can use data binding to bind the X position of the line to the position of the Slider:
<DockPanel>
<Slider
Name="HorizontalSlider"
DockPanel.Dock="Top"
Maximum="{Binding ActualWidth, ElementName=Canvas}"/>
<Canvas Name="Canvas" Margin="5.5 0">
<Line
X1="{Binding Value, ElementName=HorizontalSlider}"
Y1="0"
X2="{Binding Value, ElementName=HorizontalSlider}"
Y2="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Canvas}}"
Stroke="Red"/>
</Canvas>
</DockPanel>
(Note that I cheated a little by setting the margin on the Canvas to 5.5. The thumb on the slider has some thickness and doesn't move the entire width of the slider control, so in order to get the line to line up with the center I tried to make the canvas have the same width as the track.)

Categories

Resources