In a UWP app, is it possible to use the size and font of some text to calculate how big a TextBox would need to be to hold that text without scrolling?
This could be useful in designing an overall layout -- depending on the size of the TextBox, one might switch to a different configuration.
I'm going to be doing all this in code, not in xaml, as my app is cross-platform, and its graphical layout brains are in the platform-independent part.
If knowing the size is not possible, even knowing either of its dimensions would be nice, if that is possible.
Try this:
public sealed partial class MainPage
{
private TextBox _textBox;
private TextBlock _textBlock;
public MainPage()
{
InitializeComponent();
Loaded += MainPage_Loaded;
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
_textBlock = new TextBlock
{
Margin = new Thickness(10000, 10000, 0, 0),
};
MainGrid.Children.Add(_textBlock);
_textBox = new TextBox
{
Width = _textBlock.ActualWidth + 64, //is for clear button space
Height = _textBlock.ActualHeight,
};
_textBox.TextChanged += _textBox_TextChanged;
MainGrid.Children.Add(_textBox);
}
private void _textBox_TextChanged(object sender, TextChangedEventArgs e)
{
_textBlock.Text = _textBox.Text;
_textBox.Width = _textBlock.ActualWidth + 64;
_textBox.Height = _textBlock.ActualHeight;
}
}
It is not the perfect solution but may fit.
You just create somewhere off the screen textBlock and follow its size.
XAML has only 1 Grid x:Name="MainGrid"
Related
I'm sure this is something very easy to figure out but I cannot do it. I have a winform with 3 Label inside a Panel. When the form loads, the first Label has a Paint event that draws a rectangle on it. I would like a backgroundWorker to go through each one, wait 5 seconds, restore the Label to normal (redrawing I'm guessing) and then draw a rectangle on the following Label.
public List<Label> GetLabelList()
{
return new List<List>()
{
label1,
label2,
label3,
label4
};
}
private void bgBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
var getList = GetLabelList();
for (int i = 0; i < getList.Count; i++)
{
if ((bgBackgroundWorker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
Thread.Sleep(5000);
getList [i].Paint += RemoveLabelHighlight;
getList [i].Invalidate();
if (i < 2)
{
getList [i + 1].Paint += AddLabelHighlight;
getList [i + 1].Invalidate();
}
bgBackgroundWorker.ReportProgress((i * 10));
}
}
}
private void AddLabelHighlight(object sender, PaintEventArgs e)
{
var label = sender as Label;
e.Graphics.DrawRectangle(new Pen(Color.DeepPink, 8), label.ClientRectangle);
}
private void RemoveLabelHighlight(object sender, PaintEventArgs e)
{
var label = sender as Label;
e.Graphics.DrawRectangle(new Pen(Color.Green, 8), label.ClientRectangle); // This should return the Label back to original state
}
This works but when the rectangle is drawn, the label is cut off all the way around. Any suggestions?
Also, I'm sure there is a much better and more efficient way to achieve this, maybe by an EventHandler or something. I'd like some suggestions, if possible.
This is actually being caused by your use of the pen width of 8 pixels, I believe. Try a different size and see if that changes the size of the rectangle not being drawn.
To fill the rectangle instead, use:
e.Graphics.FillRectangle(new SolidBrush(Color.DeepPink), e.ClipRectangle);
EDIT Since you're now completely responsible for drawing the control, the text can be redrawn with a DrawString call:
e.Graphics.DrawString(label.Text, label.Font, SystemBrushes.ControlText, new PointF(0,0));
EDIT Here's how to nest a panel and a label to achieve what you're looking for:
Add a new panel, set the padding to 8,8,8,8, and BackColor to whatever you like
Add a new label to this panel, set it's AutoSize property to false, Dock property to Fill, and TextAlign property to MiddleCenter
While I have always loved doing owner-drawn stuff, sometimes it's just easier to use what's there! For fun though, I would wrap this into a new Panel-derived control to make it easy to reuse.
I have the following code in C# (.NET Framework 3.5)
public partial class MainForm : Form
{
public MainForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
Label myControl = new Label();
myControl.Text = "TEXT";
myControl.FlatStyle = FlatStyle.System;
myControl.AutoSize = true;
myControl.BorderStyle = BorderStyle.FixedSingle;
myControl.Padding = new Padding(0);
myControl.Margin = new Padding(0);
this.Controls.Add(myControl);
InitializeComponent();
}
}
Which should display a label with the text enclose by a border, like this:
------
|TEXT|
------
Instead, I get this:
--------
|TEXT |
--------
And I don't know why... My objective is to be able to have multiple labels without space between them, like this:
-----------
|TEXT|TEXT|
-----------
Am I missing something? Thanks in advance!
For clarification, I need to have NO SPACE between the text and the border.
This is what solved it for me (using #LarsTech's solution):
I added
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
this.AutoSize = false;
}
protected override void OnFontChanged(EventArgs e) {
base.OnFontChanged(e);
this.Size = GetTextSize();
}
protected override void OnResize(EventArgs e) {
base.OnResize(e);
this.Size = GetTextSize();
}
protected override void OnTextChanged(EventArgs e) {
base.OnTextChanged(e);
this.Size = GetTextSize();
}
private Size GetTextSize() {
Size padSize = TextRenderer.MeasureText(".", this.Font);
Size textSize = TextRenderer.MeasureText(this.Text + ".", this.Font);
return new Size(textSize.Width - padSize.Width, textSize.Height);
}
to my label definition.
I also added
textLabel.FlatStyle = FlatStyle.System;
Thank you very much for the help!
I don't know what's going on with the FlatStyle property, except to say that FlatStyle.System has a similar effect on my system. The other FlatStyle values indicate clearly what the effect will be on the control, but FlatStyle.System is pretty nebulous.
The appearance of the control is determined by the user's operating system.
I'm not sure what in the OS plays a role in the layout of he control. LarsTech's comment about changing it to FlatStyle.Standard (or any other value for that matter) fixes the issue for me (and doesn't trim off any text, as your comment indicates is happening to you).
You can override the alignment behavior by explicitly setting it to the center:
myControl.TextAlign = ContentAlignment.MiddleCenter;
I'm not sure exactly what you're trying to achieve (since it seems you could just enter all of your text in a single Label, not multiple next to each other), but you may also want to remove the border style:
myControl.BorderStyle = BorderStyle.None;
And, similar to what Blablablaster said, consider using a FlowLayoutPanel and adding your Label controls to that. You can place the above code in a loop, adding each one to the panel, and it'll take care of laying them out next to each other for you.
for (var i = 0; i < 10; i++)
{
Label myControl = new Label();
myControl.Text = "TEXT";
...
...
flowLayoutPanel1.Controls.Add(myControl);
}
I have the following code
<Window x:Class="Netspot.DigitalSignage.Client.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" WindowStyle="SingleBorderWindow"
WindowStartupLocation="CenterScreen"
WindowState="Normal" Closing="Window_Closing">
Any attempt to get the height / width return NaN or 0.0
Can anyone tell me a way of getting it ?
These 2 methods don't work
//Method1
var h = ((System.Windows.Controls.Panel)Application.Current.MainWindow.Content).ActualHeight;
var w = ((System.Windows.Controls.Panel)Application.Current.MainWindow.Content).ActualWidth;
//Method2
double dWidth = -1;
double dHeight = -1;
FrameworkElement pnlClient = this.Content as FrameworkElement;
if (pnlClient != null)
{
dWidth = pnlClient.ActualWidth;
dHeight = pnlClient.ActualWidth;
}
The application will not be running full screen.
1.) Subscribe to the window re size event in the code behind:
this.SizeChanged += OnWindowSizeChanged;
2.) Use the SizeChangedEventArgs object 'e' to get the sizes you need:
protected void OnWindowSizeChanged(object sender, SizeChangedEventArgs e)
{
double newWindowHeight = e.NewSize.Height;
double newWindowWidth = e.NewSize.Width;
double prevWindowHeight = e.PreviousSize.Height;
double prevWindowWidth = e.PreviousSize.Width;
}
Keep in mind this is very general case, you MAY (you may not either) have to do some checking to make sure you have size values of 0.
I used this to resize a list box dynamically as the main window changes. Essentially all I wanted was this control's height to change the same amount the window's height changes so its parent 'panel' looks consistent throughout any window changes.
Here is the code for that, more specific example:
NOTE I have a private instance integer called 'resizeMode' that is set to 0 in the constructor the Window code behind.
Here is the OnWindowSizeChanged event handler:
protected void OnWindowSizeChanged (object sender, SizeChangedEventArgs e)
{
if (e.PreviousSize.Height != 0)
{
if (e.HeightChanged)
{
double heightChange = e.NewSize.Height - e.PreviousSize.Height;
if (lbxUninspectedPrints.Height + heightChange > 0)
{
lbxUninspectedPrints.Height = lbxUninspectedPrints.Height + heightChange;
}
}
}
prevHeight = e.PreviousSize.Height;
}
You can get the width and height that the window was meant to be in the constructor after InitializeComponent has been run, they won't return NaN then, the actual height and width will have to wait until the window has been displayed.
When WindowState == Normal You can do this one from Width / Height after IntializeComponent().
When WindowState == Maximized You could get the screen resolution for this one with
System.Windows.SystemParameters.PrimaryScreenHeight;
System.Windows.SystemParameters.PrimaryScreenWidth;
You have to try to get the ActualWidth/ActualHeight values once the window is Loaded in the UI. Doing it in Window_Loaded works well.
XAML
<Grid x:Name="Grid1">
<Image x:Name="Back_Image" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
CS
MainWindow() after InitializeComponent();
Back_Image.Width = Grid1.Width;
Back_Image.Height = Grid1.Height;
WPF does the creation of controls and windows in a deferred manner. So until the window is displayed for the first time, it might not have gone through layouting yet, thus no ActualWidth/ActualHeight. You can wait until the window is loaded and get the properties then, or yet better bind these properties to a target where you need them. You could also force the layouting via UpdateLayout().
Just want to add: try to minimize the amount of size dependant logic, it is almost always possible to avoid it. Unless you are writing a layout panel of course.
Notice that when you use controls with 100% of with, they have a NaN size till they are being represented
You can check the ActualHeigh or ActualWidth just when the Loaded event is fired, but never try to verify it before the windows controls are created. Forget the idea to control that in the constructor.
In my oppinion, the best place to control this kind of things is The SizeChanged event
Hi you can get the height of the current screen in WPF with
System.Windows.SystemParameters.PrimaryScreenHeight
I just add a variable name to my window and then get the width or height of the window i want from its width or height property respectifly.
XAML:
<Window x:Name="exampleName"
...
</Window>
C#:
exampleName.Height;
or:
exampleName.Width;
My solution in case of MVVM...
I have a ViewModelBase, which is the base class for all ViewModel of the app.
1.) I created in the ViewModelBase a virtual method, which could be overriten in the child ViewModels.
public virtual void OnWindowSizeChanged(object sender, SizeChangedEventArgs e) {}
2.) The ctr of the MainWindow : Window class
public MainWindow(object dataContext)
{
InitializeComponent();
if (dataContext is ViewModelBase)
{
this.SizeChanged += ((ViewModelBase)dataContext).OnWindowSizeChanged;
DataContext = dataContext;
}
}
3.) Eg. in the MainViewModel, which is extends the ViewModelBase
public override void OnWindowSizeChanged(object sender, SizeChangedEventArgs e)
{
Console.WriteLine(e.NewSize.Height);
}
A ToolStripComboBox is placed after a ToolStripButton and is folowed by another one, which is right-aligned. How do I best set up the ToolStripComboBox to always adjust its length to fill all the space available between the preceeding and the folowing ToolStripButtons?
In past I used to handle a parent resize event, calculate the new length to set based on neighboring elements coordinates and setting the new size. But now, as I am developing a new application, I wonder if there is no better way.
I use the following with great success:
private void toolStrip1_Layout(System.Object sender, System.Windows.Forms.LayoutEventArgs e)
{
int width = toolStrip1.DisplayRectangle.Width;
foreach (ToolStripItem tsi in toolStrip1.Items) {
if (!(tsi == toolStripComboBox1)) {
width -= tsi.Width;
width -= tsi.Margin.Horizontal;
}
}
toolStripComboBox1.Width = Math.Max(0, width - toolStripComboBox1.Margin.Horizontal);
}
The above code does not suffer from the disapearing control problem.
There's no automatic layout option for this. But you can easily do it by implementing the ToolStrip.Resize event. This worked well:
private void toolStrip1_Resize(object sender, EventArgs e) {
toolStripComboBox1.Width = toolStripComboBox2.Bounds.Left - toolStripButton1.Bounds.Right - 4;
}
protected override void OnLoad(EventArgs e) {
toolStrip1_Resize(this, e);
}
Be sure to set the TSCB's AutoResize property to False or it won't work.
ToolStrip ts = new ToolStrip();
ToolStripComboBox comboBox = new TooLStripComboBox();
comboBox.Dock = DockStyle.Fill;
ts.LayoutStyle = ToolStripLayoutStyle.Table;
((TableLayoutSettings)ts.LayoutSettings).ColumnCount = 1;
((TableLayoutSettings)ts.LayoutSettings).RowCount = 1;
((TableLayoutSettings)ts.LayoutSettings).SetColumnSpan(comboBox,1);
ts.Items.Add(comboBox);
Now the combobox will dock fill correctly. Set Column or Row span accordingly.
I need some code to convert standard C# TextBox to temperature TextBox which means adding "°C" to end of the text in the textbox with another color than the default color.
To get the degree symbol you can use character code 176 e.g.
Char degree = (Char)176
You can then append this to your textbox content or I would just add a label to the right of the textbox with the degree symbol if you want to control the forecolor easily.
TextBox is a plain text editor. To get different colours you would have to muck around with a rich text box. Why not put the "°C" in a label positioned to the right of the text box? That would also make your parsing and rendering code much easier.
You could probably create your own control which inherits from TextBox and then override Text property to automaticaly add °C though other color inside the same TextBox could be problem.
Why you want to have °C in TextBox ?
Can't it just be label right after TextBox ?
You can set static text and color to what you want.
The other solutions proposed here are probably sufficient for your application; however, if you had the need to implement this with re-usability in mind, here is a custom control solution which you may extend to better suit your application:
public class TemperatureTextBox : ContainerControl
{
private const int BORDER_SIZE = 1;
// Exposes text property of text box,
// expose other text box properties as needed:
public override string Text
{
get { return textBox.Text; }
set { textBox.Text = value; }
}
private TextBox textBox = new TextBox()
{
Text = string.Empty,
BorderStyle = BorderStyle.None,
Dock = DockStyle.Fill
};
private Label label = new Label()
{
Text = "°C",
TextAlign = ContentAlignment.MiddleCenter,
Size = new Size()
{
Width = 32
},
BackColor = SystemColors.Window,
Dock = DockStyle.Right
};
public TemperatureTextBox()
{
this.BackColor = SystemColors.Window;
this.Padding = new Padding(BORDER_SIZE);
this.Controls.Add(label);
this.Controls.Add(textBox);
this.PerformLayout();
}
// Constrain control size to textbox height plus top and bottom border:
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
this.Height = (textBox.Height + (BORDER_SIZE * 2));
}
// Render a border around the control:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(
SystemPens.ControlDarkDark,
new Rectangle()
{
Width = (this.Width - BORDER_SIZE),
Height = (this.Height - BORDER_SIZE)
});
}
}
Simply create a new class and drop this code in and rebuild you solution. It will create a new TemperatureTextBox control in the toolbox which can be dropped onto a new form and visually designed.
This example exposes the Text property of the underlying text box by overriding the custom control's text property. You may want to expose other properties, and events depending on what your application needs to accomplish.