How to select text at entering to cell - silverlight

I have a Grid, and when I'm walking by cells of it, I enter to cell, but whithout selecting text. It's annoying because, when I need to change a value, I need firstly use Backspace key.
What can I do to after entering to cell have selected content of that cell?

I would recommend the use of a behavior for this purpose. Below I have included a minimal implementation that provides the functionality I think you're looking for:
/// <summary>
/// <see cref="Behavior{T}"/> of <see cref="TextBox"/> for selecting all text when the text box is focused
/// </summary>
public class TextBoxSelectOnFocusBehavior : Behavior<TextBox>
{
private void AssociatedObject_GotFocus(object sender, RoutedEventArgs e)
{
TextBox textBox = (sender as TextBox);
if (textBox != null)
textBox.SelectAll();
}
/// <summary>
/// React to the behavior being attached to an object
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.GotFocus += AssociatedObject_GotFocus;
}
/// <summary>
/// React to the behavior being detached from an object
/// </summary>
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.GotFocus -= AssociatedObject_GotFocus;
}
}
Hope this helps.

Related

In Silverlight, how do I find the first field bound to a model that has an error so that I can give it focus?

Greetings
I have a Silverlight form bound to a model object which implements INotifyDataErrorInfo and does validation when you click the save button. If some of the properties on the model come back invalid, Silverlight will automatically highlight the bound input field.
Is there a way to set the focus to the first invalid field?
UPDATE:
Is there even a way to see if an input field is in that invalid display state? If I can detect that, I can loop through my fields and set the focus manually.
Thanks,
Matthew
You could use a ValidationSummary in your view to display all validation errors your model raised. When you click on an error in the ValidationSummary the control which caused the validation error will be focused.
An example of the ValidationSummary can be found on the samples of the Silverlight Toolkit.
Until now I didn´t use the ValidationSummary in any application, so i cannot provide you any informations about usage or "how to use", but maybe this will help you
I've implemented this behavior.
First you need to subscribe to your ViewModel ErrorsChanged and PropertyChanged methods. I am doing this in my constructor:
/// <summary>
/// Initializes new instance of the View class.
/// </summary>
public View(ViewModel viewModel)
{
if (viewModel == null)
throw new ArgumentNullException("viewModel");
// Initialize the control
InitializeComponent(); // exception
// Set view model to data context.
DataContext = viewModel;
viewModel.PropertyChanged += new PropertyChangedEventHandler(_ViewModelPropertyChanged);
viewModel.ErrorsChanged += new EventHandler<DataErrorsChangedEventArgs>(_ViewModelErrorsChanged);
}
Then write handlers for this events:
/// <summary>
/// If model errors has changed and model still have errors set flag to true,
/// if we dont have errors - set flag to false.
/// </summary>
/// <param name="sender">Ignored.</param>
/// <param name="e">Ignored.</param>
private void _ViewModelErrorsChanged(object sender, DataErrorsChangedEventArgs e)
{
if ((this.DataContext as INotifyDataErrorInfo).HasErrors)
_hasErrorsRecentlyChanged = true;
else
_hasErrorsRecentlyChanged = false;
}
/// <summary>
/// Iterate over view model visual childrens.
/// </summary>
/// <param name="sender">Ignored.</param>
/// <param name="e">Ignored.</param>
private void _ViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if ((this.DataContext as INotifyDataErrorInfo).HasErrors)
_LoopThroughControls(this);
}
And finally add method:
/// <summary>
/// If we have error and we haven't already set focus - set focus to first control with error.
/// </summary>
/// <remarks>Recursive.</remarks>
/// <param name="parent">Parent element.</param>
private void _LoopThroughControls(UIElement parent)
{
// Check that we have error and we haven't already set focus
if (!_hasErrorsRecentlyChanged)
return;
int count = VisualTreeHelper.GetChildrenCount(parent);
// VisualTreeHelper.GetChildrenCount for TabControl will always return 0, so we need to
// do this branch of code.
if (parent.GetType().Equals(typeof(TabControl)))
{
TabControl tabContainer = ((TabControl)parent);
foreach (TabItem tabItem in tabContainer.Items)
{
if (tabItem.Content == null)
continue;
_LoopThroughControls(tabItem.Content as UIElement);
}
}
// If element has childs.
if (count > 0)
{
for (int i = 0; i < count; i++)
{
UIElement child = (UIElement)VisualTreeHelper.GetChild(parent, i);
if (child is System.Windows.Controls.Control)
{
var control = (System.Windows.Controls.Control)child;
// If control have error - we found first control, set focus to it and
// set flag to false.
if ((bool)control.GetValue(Validation.HasErrorProperty))
{
_hasErrorsRecentlyChanged = false;
control.Focus();
return;
}
}
_LoopThroughControls(child);
}
}
}

WPF ListView columns dont update thair width in time [duplicate]

I have a ListView WPF control with a GridView. I'd like to resize the GridView columns when the content of the columns changes.
I have several distinct data sets but when I change from one to another, the size of each columns fits the previous data. I'd like to update dynamically. How can I do that?
Finally, some results on this one. I've found a way to do the same auto-sizing that is done initially and when the gripper on a column header is double clicked.
public void AutoSizeColumns()
{
GridView gv = listView1.View as GridView;
if (gv != null)
{
foreach (var c in gv.Columns)
{
// Code below was found in GridViewColumnHeader.OnGripperDoubleClicked() event handler (using Reflector)
// i.e. it is the same code that is executed when the gripper is double clicked
if (double.IsNaN(c.Width))
{
c.Width = c.ActualWidth;
}
c.Width = double.NaN;
}
}
}
Building on top of Oskars answer, here is a blend behavior to automatically size the columns when the content changes.
/// <summary>
/// A <see cref="Behavior{T}"/> implementation which will automatically resize the automatic columns of a <see cref="ListView">ListViews</see> <see cref="GridView"/> to the new content.
/// </summary>
public class GridViewColumnResizeBehaviour : Behavior<ListView>
{
/// <summary>
/// Listens for when the <see cref="ItemsControl.Items"/> collection changes.
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
var listView = AssociatedObject;
if (listView == null)
return;
AddHandler(listView.Items);
}
private void AddHandler(INotifyCollectionChanged sourceCollection)
{
Contract.Requires(sourceCollection != null);
sourceCollection.CollectionChanged += OnListViewItemsCollectionChanged;
}
private void RemoveHandler(INotifyCollectionChanged sourceCollection)
{
Contract.Requires(sourceCollection != null);
sourceCollection.CollectionChanged -= OnListViewItemsCollectionChanged;
}
private void OnListViewItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
var listView = AssociatedObject;
if (listView == null)
return;
var gridView = listView.View as GridView;
if (gridView == null)
return;
// If the column is automatically sized, change the column width to re-apply automatic width
foreach (var column in gridView.Columns.Where(column => Double.IsNaN(column.Width)))
{
Contract.Assume(column != null);
column.Width = column.ActualWidth;
column.Width = Double.NaN;
}
}
/// <summary>
/// Stops listening for when the <see cref="ItemsControl.Items"/> collection changes.
/// </summary>
protected override void OnDetaching()
{
var listView = AssociatedObject;
if (listView != null)
RemoveHandler(listView.Items);
base.OnDetaching();
}
}
If like me you are old and prefer VB.NET then here's Oskars code:
Public Sub AutoSizeColumns()
Dim gv As GridView = TryCast(Me.listview1.View, GridView)
If gv IsNot Nothing Then
For Each c As GridViewColumn In gv.Columns
If Double.IsNaN(c.Width) Then
c.Width = c.ActualWidth
End If
c.Width = Double.NaN
Next
End If
End Sub
This works great in WPF, finally someone has worked this out. Thanks Oskar.
Isn't there a way to bind to the ActualWidth of the column? Something like :
<GridViewColumn x:Name="column1" Width="{Binding ElementName=column1, Path=ActualWidth}" />
I have tried this and it works only the first time, it seems. No binding error.
You could measure the longest string in pixels and then adjust the column widths accordingly:
Graphics graphics = this.CreateGraphics();
SizeF textSize = graphics.MeasureString("How long am I?", this.Font);
If you create an algorithm for sizing each column as a ratio of these lengths, you should get a good result.

WPF Validation: Clearing all validation errors

I have a WPF UserControl with many other controls inside of it.
TextBoxes are among these.
Every TextBox has its own validation:
<TextBox>
<TextBox.Text>
<Binding Path="MyPath" StringFormat="{}{0:N}" NotifyOnValidationError="True">
<Binding.ValidationRules>
<r:MyValidationRule ValidationType="decimal" />
</Binding.ValidationRules>
</Binding>
<TextBox.Text>
<TextBox>
a
Now suppose the user types some invalid characters into them. They will all become highlighted red.
Now I want to reset all the validation errors (from the incorrect input) and set the recent correct values coming from DataContext.
I set the DataContext in the constructor and I don't want to change it (DataContext = null won't help me then):
DataContext = _myDataContext = new MyDataContext(..);
What I've already found are these classes:
Validation.ClearInvalid(..)
BindingExpression.UpdateTarget();
I think these classes could help me, but they require the Binding of a concrete FrameworkElement and I want to do it globally for all of them.
Should I anyhow iterate through the Visual Tree (which is really what I don't like) or is there any better solution for this?
This is what a BindingGroup is for... You'd set a BindingGroup on a container of all the controls, e.g. the panel that contains them. This would cause the updates to the DataContext to be held until you call UpdateSources on the BindingGroup. If you want to reset the user's input, you'd call CancelEdit instead, and the BindingGroup would reset all controls inside the container to the (still unchanged) values of the DataContext.
I had the same problem. Multiple validated controls on a page. I found/made this solution to update (and clear all validation from) the descentents of a DependencyObject:
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
/// <summary>
/// Updates all binding targets where the data item is of the specified type.
/// </summary>
/// <param name="root">The root.</param>
/// <param name="depth">The depth.</param>
/// <param name="dataItemType">Type of the data item.</param>
/// <param name="clearInvalid">Clear validation errors from binding.</param>
public static void UpdateAllBindingTargets(this DependencyObject root, int depth, Type dataItemType, bool clearInvalid)
{
var bindingExpressions = EnumerateDescendentsBindingExpressions(root, depth);
foreach (BindingExpression be in bindingExpressions.Where(be => be.DataItem != null && be.DataItem.GetType() == dataItemType))
{
if (be != null)
{
be.UpdateTarget();
if (clearInvalid)
System.Windows.Controls.Validation.ClearInvalid(be);
}
}
}
/// <summary>
/// Enumerates all binding expressions on descendents.
/// </summary>
/// <param name="root">The root.</param>
/// <param name="depth">The depth.</param>
/// <returns></returns>
public static IEnumerable<BindingExpression> EnumerateDescendentsBindingExpressions(this DependencyObject root, int depth)
{
return root.EnumerateDescendents(depth).SelectMany(obj => obj.EnumerateBindingExpressions());
}
/// <summary>
/// Enumerates the descendents of the specified root to the specified depth.
/// </summary>
/// <param name="root">The root.</param>
/// <param name="depth">The depth.</param>
public static IEnumerable<DependencyObject> EnumerateDescendents(this DependencyObject root, int depth)
{
int count = VisualTreeHelper.GetChildrenCount(root);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(root, i);
yield return child;
if (depth > 0)
{
foreach (var descendent in EnumerateDescendents(child, --depth))
yield return descendent;
}
}
}
/// <summary>
/// Enumerates the binding expressions of a Dependency Object.
/// </summary>
/// <param name="element">The parent element.</param>
public static IEnumerable<BindingExpression> EnumerateBindingExpressions(this DependencyObject element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
LocalValueEnumerator lve = element.GetLocalValueEnumerator();
while (lve.MoveNext())
{
LocalValueEntry entry = lve.Current;
if (BindingOperations.IsDataBound(element, entry.Property))
{
if (entry.Value is PriorityBindingExpression)
{
foreach (BindingExpression expr in ((PriorityBindingExpression)entry.Value).BindingExpressions)
yield return expr;
}
else if (entry.Value is MultiBindingExpression)
{
foreach (BindingExpression expr in ((MultiBindingExpression)entry.Value).BindingExpressions)
yield return expr;
}
else
yield return entry.Value as BindingExpression;
}
}
}
Why won't you just trigger NotifyPropertyChanged for all the properties of your data source? This will update binding and UI controls should get values from datacontext (which are valid, thus validation errors will be cleared)?
I'm not sure what you mean by
I set the DataContext in the constructor and I don't want to change it
(DataContext = null won't help me then)
Generally to reset all bindings on the form you do the following: (assuming a controller for views/viewmodel wiring, otherwise just use a code-behind on the view.)
var dataContext = view.DataContext;
view.DataContext = null;
view.DataContext = dataContext;
It doesn't change it to a new data context, it just drops the data context and reloads it. This kicks off all of the bindings to re-load.
Although hbarck gave a perfectly correct answer, I would just like to add that for many standard WPF controls, BindingGroups are created automatically. Therefore, in most cases, the following simple code is enough for clearing all validation errors inside some control (for example, DataGrid):
foreach (var bg in BindingOperations.GetSourceUpdatingBindingGroups(myDataGrid))
bg.CancelEdit();

Validation in textbox in WPF

I am currently working on a WPF application where I would like to have a TextBox that can only have numeric entries in it. I know that I can validate the content of it when I lost the focus and block the content from being numeric, but in other Windows Form application, we use to totally block any input except numerical from being written down. Plus, we use to put that code in a separate dll to reference it in many places.
Here is the code in 2008 not using WPF:
Public Shared Sub BloquerInt(ByRef e As System.Windows.Forms.KeyPressEventArgs, ByRef oTxt As Windows.Forms.TextBox, ByVal intlongueur As Integer)
Dim intLongueurSelect As Integer = oTxt.SelectionLength
Dim intPosCurseur As Integer = oTxt.SelectionStart
Dim strValeurTxtBox As String = oTxt.Text.Substring(0, intPosCurseur) & oTxt.Text.Substring(intPosCurseur + intLongueurSelect, oTxt.Text.Length - intPosCurseur - intLongueurSelect)
If IsNumeric(e.KeyChar) OrElse _
Microsoft.VisualBasic.Asc(e.KeyChar) = System.Windows.Forms.Keys.Back Then
If Microsoft.VisualBasic.AscW(e.KeyChar) = System.Windows.Forms.Keys.Back Then
e.Handled = False
ElseIf strValeurTxtBox.Length < intlongueur Then
e.Handled = False
Else
e.Handled = True
End If
Else
e.Handled = True
End If
Is there an equivalent way in WPF? I wouldn't mind if this is in a style but I am new to WPF so style are a bit obscure to what they can or can't do.
You can restrict the input to numbers only using an attached property on the TextBox. Define the attached property once (even in a separate dll) and use it on any TextBox. Here is the attached property:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
/// <summary>
/// Class that provides the TextBox attached property
/// </summary>
public static class TextBoxService
{
/// <summary>
/// TextBox Attached Dependency Property
/// </summary>
public static readonly DependencyProperty IsNumericOnlyProperty = DependencyProperty.RegisterAttached(
"IsNumericOnly",
typeof(bool),
typeof(TextBoxService),
new UIPropertyMetadata(false, OnIsNumericOnlyChanged));
/// <summary>
/// Gets the IsNumericOnly property. This dependency property indicates the text box only allows numeric or not.
/// </summary>
/// <param name="d"><see cref="DependencyObject"/> to get the property from</param>
/// <returns>The value of the StatusBarContent property</returns>
public static bool GetIsNumericOnly(DependencyObject d)
{
return (bool)d.GetValue(IsNumericOnlyProperty);
}
/// <summary>
/// Sets the IsNumericOnly property. This dependency property indicates the text box only allows numeric or not.
/// </summary>
/// <param name="d"><see cref="DependencyObject"/> to set the property on</param>
/// <param name="value">value of the property</param>
public static void SetIsNumericOnly(DependencyObject d, bool value)
{
d.SetValue(IsNumericOnlyProperty, value);
}
/// <summary>
/// Handles changes to the IsNumericOnly property.
/// </summary>
/// <param name="d"><see cref="DependencyObject"/> that fired the event</param>
/// <param name="e">A <see cref="DependencyPropertyChangedEventArgs"/> that contains the event data.</param>
private static void OnIsNumericOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
bool isNumericOnly = (bool)e.NewValue;
TextBox textBox = (TextBox)d;
if (isNumericOnly)
{
textBox.PreviewTextInput += BlockNonDigitCharacters;
textBox.PreviewKeyDown += ReviewKeyDown;
}
else
{
textBox.PreviewTextInput -= BlockNonDigitCharacters;
textBox.PreviewKeyDown -= ReviewKeyDown;
}
}
/// <summary>
/// Disallows non-digit character.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">An <see cref="TextCompositionEventArgs"/> that contains the event data.</param>
private static void BlockNonDigitCharacters(object sender, TextCompositionEventArgs e)
{
foreach (char ch in e.Text)
{
if (!Char.IsDigit(ch))
{
e.Handled = true;
}
}
}
/// <summary>
/// Disallows a space key.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">An <see cref="KeyEventArgs"/> that contains the event data.</param>
private static void ReviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)
{
// Disallow the space key, which doesn't raise a PreviewTextInput event.
e.Handled = true;
}
}
}
Here is how to use it (replace "controls" with your own namespace):
<TextBox controls:TextBoxService.IsNumericOnly="True" />
You can put a validation in your binding
<TextBox>
<TextBox.Text>
<Binding Path="CategoriaSeleccionada.ColorFondo"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<utilities:RGBValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Look at this example (of my program), you put the validation inside the binding like this. With UpdateSourceTrigger you can change when you binding will be updated (lost focus, in every change...)
Well, the validation is a class, I will put you an example:
class RGBValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
// Here you make your validation using the value object.
// If you want to check if the object is only numbers you can
// Use some built-in method
string blah = value.ToString();
int num;
bool isNum = int.TryParse(blah, out num);
if (isNum) return new ValidationResult(true, null);
else return new ValidationResult(false, "It's no a number");
}
}
In short, do the job inside that method and return a new ValidationResult. The first parameter is a bool, true if the validation is good, false if not. The second parameter is only a message for information.
I think that this is the basics of textbox validation.
Hope this help.
EDIT: Sorry, I don't know VB.NET but I think that the C# code is pretty simple.

Formatting text in WinForm Label

Is it possible to format certain text in a WinForm Label instead of breaking the text into multiple labels? Please disregard the HTML tags within the label's text; it's only used to get my point out.
For example:
Dim myLabel As New Label
myLabel.Text = "This is <b>bold</b> text. This is <i>italicized</i> text."
Which would produce the text in the label as:
This is bold text. This is
italicized text.
That's not possible with a WinForms label as it is. The label has to have exactly one font, with exactly one size and one face. You have a couple of options:
Use separate labels
Create a new Control-derived class that does its own drawing via GDI+ and use that instead of Label; this is probably your best option, as it gives you complete control over how to instruct the control to format its text
Use a third-party label control that will let you insert HTML snippets (there are a bunch - check CodeProject); this would be someone else's implementation of #2.
Not really, but you could fake it with a read-only RichTextBox without borders. RichTextBox supports Rich Text Format (rtf).
Another workaround, late to the party: if you don't want to use a third party control, and you're just looking to call attention to some of the text in your label, and you're ok with underlines, you can use a LinkLabel.
Note that many consider this a 'usability crime', but if you're not designing something for end user consumption then it may be something you're prepared to have on your conscience.
The trick is to add disabled links to the parts of your text that you want underlined, and then globally set the link colors to match the rest of the label. You can set almost all the necessary properties at design-time apart from the Links.Add() piece, but here they are in code:
linkLabel1.Text = "You are accessing a government system, and all activity " +
"will be logged. If you do not wish to continue, log out now.";
linkLabel1.AutoSize = false;
linkLabel1.Size = new Size(365, 50);
linkLabel1.TextAlign = ContentAlignment.MiddleCenter;
linkLabel1.Links.Clear();
linkLabel1.Links.Add(20, 17).Enabled = false; // "government system"
linkLabel1.Links.Add(105, 11).Enabled = false; // "log out now"
linkLabel1.LinkColor = linkLabel1.ForeColor;
linkLabel1.DisabledLinkColor = linkLabel1.ForeColor;
Result:
Worked solution for me - using custom RichEditBox. With right properties it will be looked as simple label with bold support.
1) First, add your custom RichTextLabel class with disabled caret :
public class RichTextLabel : RichTextBox
{
public RichTextLabel()
{
base.ReadOnly = true;
base.BorderStyle = BorderStyle.None;
base.TabStop = false;
base.SetStyle(ControlStyles.Selectable, false);
base.SetStyle(ControlStyles.UserMouse, true);
base.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
base.MouseEnter += delegate(object sender, EventArgs e)
{
this.Cursor = Cursors.Default;
};
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x204) return; // WM_RBUTTONDOWN
if (m.Msg == 0x205) return; // WM_RBUTTONUP
base.WndProc(ref m);
}
}
2) Split you sentence to words with IsSelected flag, that determine if that word should be bold or no :
private void AutocompleteItemControl_Load(object sender, EventArgs e)
{
RichTextLabel rtl = new RichTextLabel();
rtl.Font = new Font("MS Reference Sans Serif", 15.57F);
StringBuilder sb = new StringBuilder();
sb.Append(#"{\rtf1\ansi ");
foreach (var wordPart in wordParts)
{
if (wordPart.IsSelected)
{
sb.Append(#"\b ");
}
sb.Append(ConvertString2RTF(wordPart.WordPart));
if (wordPart.IsSelected)
{
sb.Append(#"\b0 ");
}
}
sb.Append(#"}");
rtl.Rtf = sb.ToString();
rtl.Width = this.Width;
this.Controls.Add(rtl);
}
3) Add function for convert you text to valid rtf (with unicode support!) :
private string ConvertString2RTF(string input)
{
//first take care of special RTF chars
StringBuilder backslashed = new StringBuilder(input);
backslashed.Replace(#"\", #"\\");
backslashed.Replace(#"{", #"\{");
backslashed.Replace(#"}", #"\}");
//then convert the string char by char
StringBuilder sb = new StringBuilder();
foreach (char character in backslashed.ToString())
{
if (character <= 0x7f)
sb.Append(character);
else
sb.Append("\\u" + Convert.ToUInt32(character) + "?");
}
return sb.ToString();
}
Works like a charm for me!
Solutions compiled from :
How to convert a string to RTF in C#?
Format text in Rich Text Box
How to hide the caret in a RichTextBox?
Create the text as a RTF file in wordpad
Create Rich text control with no borders and editable = false
Add the RTF file to the project as a resource
In the Form1_load do
myRtfControl.Rtf = Resource1.MyRtfControlText
AutoRichLabel
I was solving this problem by building an UserControl that contains a TransparentRichTextBox that is readonly. The TransparentRichTextBox is a RichTextBox that allows to be transparent:
TransparentRichTextBox.cs:
public class TransparentRichTextBox : RichTextBox
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
static extern IntPtr LoadLibrary(string lpFileName);
protected override CreateParams CreateParams
{
get
{
CreateParams prams = base.CreateParams;
if (TransparentRichTextBox.LoadLibrary("msftedit.dll") != IntPtr.Zero)
{
prams.ExStyle |= 0x020; // transparent
prams.ClassName = "RICHEDIT50W";
}
return prams;
}
}
}
The final UserControl acts as wrapper of the TransparentRichTextBox. Unfortunately, I had to limit it to AutoSize on my own way, because the AutoSize of the RichTextBox became broken.
AutoRichLabel.designer.cs:
partial class AutoRichLabel
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.rtb = new TransparentRichTextBox();
this.SuspendLayout();
//
// rtb
//
this.rtb.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.rtb.Dock = System.Windows.Forms.DockStyle.Fill;
this.rtb.Location = new System.Drawing.Point(0, 0);
this.rtb.Margin = new System.Windows.Forms.Padding(0);
this.rtb.Name = "rtb";
this.rtb.ReadOnly = true;
this.rtb.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.None;
this.rtb.Size = new System.Drawing.Size(46, 30);
this.rtb.TabIndex = 0;
this.rtb.Text = "";
this.rtb.WordWrap = false;
this.rtb.ContentsResized += new System.Windows.Forms.ContentsResizedEventHandler(this.rtb_ContentsResized);
//
// AutoRichLabel
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.BackColor = System.Drawing.Color.Transparent;
this.Controls.Add(this.rtb);
this.Name = "AutoRichLabel";
this.Size = new System.Drawing.Size(46, 30);
this.ResumeLayout(false);
}
#endregion
private TransparentRichTextBox rtb;
}
AutoRichLabel.cs:
/// <summary>
/// <para>An auto sized label with the ability to display text with formattings by using the Rich Text Format.</para>
/// <para>­</para>
/// <para>Short RTF syntax examples: </para>
/// <para>­</para>
/// <para>Paragraph: </para>
/// <para>{\pard This is a paragraph!\par}</para>
/// <para>­</para>
/// <para>Bold / Italic / Underline: </para>
/// <para>\b bold text\b0</para>
/// <para>\i italic text\i0</para>
/// <para>\ul underline text\ul0</para>
/// <para>­</para>
/// <para>Alternate color using color table: </para>
/// <para>{\colortbl ;\red0\green77\blue187;}{\pard The word \cf1 fish\cf0 is blue.\par</para>
/// <para>­</para>
/// <para>Additional information: </para>
/// <para>Always wrap every text in a paragraph. </para>
/// <para>Different tags can be stacked (i.e. \pard\b\i Bold and Italic\i0\b0\par)</para>
/// <para>The space behind a tag is ignored. So if you need a space behind it, insert two spaces (i.e. \pard The word \bBOLD\0 is bold.\par)</para>
/// <para>Full specification: http://www.biblioscape.com/rtf15_spec.htm </para>
/// </summary>
public partial class AutoRichLabel : UserControl
{
/// <summary>
/// The rich text content.
/// <para>­</para>
/// <para>Short RTF syntax examples: </para>
/// <para>­</para>
/// <para>Paragraph: </para>
/// <para>{\pard This is a paragraph!\par}</para>
/// <para>­</para>
/// <para>Bold / Italic / Underline: </para>
/// <para>\b bold text\b0</para>
/// <para>\i italic text\i0</para>
/// <para>\ul underline text\ul0</para>
/// <para>­</para>
/// <para>Alternate color using color table: </para>
/// <para>{\colortbl ;\red0\green77\blue187;}{\pard The word \cf1 fish\cf0 is blue.\par</para>
/// <para>­</para>
/// <para>Additional information: </para>
/// <para>Always wrap every text in a paragraph. </para>
/// <para>Different tags can be stacked (i.e. \pard\b\i Bold and Italic\i0\b0\par)</para>
/// <para>The space behind a tag is ignored. So if you need a space behind it, insert two spaces (i.e. \pard The word \bBOLD\0 is bold.\par)</para>
/// <para>Full specification: http://www.biblioscape.com/rtf15_spec.htm </para>
/// </summary>
[Browsable(true)]
public string RtfContent
{
get
{
return this.rtb.Rtf;
}
set
{
this.rtb.WordWrap = false; // to prevent any display bugs, word wrap must be off while changing the rich text content.
this.rtb.Rtf = value.StartsWith(#"{\rtf1") ? value : #"{\rtf1" + value + "}"; // Setting the rich text content will trigger the ContentsResized event.
this.Fit(); // Override width and height.
this.rtb.WordWrap = this.WordWrap; // Set the word wrap back.
}
}
/// <summary>
/// Dynamic width of the control.
/// </summary>
[Browsable(false)]
public new int Width
{
get
{
return base.Width;
}
}
/// <summary>
/// Dynamic height of the control.
/// </summary>
[Browsable(false)]
public new int Height
{
get
{
return base.Height;
}
}
/// <summary>
/// The measured width based on the content.
/// </summary>
public int DesiredWidth { get; private set; }
/// <summary>
/// The measured height based on the content.
/// </summary>
public int DesiredHeight { get; private set; }
/// <summary>
/// Determines the text will be word wrapped. This is true, when the maximum size has been set.
/// </summary>
public bool WordWrap { get; private set; }
/// <summary>
/// Constructor.
/// </summary>
public AutoRichLabel()
{
InitializeComponent();
}
/// <summary>
/// Overrides the width and height with the measured width and height
/// </summary>
public void Fit()
{
base.Width = this.DesiredWidth;
base.Height = this.DesiredHeight;
}
/// <summary>
/// Will be called when the rich text content of the control changes.
/// </summary>
private void rtb_ContentsResized(object sender, ContentsResizedEventArgs e)
{
this.AutoSize = false; // Disable auto size, else it will break everything
this.WordWrap = this.MaximumSize.Width > 0; // Enable word wrap when the maximum width has been set.
this.DesiredWidth = this.rtb.WordWrap ? this.MaximumSize.Width : e.NewRectangle.Width; // Measure width.
this.DesiredHeight = this.MaximumSize.Height > 0 && this.MaximumSize.Height < e.NewRectangle.Height ? this.MaximumSize.Height : e.NewRectangle.Height; // Measure height.
this.Fit(); // Override width and height.
}
}
The syntax of the rich text format is quite simple:
Paragraph:
{\pard This is a paragraph!\par}
Bold / Italic / Underline text:
\b bold text\b0
\i italic text\i0
\ul underline text\ul0
Alternate color using color table:
{\colortbl ;\red0\green77\blue187;}
{\pard The word \cf1 fish\cf0 is blue.\par
But please note: Always wrap every text in a paragraph. Also, different tags can be stacked (i.e. \pard\b\i Bold and Italic\i0\b0\par) and the space character behind a tag is ignored. So if you need a space behind it, insert two spaces (i.e. \pard The word \bBOLD\0 is bold.\par). To escape \ or { or }, please use a leading \.
For more information there is a full specification of the rich text format online.
Using this quite simple syntax you can produce something like you can see in the first image. The rich text content that was attached to the RtfContent property of my AutoRichLabel in the first image was:
{\colortbl ;\red0\green77\blue187;}
{\pard\b BOLD\b0 \i ITALIC\i0 \ul UNDERLINE\ul0 \\\{\}\par}
{\pard\cf1\b BOLD\b0 \i ITALIC\i0 \ul UNDERLINE\ul0\cf0 \\\{\}\par}
If you want to enable word wrap, please set the maximum width to a desired size. However, this will fix the width to the maximum width, even when the text is shorter.
Have fun!
There is an excellent article from 2009 on Code Project named "A Professional HTML Renderer You Will Use" which implements something similar to what the original poster wants.
I use it successfully within several projects of us.
Very simple solution:
Add 2 labels on the form, LabelA and LabelB
Go to properties for LabelA and dock it to left.
Go to properties for LabelB and dock it to left as well.
Set Font to bold for LabelA .
Now the LabelB will shift depending on length of text of LabelA.
That's all.
I Would also be interested in finding out if it is possible.
When we couldn't find a solution we resorted to Component Ones 'SuperLabel' control which allows HTML markup in a label.
Realising this is an old question, my answer is more for those, like me, who still may be looking for such solutions and stumble upon this question.
Apart from what was already mentioned, DevExpress's LabelControl is a label that supports this behaviour - demo here. Alas, it is part of a paid library.
If you're looking for free solutions, I believe HTML Renderer is the next best thing.
A FlowLayoutPanel works well for your problem. If you add labels to the flow panel and format each label's font and margin properties, then you can have different font styles. Pretty quick and easy solution to get working.
Yeah.
You can implements, using HTML Render.
For you see, click on the link: https://htmlrenderer.codeplex.com/
I hope this is useful.

Resources