Set font-height to half the textblock height - c#

I am trying to set the font-height to half the size of the TextBlock. The TextBlock is in a Grid ie. in a row of the Grid.
The grid row spans multiple rows.
I have tried.
Textblock t = new TextBlock();
t.LineHeight = t.ActualHeight/2;
But the ActualHeight is always 0.

ActualHeight is only calculated after the element is loaded. To get the size of the TextBlock before being loaded into the visual tree, you can call the Measure() method like this:
var t = new TextBlock();
var infiniteSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
t.Text = "Something";
t.FontSize = 12;
t.Measure(infiniteSize);
t.LineHeight = t.DesiredSize.Height / 2;
The infiniteSize variable tells the Measure() method to give you the optimal size of the element assuming you have infinite space to draw the element.
Alternate Solution
You can tap into TextBlock.SizeChanged event and update the line-height.
var t = new TextBlock();
t.SizeChanged += (sender, args) =>
{
t.LineHeight = t.ActualHeight / 2;
};

Related

C# WPF how to draw a circle which extends to fill its parent and remains a circle in code behind

I have to draw a circle in a grid. That grid has to adapt proportionally to height and width defined by the Column/Row definition of its parent grid.
Now if I put stretch it will fill all the space and become an ellipsis while I want it to be a circle.
Ok in short the parent grid adapts proportionally like that
then in a routine I add the following code:
public void RadialPercentage(Grid grd )
{
Ellipse elpExt = new Ellipse();
elpExt.Stroke = Brushes.Green;
elpExt.StrokeThickness = 4;
//elpExt.Margin = new Thickness(0);
//elpExt.HorizontalAlignment = HorizontalAlignment.Center;
elpExt.VerticalAlignment = VerticalAlignment.Stretch;
grd.Children.Add(elpExt);
Ellipse elpInt = new Ellipse();
elpInt.Stroke = Brushes.Blue;
elpInt.StrokeThickness = 4;
elpInt.Margin = new Thickness(20);
//elpInt.Width = elpInt.Height = dim-20;
//elpInt.HorizontalAlignment = HorizontalAlignment.Center;
elpInt.VerticalAlignment = VerticalAlignment.Stretch;
grd.Children.Add(elpInt);
return;
}
but the effect is the following:
so it stretches both vertically and horizontally even if I only put the vertical and not the horizontal constraint. If I set it to center the ellipse collapses.
To solve the problem even I am not sure that this is the right thing to do I tried to take a look of the weight/heigth of the parent grid but obviously both those values and the actual values are set to zero.
thanks for helping
Patrick
What about setting Width's binding to ActualHeight of the ellipse and set HorizontalAlignment to Center? Something like this:
var ellipse = new Ellipse();
var binding = new Binding(Ellipse.ActualHeightProperty.Name)
{
RelativeSource = new RelativeSource(RelativeSourceMode.Self),
Mode = BindingMode.OneWay
};
ellipse.VerticalAlignment = VerticalAlignment.Stretch;
ellipse.HorizontalAlignment = HorizontalAlignment.Center;
BindingOperations.SetBinding(ellipse, Ellipse.WidthProperty, binding);
You can update the size of your Ellipse each time the parent Grid is resized.
You should add to your Grid the SizeChanged Event. XAML example:
<Grid Name = "MyGrid"
SizeChanged = "MyGridSizeChanged">
<!-- rows and columns definitions -->
<Ellipse Name = "MyEllipse"
Grid.Row = "i"
Grid.Column = "j" />
</Grid>
Now, each time the Grid is resized the function MyGridSizeChanged will executed. You should add into it code, which set sizes of your Ellipse equal to smallest side of contained cell. C# example:
void MyGridSizeChanged(object sender, SizeChangedEventArgs e) {
if (sender is Grid myGrid) {
var cellHeight = myGrid.RowDefinitions[Grid.GetRow(MyEllipse)].ActualHeight;
var cellWidth = myGrid.ColumnDefinitions[Grid.GetColumn(MyEllipse)].ActualWidth;
var newSize = Math.Min(cellHeight, cellWidth);
MyEllipse.Height = newSize;
MyEllipse.Width = newSize;
}
}

Creating XAML grid rows programmatically

I'm trying to generate a grid of data programmatically.
private void WorkPackageSearchButton_Click(object sender, RoutedEventArgs e)
{
EWP[] workPackages = SqlDataLayer.getSearchedWorkPackages(WorkPackageSearchBox.Text);
//declare variables
RowDefinition RowDef;
StackPanel StackP;
TextBlock TextB;
TextBlock TextC;
for (int i = 0; i < workPackages.Length; i++)
{
//Define new Row to add
RowDef = new RowDefinition();
RowDef.Height = new GridLength(30);
//Add row definition to Grid
WorkPackageResults.RowDefinitions.Add(RowDef);
//Define the control that will be added to new row
TextB = new TextBlock();
TextB.Text = workPackages[i].EWPStatus;
//TextB.Width = 75;
TextC = new TextBlock();
TextC.Text = workPackages[i].EWPCode;
//TextC.Width = 175;
//create stackpanel and define which row to add the stackpanel to
StackP = new StackPanel();
StackP.SetValue(Grid.RowProperty, i);
//add your control to the stackpanel
StackP.Children.Add(TextB);
StackP.Children.Add(TextC);
//add the stackpanel to the grid
WorkPackageResults.Children.Add(StackP);
}
}
The result is the data being added on top of each other:
How do I get each of the textblocks per iteration of the loop to be next to each other in a single row?
You have to set the Orientation of the StackPanel you're using to Horizontal
StackP = new StackPanel();
StackP.Orientation = Orientation.Horizontal;
the default Orientation of a StackPanel is Vertical, so it displays the items rows based.
Msdn Link and Examples

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;
}

WPF grid cells in C# no text

I need to show some data in a structured way with colored letters and rows with background colors.
I made a grid in a WPF Window. It shows the textboxes and some of the labels, but none of the text. Also the column header, last column, gridseperators, grid bot and left edges are invisible.
My grid is called propertiesView.
Code for adding header elements (labels)
private void AddHeaderElement(string text, int row, int col)
{
Label headerElement = new Label();
headerElement.Height = cellHeight;
headerElement.Width = cellWidth;
headerElement.DataContext = text;
headerElement.Background = headerBackground;
headerElement.BorderBrush = new SolidColorBrush(Color.FromRgb(120, 120, 120));
headerElement.BorderThickness = new Thickness(3);
propertiesView.Children.Add(headerElement);
Grid.SetRow(headerElement, row);
Grid.SetColumn(headerElement, col);
}
Code for adding cells
RichTextBox cell = new RichTextBox();
cell.Height = cellHeight;
cell.Width = cellWidth;
cell.ToolTip = toolTip;
cell.DataContext = text;
cell.Background = rowDifferent;
propertiesView.Children.Add(cell);
//box.SetValue(Grid.RowProperty, rowCount);
//box.SetValue(Grid.ColumnProperty, columnCount);
Grid.SetRow(cell, rowCount);
Grid.SetColumn(cell, columnCount);
Code for adding grid seperators
GridSplitter colSeperator = new GridSplitter();
colSeperator.Margin = new Thickness(-2.5, 0, 0, 0);
colSeperator.Width = 5;
colSeperator.ResizeDirection = GridResizeDirection.Columns;
colSeperator.ResizeBehavior = GridResizeBehavior.CurrentAndNext;
colSeperator.VerticalAlignment = VerticalAlignment.Stretch;
colSeperator.HorizontalAlignment = HorizontalAlignment.Left;
propertiesView.Children.Add(colSeperator);
Grid.SetColumn(colSeperator, 0);
Grid.SetRowSpan(colSeperator, totalRows + 1);
The tooltips do show the right text.
I tried using TextBox instead of RichTextBox and setting all this stuff in the class constructor instead of a seperate method.
Well it seems like you just never set the Content dependency property on your labels, or the Document of your RichTextBoxes.
For your labels, as you set the text parameter as the DataContext, you can just add something like
headerElement.SetBinding(Label.ContentProperty, new Binding());
Turns out I needed labels with textblocks, spans and runs.
Style can be added on the span (through properties like Foreground, TextDecoration or FontWeight). Add the text to the span inside a Run, then add all spans to a textblock, which is shown through a label.
Span span = new Span();
span.Foreground = Brushes.Black;
span.Inlines.Add(new Run("Text"));
textBlock.Inlines.Add(span);
Label cell = new Label();
cell.MinHeight = cellHeight;
cell.MaxWidth = cellWidth * 3;
cell.MinWidth = cellWidth;
cell.ToolTip = "toolTip";
cell.BorderThickness = new Thickness(2);
TextBlock cellText = new TextBlock();
cellText.HorizontalAlignment = HorizontalAlignment.Stretch;
cellText.TextWrapping = TextWrapping.WrapWithOverflow;
cell.Content = cellText;
The text works now, I should be able to get the gridseperators working.

Adding controls to GroupBox during runtime

I'm trying to create a GroupBox, add a Grid (or StackPanel) to it then put some TextBlocks on it, all during runtime. This is what i've tried
GroupBox groupBox1 = new GroupBox();
Grid grid1 = new Grid();
groupBox1.Width = 85;
groupBox1.Height = 60;
grid1.Height = 85;
grid1.Width = 60;
groupBox1.Content = grid1.Children.Add(textBlock1);
groupBox1.Margin = new Thickness(50, 50, 0, 0);
mainWindow.canvas.Children.Add(groupBox1);
But all I get is a groupbox with a thick white border with nothing in it.
As far as I can see a Grid.Children.Add returns an int and that's not what you want to set the content of the groupBox1 to.
An untested idea from me as a non WPF expert is to set the grid as the Content of your groupbox.
grid1.Children.Add(textBlock1);
groupBox1.Content = grid1;
For simple checkboxes i used this code :
var container = new FlowLayoutPanel
{
FlowDirection = FlowDirection.TopDown,
Dock = DockStyle.Fill
};
myGroupBox.Controls.Add(container);
foreach (var myText in textList)
{
var checkBox = new CheckBox
{
Text = myText
};
container.Controls.Add(checkBox);
}
Of course the foreach statement is just for the example :)

Categories

Resources