How to center TextBox element inside Grid - c#

I'm developing WP8 app for my own needs and want it to have small live tile with text.
Since small tile cannot display text, I'm generating appropriate image with needed text.
Here is the code:
WriteableBitmap bmpSmall = new WriteableBitmap(159, 159);
var grid = new Grid();
grid.Width = bmpSmall.PixelWidth;
grid.Height = bmpSmall.PixelHeight;
var background = new Canvas();
background.Width = bmpSmall.PixelWidth;
background.Height = bmpSmall.PixelHeight;
SolidColorBrush backColor = new SolidColorBrush((Color)Application.Current.Resources["PhoneAccentColor"]);
background.Background = backColor;
var textBlock = new TextBlock();
textBlock.Text = "qwerty";
textBlock.FontWeight = FontWeights.Bold;
textBlock.HorizontalAlignment = HorizontalAlignment.Center;
textBlock.VerticalAlignment = VerticalAlignment.Center;
textBlock.FontSize = 28;
textBlock.Foreground = new SolidColorBrush(Colors.White);
grid.Children.Add(textBlock);
bmpSmall.Render(background, null);
bmpSmall.Render(grid, null);
bmpSmall.Invalidate();
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream imageStream = new IsolatedStorageFileStream("/Shared/ShellContent/smallTile.jpg", System.IO.FileMode.Create, isf))
{
bmpSmall.SaveJpeg(imageStream, 159, 159, 0, 100);
}
}
ShellTile tile = ShellTile.ActiveTiles.First();
FlipTileData tileData = new FlipTileData();
tileData.SmallBackgroundImage = new Uri(#"isostore:/Shared/ShellContent/smallTile.jpg", UriKind.Absolute);
tile.Update(tileData);
And result looks like:
As you see, text is aligned to top left corner. The question is "Why"? Since I'd set textBlock.HorizontalAlignment and textBlock.VerticalAlignment - I expect it in the center of the image.
For example the following XAML looks like you can expect and like I need:
<Grid Width="159" Height="159">
<Grid.Background>
<SolidColorBrush Color="{StaticResource PhoneAccentColor}"/>
</Grid.Background>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontWeight="Bold" FontSize="28" Foreground="White">qwerty</TextBlock>
</Grid>
What did I miss? How can I center text?

Give the following code a try:
WriteableBitmap bmpSmall = new WriteableBitmap(159, 159);
var grid = new Grid();
grid.Width = bmpSmall.PixelWidth;
grid.Height = bmpSmall.PixelHeight;
var background = new Canvas();
background.Width = bmpSmall.PixelWidth;
background.Height = bmpSmall.PixelHeight;
SolidColorBrush backColor = new SolidColorBrush((Color)Application.Current.Resources["PhoneAccentColor"]);
background.Background = backColor;
var textBlock = new TextBlock();
textBlock.Width = bmpSmall.PixelWidth;
textBlock.Text = "qwerty";
textBlock.FontWeight = FontWeights.Bold;
textBlock.HorizontalAlignment = HorizontalAlignment.Stretch;
textBlock.VerticalAlignment = VerticalAlignment.Center;
textBlock.FontSize = 28;
textBlock.Foreground = new SolidColorBrush(Colors.White);
textBlock.TextAlignment = TextAlignment.Center;
grid.Children.Add(background);
grid.Children.Add(textBlock);
grid.Measure(new Size(bmpSmall.PixelWidth, bmpSmall.PixelHeight));
grid.Arrange(new Rect(0,0,bmpSmall.PixelWidth, bmpSmall.PixelHeight));
grid.UpdateLayout();
bmpSmall.Render(grid, null);
bmpSmall.Invalidate();
I set the TextBlock width to the same as the rest of the tile, and set HorizontalAlignment to stretch, so that control would take up the whole width of the tile. Then I set the TextAlignment property to TextAlignment.Center, in order to center the text. Hope that helps!
Edit: Apparently for writable bitmaps, you must do the measure/arrange/layout steps yourself in order to render controls as you would think they should be rendered. Give this updated code a try, it should work this time!

You will have to set the HorizontalAlignment="Stretch" VerticalAlignment="Stretch" for grid also. Currently grid is only of size of its content i.e textblock.

After copying your code into an empty WPF application, I'm afraid to tell you that your code works just fine:
All I did was to set the Grid.Background to red and the TextBlock.Background to black to clarify the situation:
<Grid Width="159" Height="159">
<Grid.Background>
<SolidColorBrush Color="Red"/>
</Grid.Background>
<TextBlock Background="Black" HorizontalAlignment="Center"
VerticalAlignment="Center" FontWeight="Bold" FontSize="28"
Foreground="White">qwerty</TextBlock>
Therefore, I can only assume that you have a problem elsewhere in your code.
UPDATE >>>
Sorry, you're right, I did misunderstand you. However, after testing your C# code, the story is just the same:
This might look like the same image, but it actually comes from your C# code... I made a couple of little changes, but nothing that affected the position of the TextBlock:
Grid grid = new Grid();
grid.Background = Brushes.Red;
grid.Width = grid.Height = 159.0;
TextBlock textBlock = new TextBlock();
textBlock.Text = "qwerty";
textBlock.Background = Brushes.Black;
textBlock.FontWeight = FontWeights.Bold;
textBlock.HorizontalAlignment = HorizontalAlignment.Center;
textBlock.VerticalAlignment = VerticalAlignment.Center;
textBlock.FontSize = 28;
textBlock.Foreground = Brushes.White;
grid.Children.Add(textBlock);
this.Content = grid;
If you put this code into a new WPF project, you'll see that it works just fine, so that only leaves your WriteableBitmap object as the culprit of this problem... what are you using that for? If you're just adding it to your UI, then you can simply add the controls to the Window directly.

Related

Set Background Color of Dynamically Generated WPF Canvas?

I have a dynamically created WPF Canvas element inside a ContentControl (the same thing happens in a UserControl.) Whenever I attempt to set the background, either upon creation or later on in the program, the background color won't paint (I am however able to set the background on a Label inside of the Canvas (also added dynamically.) The creation code I have looks like:
_rootGrid = new Grid();
_rootGrid.Name = "_rootGrid";
_rootGrid.Margin = new Thickness(0);
_rootGrid.HorizontalAlignment = HorizontalAlignment.Stretch;
_rootGrid.VerticalAlignment = VerticalAlignment.Stretch;
_rootGrid.RowDefinitions.Add(new RowDefinition());
_rootGrid.RowDefinitions.Add(new RowDefinition());
_rootGrid.ColumnDefinitions.Add(new ColumnDefinition());
_rootGrid.RowDefinitions[0].Height = new GridLength(24);
_rootGrid.RowDefinitions[1].Height = new GridLength(0, GridUnitType.Star);
_rootGrid.ColumnDefinitions[0].Width = new GridLength(0, GridUnitType.Star);
_headerBlock = new Canvas();
_headerBlock.Name = "_headerBlock";
_headerBlock.SetValue(Grid.RowProperty, 0);
_headerBlock.SetValue(Grid.ColumnProperty, 0);
_headerBlock.PreviewMouseLeftButtonUp += _headerBlock_PreviewMouseLeftButtonUp;
_headerBlock.Background = Brushes.Red;
_title = new Label();
_title.Name = "_title";
_title.Content = "Title";
_title.VerticalAlignment = VerticalAlignment.Center;
_title.HorizontalAlignment = HorizontalAlignment.Left;
_title.FontWeight = FontWeights.Bold;
_title.Background = Brushes.Blue;
_clientBlock = new ScrollViewer();
_clientBlock.Name = "_clientBlock";
_clientBlock.Margin = new Thickness(0);
_clientBlock.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
_clientBlock.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
_clientBlock.SetValue(Grid.RowProperty, 1);
_clientBlock.SetValue(Grid.ColumnProperty, 0);
_clientArea = new Grid();
_clientArea.Name = "_clientArea";
_clientArea.HorizontalAlignment = HorizontalAlignment.Stretch;
_clientArea.VerticalAlignment = VerticalAlignment.Stretch;
_headerBlock.Children.Add(_title);
_rootGrid.Children.Add(_headerBlock);
_clientBlock.Content = _clientArea;
_rootGrid.Children.Add(_clientBlock);
base.Content = _rootGrid;
And is called inside of the ContentControl constructor. From that I would expect the header to contain a full row of Red with a Blue rectangle around the text, but all I get is the Blue rectangle around text with most of the row left Transparent (noticeable due to the Green background of the root Grid.) Any help on this would be appreciated as it is enormously frustrating. I'm using version 6.2 of the .NET framework on Windows 7 if that plays into it (I have noticed some other odd behaviors, but am going for dynamic generation mostly because these ContentControls take lots of child elements and the VS 2017 XAML parser is too broken to allow them to be named - which makes them virtually useless.)
The solution is to use non-zero Width for ColumnDefinition:
_rootGrid.ColumnDefinitions[0].Width = new GridLength(1, GridUnitType.Star); // equal to Width="*" in xaml
When 0 is used, Canvas has 0 witdh. But is is possible to see blue Label because Canvas doesn't clip contents on its bounds.
If you try Grid (var _headerBlock = new Grid();) with zero width column, there won't be anything displayed at all.

align image to the right inside a button programmatically WPF

I'm trying to align a image inside a button to the right. however it does not seem to do what i want it to do. i have been trying for several hours now, so i think it is time to ask here. this is my code atm.
public class DirectoryButton : Button
{
public DirectoryButton(string name, string content)
{
Name = name.Replace(" ", "");
//Content = new BitmapImage(new Uri("../Resources/folder-icon.png", UriKind.Relative)) + content;
//Content = new BitmapImage(new Uri("../Resources/folder-icon.png", UriKind.Relative));
var img = new BitmapImage(new Uri("../Resources/folder-icon.png", UriKind.Relative));
var sp = new UniformGrid
{
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
Columns = 3,
Rows = 1
};
sp.Children.Add(new Label());
sp.Children.Add(new Label { Content = content, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center });
sp.Children.Add(new Image { Source = img, HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Center});
//sp.Orientation = Orientation.Horizontal;
Content = sp;
//Content = content;
Margin = new Thickness(0, 5, 0, 5);
Height = 65;
Width = 350;
FontWeight = FontWeights.Bold;
Foreground = new SolidColorBrush(Colors.Black);
Background = new SolidColorBrush(Colors.Gray);
HorizontalContentAlignment = HorizontalAlignment.Center;
VerticalAlignment = VerticalAlignment.Center;
Visibility = Visibility.Visible;
}
}
button Image
this makes the image in the button appear but it is not to the right. i cannot really do anything in the xaml since everything is created programmaticaly.
Regards,
Bjorn
Set the HorizontalContentAlignment to Stretch:
public class DirectoryButton : Button
{
public DirectoryButton(string name, string content)
{
Name = name.Replace(" ", "");
//Content = new BitmapImage(new Uri("../Resources/folder-icon.png", UriKind.Relative)) + content;
//Content = new BitmapImage(new Uri("../Resources/folder-icon.png", UriKind.Relative));
var img = new BitmapImage(new Uri("../Resources/folder-icon.png", UriKind.Relative));
var sp = new UniformGrid
{
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
Columns = 3,
Rows = 1
};
sp.Children.Add(new Label());
sp.Children.Add(new Label { Content = content, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center });
sp.Children.Add(new Image { Source = img, HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Center });
//sp.Orientation = Orientation.Horizontal;
Content = sp;
//Content = content;
Margin = new Thickness(0, 5, 0, 5);
Height = 65;
Width = 350;
FontWeight = FontWeights.Bold;
Foreground = new SolidColorBrush(Colors.Black);
Background = new SolidColorBrush(Colors.Gray);
HorizontalContentAlignment = HorizontalAlignment.Stretch; //<--
VerticalAlignment = VerticalAlignment.Center;
Visibility = Visibility.Visible;
}
This should make the UniformGrid stretch to fill the entire Button and the Image will then end up to the right.
As I tried to explain in the comments above.
The code you've provide us works like it should.
You create an UniformGrid with three cells and add three childs to the grid.
Each cell of the grid will have the same size.
Your empty label will got to the left one. The next label to the center.
You will add your image to the right cell and set HorizontalAlignment = HorizontalAlignment.Right. This will align your image within his cell at the right edge. And also to the right edge of your grid.
Now you add your UniformGrid as Content to your button.
With the following line of code
HorizontalContentAlignment = HorizontalAlignment.Center;
you will center your grid within the button.
That's why your image is not at the right edge of your button.
As already suggested in my comment you can set
HorizontalContentAlignment = HorizontalAlignment.Right;
and your Image and the whole grid will move to the right edge, but this means also that your centered Label will also move to the right.
If you want to let the second Label stay within the center of the Button take the Answer provided by mm8 and use
HorizontalContentAlignment = HorizontalAlignment.Stretch;

How to programmatically maintain aspect ratio of object in wpf

I programmatically add a Border with a certain width and height to a grid. However, I want to get either one of the following:
Make the border keep aspect ratio and fill make it as big as possible inside the grid
Make the border scale whenever the grid scales down or up (so not particularily the biggest possible, more like a percentage of the grid)
At the moment this is the situation when I resize my window:
Color borderColor = (Color)ColorConverter.ConvertFromString(BorderColor);
Color backgroundColor = (Color)ColorConverter.ConvertFromString(BackgroundColor);
Border border = new Border();
border.BorderThickness = new Thickness(BorderSize);
border.CornerRadius = new CornerRadius(TopLeftCornerRadius, TopRightCornerRadius, BottomRightCornerRadius, BottomLeftCornerRadius);
border.BorderBrush = (SolidColorBrush)(new BrushConverter().ConvertFrom(BorderColor));
border.Background = (SolidColorBrush)(new BrushConverter().ConvertFrom(BackgroundColor));
border.Width = Width;
border.Height = Height;
border.Margin = new Thickness(10);
previewgrid.Children.Add(border);
The normal situation:
The scaled situation:
So I would like it to resize properly and stay inside the white rectangle. By the way, the white grid has a margin as you can see ;-)
Thanks in advance!
As lerthe61 suggested, just use a Viewbox with its Stretch property set to Uniform:
Color borderColor = (Color)ColorConverter.ConvertFromString(BorderColor);
Color backgroundColor = (Color)ColorConverter.ConvertFromString(BackgroundColor);
Border border = new Border();
border.BorderThickness = new Thickness(BorderSize);
border.CornerRadius = new CornerRadius(TopLeftCornerRadius, TopRightCornerRadius, BottomRightCornerRadius, BottomLeftCornerRadius);
border.BorderBrush = (SolidColorBrush)(new BrushConverter().ConvertFrom(BorderColor));
border.Background = (SolidColorBrush)(new BrushConverter().ConvertFrom(BackgroundColor));
border.Width = Width;
border.Height = Height;
border.Margin = new Thickness(10);
Viewbox viewBox = new Viewbox();
viewBox.Stretch = Stretch.Uniform;
viewBox.Child = border;
previewgrid.Children.Add(viewBox);
Please, note that this solution does not work if previewgrid is a Canvas.
I hope it can help you.
The simplest way:
<Grid Margin="50">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Border CornerRadius="50,0,0,50"
Background="Green" />
<Border CornerRadius="0"
Grid.Column="1"
Background="Green" />
<Border CornerRadius="0,50,50,0"
Grid.Column="2"
Background="Green" />
</Grid>
By C#:
myGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(50) });
myGrid.ColumnDefinitions.Add(new ColumnDefinition());
myGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(50) });
Border b1 = new Border
{
Background = new SolidColorBrush(Colors.Blue),
CornerRadius = new CornerRadius(100, 0, 0, 100)
};
Grid.SetColumn(b1, 0);
Border b2 = new Border
{
Background = new SolidColorBrush(Colors.Blue),
};
Grid.SetColumn(b2, 1);
Border b3 = new Border
{
Background = new SolidColorBrush(Colors.Blue),
CornerRadius = new CornerRadius(0, 100, 100, 0),
};
Grid.SetColumn(b3, 2);
myGrid.Children.Add(b1);
myGrid.Children.Add(b2);
myGrid.Children.Add(b3);
Normal:
Resized:
Is it good enough for you?

Why ClipToBounds = false not work?

I do not want cut text of textblock. For this reason, I set viewBox.ClipToBounds to false, But it doesn't work.
Please tell me why ClipToBounds=false not work in this code:
private void Btn1_Click(object sender, RoutedEventArgs e)
{
Button button = new Button(); button.Background = Brushes.Red;
button.Width = 70; button.Height = 20;
Canvas.SetLeft(button, 100); Canvas.SetTop(button, 120);
button.Padding = new Thickness(1);
StackPanel stackPanel = new StackPanel();
Viewbox viewBox = new Viewbox();
viewBox.ClipToBounds = false;
Canvas canvas = new Canvas();
canvas.Width = button.Width; canvas.Height = button.Height;
TextBlock textBlock = new TextBlock();
textBlock.Text = "this is a test";
textBlock.FontSize = 15;
textBlock.FontFamily = new FontFamily("Arial");
textBlock.TextWrapping = TextWrapping.NoWrap;
textBlock.Foreground = Brushes.Green;
textBlock.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
textBlock.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
viewBox.Height = 20;
textBlock.IsHitTestVisible = false;
stackPanel.Children.Add(viewBox);
viewBox.Child = canvas;
canvas.Children.Add(textBlock);
button.Content = stackPanel;
Canvas MainCanvas = new Canvas();
MainCanvas.Children.Add(button);
this.Content = MainCanvas;
}
Screenhsot:
The screenshot below is what I want. :
ClipToBounds is false by default. However, clipping can still happen due to the way certain elements perform layout. Basically the way things work in WPF is that setting ClipToBounds = true will force things to clip. Leaving it set to false means that WPF determines how things should clip based on measure constraints and arrange rects.
If you look at the ArrangeCore and MeasureCore methods in FrameworkElement, you will see that there is quite a bit of logic determining whether something should clip. Of course, things that override FrameworkElement are free to render however they want, but generally they will obey the clipping rules established by the base class.
In the case of a TextBlock, it will definitely clip text that goes outside of its bounds if its size is constrained. You can see this by simply setting a Width on it, or placing it is a parent that has a Width set on it.
If you really need the text to render outside of the bounds of the control, you may have to consider something like writing a custom text rendering element.
Even then, it is still going to be clipped by its parent as soon as you place it in something else that clips. So, you could still end up stuck.
You could try placing the TextBlock on top of the button instead of inside of it, and setting its position to get it in the right place (maybe by binding it to something). This would work, but might get hard to manage if you need to do it too much.
Basically, you are trying to go against one of the hard-coded rules of WPF, so you are likely not going to find an easy way to do it. Perhaps you might want to reevaluate your design and determine if this behavior is really necessary for what you want to do, or if you can go about it in a different way.
Thanks to elgonzo and Xavier.
I realized that I should not put the canvas in the viewbox.
By 2 change my problem solved.
1 - Swap viewbox with canvas.
2 - Remove canvas.with = ...
This is correct code :
private void Btn1_Click(object sender, RoutedEventArgs e)
{
Button button = new Button(); button.Background = Brushes.Red;
button.Width = 70; button.Height = 20;
Canvas.SetLeft(button, 100); Canvas.SetTop(button, 120);
button.Padding = new Thickness(1);
StackPanel stackPanel = new StackPanel();
Viewbox viewBox = new Viewbox();
viewBox.ClipToBounds = false;
Canvas canvas = new Canvas();
// canvas.Width = button.Width; canvas.Height = button.Height;
TextBlock textBlock = new TextBlock();
textBlock.Text = "this is a test";
textBlock.FontSize = 15;
textBlock.FontFamily = new FontFamily("Arial");
textBlock.TextWrapping = TextWrapping.NoWrap;
textBlock.Foreground = Brushes.Green;
textBlock.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
textBlock.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
viewBox.Height = 20;
textBlock.IsHitTestVisible = false;
stackPanel.Children.Add(canvas);
viewBox.Child = textBlock;
canvas.Children.Add(viewBox);
button.Content = stackPanel;
Canvas MainCanvas = new Canvas();
MainCanvas.Children.Add(button);
this.Content = MainCanvas;
}

How to force text into a specific width?

I use a GridView to display my layout. To this GridView i manually add some RowDefinitions and in this RowDefinitions I add 1 Canvas containing 2 rectangles:
foreach (Method m in sourceFile.getMethods())
{
if (!m.getName().StartsWith("<") && !m.getName().EndsWith(">"))
{
RowDefinition row = new RowDefinition();
row.Height = GridLength.Auto;
MethodsContainer.RowDefinitions.Add(row);
Canvas c = new Canvas();
c.Width = width;
c.Height = height;
c.Tag = m;
Contacts.AddPreviewContactDownHandler(c, new ContactEventHandler(onContactDown));
Rectangle r1 = new Rectangle();
r1.Height = height;
r1.Width = m.getLoc() * (width / 1000);
Canvas.SetLeft(r1, 0);
Canvas.SetLeft(r1, 0);
r1.Fill = Brushes.Red;
Rectangle r2 = new Rectangle();
r2.Height = height;
r2.Width = width - r1.Width;
Canvas.SetTop(r2, 0);
Canvas.SetLeft(r2, r1.Width);
r2.Fill = Brushes.Blue;
c.Children.Add(r1);
c.Children.Add(r2);
Grid.SetRow(c, rowCounter);
MethodsContainer.Children.Add(c);
rowCounter++;
}
}
The canvas is 200px width and 30px height. Both rectangles fill the Canvas exactly. Now i want to add some text over this both Rectangles. But I don't know how long the text is. However I want to force that the text is always printed into this 200px. How can I achieve that?
Sounds like you could make use of a ViewBox. This will make your text stretch both horizontally and vertically. I assume that's what you want if I understood the question correctly. Example in xaml
<Canvas Width="200"
Height="30">
<Viewbox Width="200"
Height="30">
<TextBlock Text="Text that will fit in 200 Width"/>
</Viewbox>
</Canvas>
And in code behind it will be like
TextBlock textBlock = new TextBlock();
textBlock.Text = "Text that will fit in 200 Width";
Viewbox viewBox = new Viewbox();
viewBox.Width = width;
viewBox.Height = height;
viewBox.Child = textBlock;
c.Children.Add(viewBox);
var txt = new TextBlock();
txt.MaxWidth = 200;
// optionally you might want the text to wrap if too long
txt.TextWrapping = TextWrapping.Wrap;
By the way, it's not pixels in WPF, it's a device-independent unit (1/96th inch) measurement.

Categories

Resources