Why is my text cropped? - wpf

When i try to add a text block to a border element, i only see part of the text. I rotate the text after adding it to the border and that is causing the problem. Increasing the width of the border fixes this issue. But, my border needs to be only 20 units wide.
alt text http://img257.imageshack.us/img257/1702/textcrop.jpg
What am i missing here?
<Border
Name="BranchBorder"
CornerRadius="0"
HorizontalAlignment="Left"
Width="20">
<TextBlock
Name="Branch"
FontSize="14"
FontWeight="Bold"
VerticalAlignment="Center">
<TextBlock.RenderTransform>
<RotateTransform
Angle="-90"/>
</TextBlock.RenderTransform>
Branch
</TextBlock>
</Border>

Try using LayoutTransform
<Border
Name="BranchBorder"
CornerRadius="0"
HorizontalAlignment="Left"
Width="20">
<TextBlock
Name="Branch"
FontSize="14"
FontWeight="Bold"
VerticalAlignment="Center">
<TextBlock.LayoutTransform>
<RotateTransform
Angle="-90"/>
</TextBlock.LayoutTransform>
Branch
</TextBlock>
</Border>
There are bunch of blog entries describing the difference between RenderTransform and LayoutTransform and here is a cool visual demo from Charles Petzold RenderTransformVersusLayoutTransform.xaml

It appears as though the text is inheriting the <border> transformations before the rotation transform has a chance to rotate it. This means that the text first gets cropped to 20 units wide, and then gets rotated -90 degrees.
While I don't have an actual solution, I can confirm that its order of transformations that's causing the issue.

Related

WPF XAML Rotate 90° in UWP margin issue

Hi I'm struggling with rotation in XAML. I found a solution that LayoutTransform should solve this issue, but on Windows Universal Plattform this option isn't available!
This is my Code:
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"
HorizontalAlignment="Center">
<Rectangle Fill="Red" Height="100" Width="100" />
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center"
Text="long long long long long long long long text"
RenderTransformOrigin="0.5,0.5" >
<TextBlock.RenderTransform>
<CompositeTransform Rotation="90"/>
</TextBlock.RenderTransform>
</TextBlock>
<Rectangle Fill="Green" Height="100" Width="100" />
</StackPanel>
But as you can see there are an huge margin between the colored boxes and the textblock as you can see here:
So my first guess was to add an Width="30" property to the textblock, but this isn't working as you can see here (not only the offset is the problem, but the cropping is the problem):
Any suggestions?
Try the LayoutTransform control of the WinRTXamlToolkit. Following links should help.
Github Source link and Demo link

Validation.ErrorTemplate size

I've got the following control template which I use as a Validation.ErrorTemplate for TextBoxes:-
<ControlTemplate x:Key="ControlValidationErrorTemplate">
<DockPanel LastChildFill="True">
<Border Background="Red"
DockPanel.Dock="right"
Padding="2,0,2,0"
ToolTip="{Binding ElementName=valAdorner, Path=AdornedElement.(Validation.Errors), Converter={x:Static val:ValidationErrorsConverter.Instance}}">
<TextBlock Text="!"
VerticalAlignment="center"
HorizontalAlignment="center"
FontWeight="Bold"
Foreground="white" />
</Border>
<AdornedElementPlaceholder x:Name="valAdorner"
VerticalAlignment="Center">
<Border BorderBrush="red"
BorderThickness="1" />
</AdornedElementPlaceholder>
</DockPanel>
</ControlTemplate>
When a TextBox contains invalid content, the above template applies a red border and adds a red box containing an exclamation mark immediately to the right of the TB.
The problem is, the exclamation mark overlaps anything immediately to the right of the TB, rather than the layout changing to accomomodate the exclamation mark. I have a similar problem in DataGrids - the exclamation mark overlaps the right-hand edge of the containing cell, rather than the column width increasing to accommodate it.
Using Snoop, it appears that the template is being displayed in an "adorner layer" which I assume is a separate visual tree? This would explain why the window's layout isn't recalculated to take into account the exclamation mark. Can anyone suggest a way to achieve what I want?
As I suspected, it's due to the error template being rendered on the adorner layer, so it doesn't affect the layout of the window. See: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/9de3c9e5-5759-4f88-9184-175d3eaabdad/
I'm now using this control template instead:-
<ControlTemplate x:Key="ControlValidationErrorTemplate">
<Grid>
<Polygon Points="9,9 9,0 0,0"
Stroke="Red"
StrokeThickness="1"
Fill="Red"
HorizontalAlignment="Right"
VerticalAlignment="Top"
ToolTip="{Binding ElementName=valAdorner, Path=AdornedElement.(Validation.Errors), Converter={x:Static val:ValidationErrorsConverter.Instance}}" />
<AdornedElementPlaceholder x:Name="valAdorner"
VerticalAlignment="Center">
<Border BorderBrush="red"
BorderThickness="1" />
</AdornedElementPlaceholder>
</Grid>
</ControlTemplate>
This draws a red border around the control, with a small red triangle overlapping the top-right corner of the control - hovering over this displays a tooltip containing the error message.

WPF: Resizing a circle, keeping the center point instead of TopLeft?

I'd like to resize a circle on my canvas with the help of a slider. This circle can be moved around on the canvas by some drag&drop stuff I did in code behind, so its position is not fixed.
I have bound the slider's value to an ellipse's height and width. Unfortunately, when I use the slider, the circle gets resized with its top left point (actually the top left point of the rectangle it's sitting in) staying the same during the operation.
I would like to resize it with its center point being constant during the operation. Is there an easy way to do this in XAML? BTW, I already tried ScaleTransform, but it didn't quite do what I wanted.
Thanks a bunch! :-)
Jan
<Canvas x:Name="MyCanvas">
<!-- this is needed for some adorner stuff I do in code behind -->
<AdornerDecorator Canvas.Left="10"
Canvas.Top="10">
<Ellipse x:Name="myEllipse"
Height="{Binding Path=Value, ElementName=mySlider}"
Width="{Binding Path=Value, ElementName=mySlider}"
Stroke="Aquamarine"
Fill="AliceBlue"
RenderTransformOrigin="0.5 0.5">
<Ellipse.RenderTransform>
<RotateTransform Angle="{Binding Path=Value, ElementName=myRotationSlider}" />
</Ellipse.RenderTransform>
</Ellipse>
</AdornerDecorator>
<Slider x:Name="mySlider"
Maximum="100"
Minimum="0"
Width="100"
Value="10"
Canvas.Left="150"
Canvas.Top="10" />
<Slider x:Name="myRotationSlider"
Maximum="360"
Minimum="0"
Width="100"
Value="0"
Canvas.Left="150"
Canvas.Top="50" />
</Canvas>
You can bind your Canvas.Left and Canvas.Top to your Height and Width via a ValueConverter.
Specifically (edit):
Create a property each for the Canvas.Left and Canvas.Top and bind to these.
Store the old values for Width and Heigth or the old slider value.
Whenever the slider is changed, get the incremental change "dx" by subtracting the stored value.
(Don't forget to update the stored value...)
Add dx to Width and Height property.
And, as Will said, add dx/2*-1 to Canvas.Left and Canvas.Top properties.
Does that make sense?
The problem is that you are using the SLIDER to adjust the width and height. Width and height are not calculated around RenderTransformOrigin; only RenderTransforms use that value.
Here's a corrected version (brb, kaxaml):
<Canvas x:Name="MyCanvas">
<!-- this is needed for some adorner stuff I do in code behind -->
<AdornerDecorator Canvas.Left="50" Canvas.Top="50">
<Ellipse
x:Name="myEllipse"
Width="10"
Height="10"
Fill="AliceBlue"
RenderTransformOrigin="0.5 0.5"
Stroke="Aquamarine">
<Ellipse.RenderTransform>
<TransformGroup>
<RotateTransform Angle="{Binding Path=Value, ElementName=myRotationSlider}"/>
<ScaleTransform
CenterX=".5"
CenterY=".5"
ScaleX="{Binding Path=Value, ElementName=mySlider}"
ScaleY="{Binding Path=Value, ElementName=mySlider}"/>
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
</AdornerDecorator>
<Slider
x:Name="mySlider"
Width="100"
Canvas.Left="150"
Canvas.Top="10"
Maximum="10"
Minimum="0"
SmallChange=".01"
Value="1"/>
<Slider
x:Name="myRotationSlider"
Width="100"
Canvas.Left="150"
Canvas.Top="50"
Maximum="360"
Minimum="0"
Value="0"/>
</Canvas>
Of course, this will probably not work for you. Why? Well, the ScaleTransform I used zooms not only the circle but also the border; as the circle gets bigger the border does as well. Hopefully you won't care about this.
Also, realize when combining transforms (scale then rotate in this case) that they are applied in order, and one may affect how another is done. In your case, you would not notice this. But if, say, you were doing a rotate and translate, the order would be relevant.
Ah, what was I thinking? Just stick the ellipse in a Grid (simplest solution but other containers would work). The grid automatically takes care of centering the ellipse as it is resized. No need for any value converters! Here's the code:
<Canvas x:Name="MyCanvas">
<!-- this is needed for some adorner stuff I do in code behind -->
<Grid Width="100" Height="100">
<AdornerDecorator>
<Ellipse
x:Name="myEllipse"
Width="{Binding Path=Value, ElementName=mySlider}"
Height="{Binding Path=Value, ElementName=mySlider}"
Fill="AliceBlue"
RenderTransformOrigin="0.5 0.5"
Stroke="Aquamarine">
<Ellipse.RenderTransform>
<RotateTransform Angle="{Binding Path=Value, ElementName=myRotationSlider}"/>
</Ellipse.RenderTransform>
</Ellipse>
</AdornerDecorator>
</Grid>
<Slider
x:Name="mySlider"
Width="100"
Canvas.Left="150"
Canvas.Top="10"
Maximum="100"
Minimum="0"
Value="10"/>
<Slider
x:Name="myRotationSlider"
Width="100"
Canvas.Left="150"
Canvas.Top="50"
Maximum="360"
Minimum="0"
Value="0"/>
</Canvas>
Since you're using a Canvas, the location an element has is the location. If you want the Top,Left position to change you need to do it yourself. If you were using another Panel type, like a Grid, you could change the alignment of your Ellipse to place it in the same relative location no matter what the size. You could get that effect by adding a Grid inside your AdornerDecorator and centering the Ellipse but you'd also need to set the AdornerDecorator or Grid to a fixed size because they won't stretch in a Canvas.
The best solution you could use would be a ScaleTransform applied to the RenderTransform property with a RenderTransformOrigin of 0.5,0.5. You said you had problems with ScaleTransform but not what the problem was.
Wrap your Ellipse in a Grid of the maximum size. As long as it is smaller, the Ellipse will be centered in the Grid:
<Grid
Canvas.Left="10"
Canvas.Top="10"
Width="100"
Height="100">
<AdornerDecorator>
<Ellipse x:Name="myEllipse"
Height="{Binding Path=Value, ElementName=mySlider}"
Width="{Binding Path=Value, ElementName=mySlider}"
Stroke="Aquamarine"
Fill="AliceBlue"
RenderTransformOrigin="0.5 0.5">
<Ellipse.RenderTransform>
<RotateTransform Angle="{Binding Path=Value, ElementName=myRotationSlider}" />
</Ellipse.RenderTransform>
</Ellipse>
</AdornerDecorator>
</Grid>
You may need to adjust your dragging logic to handle dragging the Grid instead of the Ellipse itself.
I've found a very easy way to do this in plain XAML: set Margin="-1000000". Read more here: Positioning an element inside the Canvas by its center (instead of the top left corner) using only XAML in WPF

How do I put a border around an image in WPF?

I have a StackPanel containing five images and I want to put a black border around each image.
The XAML I have at the moment is:
<Image Name="imgPic1"
Width="100"
Height="75"
Stretch="Fill"
VerticalAlignment="Top" />
I thought I would be just able to put a one-unit margin or padding on the image and set a background color to 000000 but Padding and Background are both invalid for images.
What is an easy way to do this in XAML? Do I really have to put each image inside another control to get a border around it or is there some other trickery I can use?
Simply wrap the Image in a Border control
<Border BorderThickness="1">
<Image Name="imgPic1"
Width="100"
Height="75"
Stretch="Fill"
VerticalAlignment="Top" />
</Border>
You could also provide a style you apply to images that does this if you don't want to do it around every image
Final solution from answer and comments added by Pax:
<Border BorderThickness="1"
BorderBrush="#FF000000"
VerticalAlignment="Top">
<Image Name="imgPic1"
Width="100"
Height="75"
Stretch="Fill"
VerticalAlignment="Top"/>
</Border>
The accepted answer will not work because of the problem described here
https://wpf.2000things.com/2011/04/17/279-adding-a-border-around-an-image-control/
I solved it this way.
<Viewbox>
<Border BorderThickness="3" BorderBrush="Red">
<Image Stretch="None" ></Image>
</Border>
</Viewbox>
I just stumbled upon this post and the other answer did not work right. Maybe because I now use framework 4 and this post is old?
In any case - if someone will see this by chance in the future - here is my answer:
<Border Name="brdSiteLogo"
BorderThickness="2"
BorderBrush="#FF000000"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="12,112,0,0"
Height="128"
Width="128">
<Image Name="imgSiteLogo"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Stretch="Fill"/>
</Border>
The border thickness and brush are important (if you wont choose a color - you will not see the border!!!)
Also, the border should be aligned on your window. The image is "inside" the border, so you can use margins or just stretch it like I did.

TextBlock filling vertical space

I want to create a TextBlock (or some other element with text in it for display only) that is vertical (-90 transform angle), but I want that element to fill up the vertical space it is contained in, but have a defined horizontal amount (I'm using vertical and horizontal terms instead of height and width since it's swapped when I have the TextBlock go vertical), and have it aligned to the left side of the container.
I believe I understand how to make a TextBlock go vertical using RenderTransform or LayoutTransform. However, I cannot seem to get the 'docking' to work properly, whenever I change the vertical aspect of the container the TextBlock increases in horizontal aspect instead of vertical.
Here is what I have:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="AttendanceTracker.StudentView"
x:Name="UserControl" Height="172.666" Width="417.333">
<StackPanel x:Name="LayoutRoot" Orientation="Horizontal">
<Border BorderBrush="Black" BorderThickness="1" RenderTransformOrigin="0.5,0.5" Background="#52FFFFFF" Width="139.667">
<TextBlock Text="My Title" TextWrapping="Wrap" FontSize="18.667" TextAlignment="Center" Foreground="White" Margin="-58.509,68.068,49.158,70.734" Background="Black" RenderTransformOrigin="0.5,0.5" Width="147.017" d:LayoutOverrides="Height">
<TextBlock.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="-90"/>
<TranslateTransform/>
</TransformGroup>
</TextBlock.RenderTransform>
</TextBlock>
</Border>
</StackPanel>
Change the height of the UserControl and you will notice that the TextBlock increases in horizontal aspect instead of the desired vertical aspect.
If I understand you correctly, then this should point you in the right direction:
<StackPanel Orientation="Horizontal">
<TextBlock Background="Red" Text="My Title">
<TextBlock.LayoutTransform>
<TransformGroup>
<RotateTransform Angle="90"/>
</TransformGroup>
</TextBlock.LayoutTransform>
</TextBlock>
</StackPanel>
The key is to use LayoutTransform, not RenderTransform. This will ensure that another layout pass occurs after the transform occurs. Otherwise, the layout system is using the original bounding rectangle to layout the TextBlock.
Beyond that, I just got rid of all the Blend-generated cruft to see what was going on. Here's the result:
alt text http://img187.imageshack.us/img187/1189/screenshottbv.png

Resources