I'm adding some TextBlock elements to Border elements in a StackPanel.
I'm adding and formating the text of the TextBlock by adding Inlines.
When clicked, I want to get the formated text of the TextBlock.Here is my code.
public void addText()
{
TextBlock myText = new TextBlock();
myText.Inlines.Add(new Bold(new Run("Hello ")));
myText.Inlines.Add("World!");
Border myBorder = new Border();
myBorder.Child = myText;
myBorder.MouseDown += new MouseButtonEventHandler(Border_Clicked);
myStackPanel.Children.Add(myBorder);
}
private void Border_Clicked(object sender, MouseButtonEventArgs e)
{
//Border senderBox = (Border)sender;
//TextBlock senderText = (TextBlock)senderBox.Child;
//Bold inline = (Bold) senderText.Inlines.ElementAt(0);
// How to Output "Hello "?
}
Border_Clicked should output "Hello ". As you can see I'm able to get to the bolded Text but how can I ouput it?
#Helen, There is a way to get the Text from the TextPointer using TextRange. Try this code
void myBorder_MouseDown(object sender, MouseButtonEventArgs e)
{
var senderBox = (Border)sender;
var senderText = (TextBlock)senderBox.Child;
var inline = (Bold)senderText.Inlines.ElementAt(0);
var textRange = new TextRange(inline.ContentStart, inline.ContentEnd);
Console.WriteLine(textRange.Text);
}
Is the problem to get text out of Bold element?
private void Border_Clicked(object sender, MouseButtonEventArgs e)
{
var border = (Border)sender;
var textBlock = (TextBlock)border.Child;
var bold = (Bold)textBlock.Inlines.ElementAt(0);
// How to Output "Hello "?
// try
var output = ((Run)bold).Text;
// or rather (because Bold is a wrapper)
var output = ((Run)bold.Inlines[0]).Text;
}
If you can add inline like this
myText.Inlines.Add(new Run("Bold text") { FontWeight = FontWeight.Bold });
then it's
var run = (Run)textBlock.Inlines[0];
var output = run.Text;
It is not possible to control Font characteristics in a MessageBox. I think you should consider to create a "custom MessageBox". Something like this:
<Window x:Class="WpfApplication1.CustomMessageBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight" MaxWidth="400">
<Grid x:Name="GridContent" Margin="10">
</Grid>
</Window>
So in its constructor you could send your Bold:
private Bold _text;
public CustomMessageBox(Bold formattedText)
{
_text = formattedText;
GridContent.Child = _text;
}
Using:
private void Border_Clicked(object sender, MouseButtonEventArgs e)
{
Border senderBox = (Border)sender;
TextBlock senderText = (TextBlock)senderBox.Child;
Bold inline = (Bold) senderText.Inlines.ElementAt(0);
var customMsgBox = new CustomMessageBox(inline);
customMsgBox.ShowModal();
}
Now, if you are not sure it will be always a Bold object, I suggest you to save your formatted text in XML and load it after. Take a look at this: showing formatted text
Related
This is my code to only make the Header of the listView bold, but it is not working, because not only the Header, but all the items are getting bold.
listView.Columns[0].ListView.Font = new Font(listView.Columns[0].ListView.Font, FontStyle.Bold);
Anyone has a solution?
Unfortunately you cannot change the header font. But you can change the font for each individual list item. The simple but hacky approach is to set ListView.Font to the bold font and change the font of every ListItem.Font to the default font.
listView.Font = _headerFont;
foreach(ListViewItem item in listView.Items)
{
item.Font = SystemFonts.DefaultFont;
}
Alternatively for full control set the OwnerDraw property to true and handle both DrawColumnHeader and DrawItem events like below:
public partial class Form1 : Form
{
private readonly Font _headerFont = new Font(SystemFonts.DefaultFont, FontStyle.Bold);
public Form1()
{
InitializeComponent();
listView.OwnerDraw = true;
listView.DrawColumnHeader += DrawColumnHeader;
listView.DrawItem += DrawItem;
}
private void DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
// Draw default background
e.DrawBackground();
// Draw text in a different font
TextRenderer.DrawText(e.Graphics,
e.Header.Text,
_headerFont,
e.Bounds,
SystemColors.ControlText,
TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
}
private void DrawItem(object sender, DrawListViewItemEventArgs e)
{
// Use defaults for Items
e.DrawDefault = true;
}
}
The example above shows how it works, but in a real world application you also have to draw dependent on the item's state like in the more comprehensive example in the .NET docs
Using the answer of Christian Held, I come up with the next solution:
Put this in the constructor of the form:
List<ColumnHeader> columns = new List<ColumnHeader>
{
new ColumnHeader{Text = "ColumnOne", TextAlign = HorizontalAlignment.Left},
new ColumnHeader{Text = "ColumnTwo", TextAlign = HorizontalAlignment.Left},
new ColumnHeader{Text = "ColumnThree", TextAlign = HorizontalAlignment.Left}
};
MyListView.Font = new Font(SystemFonts.DefaultFont, FontStyle.Bold);
columns.ForEach(col => MyListView.Columns.Add(col));
MyListView.View = View.Details;
After that, when you fill with data your ListView, you must set the font for every record added:
foreach (MyData myData in MyDatas){
listViewItem = new ListViewItem(new string[]
{
myData.Data1,
myData.Data2,
myData.Data3
});
listViewItem.Font = SystemFonts.DefaultFont;
MyListView.Items.Add(listViewItem);
}
And it works fine for me. If you want to change the backcolor or other things in the font of the data, just change the SystemFonts.DefaultFont for some font that you like, something like
var myFont = new Font(...your details here....);
I want to set this kind of text on TextBlock in a UWP project :
"<Bold>" + variable + "</Bold>"
But set it to Text value do not consider <Bold> tags.
So i searched how to do and the only one answer is "creat Inlines and add it to your textBlock".
but i don't want to do it on my View Model.
So i'm looking for a converter to replace my text attribute by a inlines collection to set on my textBlock.
I found some example (https://social.msdn.microsoft.com/Forums/en-US/1a1af975-e186-4167-b1c9-cc86afcdd93a/how-to-show-text-in-textblock-as-rich-text-format?forum=wpf), but not working on universal Windows apps (UWP).
I tried this but i have an error (unable to cast Binding to string):
<TextBlock x:Name="newsArticleSections"
Tag="{Binding RelativeSource={RelativeSource Self}, Mode=OneWay, Converter={StaticResource TextToRunConverter}, ConverterParameter={Binding ArticleSections}}"/>
And this is my converter :
public object Convert(object value, Type targetType, object parameter, string language)
{
TextBlock textblock = value as TextBlock;
textblock.ClearValue(TextBlock.TextProperty);
Run run = new Run();
run.Text = (string)parameter;
textblock.Inlines.Add(run);
return null;
}
It's just the ways that i had explored, but with no result for the moment.
Does someone has another idea ?
#devuxer answer was a good idea, but only for WPF project.
So i used it to make UWP solution and it works :
Create a Formatter class :
public class TextBlockFormatter
{
public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
"FormattedText",
typeof(string),
typeof(TextBlockFormatter),
new PropertyMetadata(null, FormattedTextPropertyChanged));
public static void SetFormattedText(DependencyObject textBlock, string value)
{
textBlock.SetValue(FormattedTextProperty, value);
}
public static string GetFormattedText(DependencyObject textBlock)
{
return (string)textBlock.GetValue(FormattedTextProperty);
}
private static void FormattedTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Clear current textBlock
TextBlock textBlock = d as TextBlock;
textBlock.ClearValue(TextBlock.TextProperty);
textBlock.Inlines.Clear();
// Create new formatted text
string formattedText = (string)e.NewValue ?? string.Empty;
string #namespace = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
formattedText = $#"<Span xml:space=""preserve"" xmlns=""{#namespace}"">{formattedText}</Span>";
// Inject to inlines
var result = (Span)XamlReader.Load(formattedText);
textBlock.Inlines.Add(result);
}
}
And add this reference to your XAML file :
xmlns:helpers="using:MyProject.Helpers"
To use the formatter, simply add a TextBlock and declare your binding on FormattedText, like this :
<TextBlock x:Name="textBlock" helpers:TextBlockFormatter.FormattedText="{Binding Content}">
I've been using the following solution for WPF projects (not UWP), so I'm not sure if it will work for you, but feel free to give it a try.
You begin by putting the following into a class file within, say, a Helpers folder inside your project:
public class Formatter
{
public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
"FormattedText",
typeof(string),
typeof(Formatter),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure, FormattedTextPropertyChanged));
public static void SetFormattedText(DependencyObject textBlock, string value)
{
textBlock.SetValue(FormattedTextProperty, value);
}
public static string GetFormattedText(DependencyObject textBlock)
{
return (string)textBlock.GetValue(FormattedTextProperty);
}
private static void FormattedTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBlock = d as TextBlock;
if (textBlock == null) return;
const string #namespace = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
var formattedText = (string)e.NewValue ?? string.Empty;
formattedText = $#"<Span xml:space=""preserve"" xmlns=""{#namespace}"">{formattedText}</Span>";
textBlock.Inlines.Clear();
using (var xmlReader = XmlReader.Create(new StringReader(formattedText)))
{
var result = (Span)XamlReader.Load(xmlReader);
textBlock.Inlines.Add(result);
}
}
}
Then, in your XAML file, reference the namespace, like so:
xmlns:helpers="clr-namespace:MyProject.Helpers"
To use the formatter, simply add a TextBlock and declare your binding on FormattedText (instead of Text), like this:
<TextBlock helpers:Formatter.FormattedText="{Binding Content}" />
I use something like this:
XAML
<TextBlock Name="TB" Text="Bold Text " FontWeight="Bold" />
At the Controller:
TB.Inlines.Add(new Run { Text = "New append text with diferent FontWeigth on the same TextBlock", FontWeight = FontWeights.Normal } );
Output will be:
Bold Text New append text with diferent FontWeigth on the same TextBlock
I hope my contribution is still useful, or may be useful to other reaching this question. I've updated the Formatter class from #Geoffrey, so that specific characters in the textblock are turning bold. I having a list with people and a search query at the top. The query part in name of the person is turning bold.
Query: ic
Result: Rick
public class TextBlockFormatter
{
const string #namespace = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
"FormattedText",
typeof(string),
typeof(TextBlockFormatter),
new PropertyMetadata(null, FormattedTextPropertyChanged));
public static void SetFormattedText(DependencyObject textBlock, string value)
{
textBlock.SetValue(FormattedTextProperty, value);
}
public static string GetFormattedText(DependencyObject textBlock)
{
return (string)textBlock.GetValue(FormattedTextProperty);
}
private static void FormattedTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBlock = (TextBlock)d;
if (textBlock.DataContext == null)
{
textBlock.DataContextChanged += TextBlock_DataContextChanged;
return;
}
var query = (string)e.NewValue ?? string.Empty;
HighlightSearch(textBlock, query);
}
private static void HighlightSearch(TextBlock textBlock, string value)
{
var name = ((Person)textBlock.DataContext).Name;
var query = value.ToUpper();
var indexOfSearch = name.ToUpper().IndexOf(query, 0);
if (indexOfSearch < 0) return;
// add normal text first
var firstText = name.Substring(0, indexOfSearch).Replace("&", "&");
var first = $#"<Span xml:space=""preserve"" xmlns=""{#namespace}"">{firstText}</Span>";
var firstResult = (Span)XamlReader.Load(first);
// add the bold text
var boldText = name.Substring(indexOfSearch, query.Length).Replace("&", "&");
var bold = $#"<Bold xml:space=""preserve"" xmlns=""{#namespace}"">{boldText}</Bold>";
var boldResult = (Bold)XamlReader.Load(bold);
// add the rest of the text
var restStartIndex = indexOfSearch + query.Length;
var restText = name.Substring(restStartIndex, name.Length - restStartIndex).Replace("&", "&");
var rest = $#"<Span xml:space=""preserve"" xmlns=""{#namespace}"">{restText}</Span>";
var restResult = (Span)XamlReader.Load(rest);
// Clear current textBlock
textBlock.ClearValue(TextBlock.TextProperty);
textBlock.Inlines.Clear();
// Inject to inlines
textBlock.Inlines.Add(firstResult);
textBlock.Inlines.Add(boldResult);
textBlock.Inlines.Add(restResult);
}
private static void TextBlock_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
var block = (TextBlock)sender;
if (block.DataContext == null) return;
block.DataContextChanged -= TextBlock_DataContextChanged;
var query = (string)sender.GetValue(FormattedTextProperty);
HighlightSearch(block, query);
}
}
Use in XAML:
<TextBlock Text="{Binding Name}" helpers:TextBlockFormatter.FormattedText="{Binding ElementName=queryTextBox, Path=Text}" />
I had some issues, For example my datasource was not set when the FormattedTextPropertyChanged was called. Also you need to take care of escaping the text. I made myself easy by using the Replace function.
I put some colored text to my rich text box my using the following code:
richTextBox1.SelectionColor = Color.Blue;
richTextBox1.SelectedText = "Name";
richTextBox1.SelectionColor = Color.Black;
richTextBox1.SelectedText = ": some message.";
But when I hide the richtextbox from the user by setting its parent property to null (I have this panel that holds different rich text boxes from time to time), and put it back, the rich text box does not retain the text colors I applied to it. All texts becomes black.
Update: I tried an experiment. In my main program, I have a UserControl (which has a Panel) where I put a RichTextBox with colored text. I have many RichTextBoxes which I store to a HashTable.
So when I need a RichTextBox, I retrieve it from my HashTable, put some colored text into it, put it inside my UserControl's Panel and finally put my UserControl to my program's Form. My UserControl can actually be temporarily removed from the program's Form when a user clicks on a button, I do using Controls.Remove(). To put it back into my Form, I use Controls.Add(). The problem is, when the UserControl is added back, the RichTextBox's texts are not colored anymore.
I tried doing something similar in another experimental program.
public partial class Form1 : Form
{
private chat.UserControl1 ChatWindowKuno = new chat.UserControl1();
private Hashtable htChatLogs = new Hashtable(30);
public Form1()
{
InitializeComponent();
createRTBox();
}
private void createRTBox()
{
RichTextBox richTextBox1 = new RichTextBox();
richTextBox1.Multiline = true;
richTextBox1.Dock = DockStyle.Fill;
richTextBox1.ReadOnly = true;
richTextBox1.BackColor = SystemColors.Window;
htChatLogs.Add("Basta", richTextBox1);
}
private void button1_Click_1(object sender, EventArgs e)
{
if (ChatWindowKuno.Parent == null)
ChatWindowKuno.Parent = tabPage2;
else
ChatWindowKuno.Parent = null;
}
private void button2_Click(object sender, EventArgs e)
{
// Clear all text from the RichTextBox;
RichTextBox richTextBox1 = (RichTextBox)htChatLogs["Basta"];
richTextBox1.Clear();
richTextBox1.SelectionFont = new Font("Segoe UI", 8.25F, FontStyle.Regular);
richTextBox1.SelectionColor = Color.Blue;
richTextBox1.SelectedText = "Xel";
richTextBox1.SelectionColor = Color.Black;
richTextBox1.SelectedText = ": Listening to Be My Last by Utada Hikaru.";
richTextBox1.SelectionColor = Color.Gray;
richTextBox1.SelectionFont = new Font("Segoe UI", 8.25F, FontStyle.Italic);
richTextBox1.SelectedText = " [5:56pm] \n";
richTextBox1.SelectionColor = Color.Gray;
richTextBox1.SelectedText = "[5:56pm] ";
richTextBox1.SelectionFont = new Font("Segoe UI", 8.25F, FontStyle.Regular);
richTextBox1.SelectionColor = Color.Blue;
richTextBox1.SelectedText = "Xel";
richTextBox1.SelectionColor = Color.Black;
richTextBox1.SelectedText = ": Listening to Be My Last by Utada Hikaru.";
}
private void button3_Click(object sender, EventArgs e)
{
RichTextBox richTextBox1 = (RichTextBox)htChatLogs["Basta"];
ChatWindowKuno.ChatLog = richTextBox1;
}
}
The ChatLog property of usercontrol1 is this:
public Control ChatLogPanel
{
get
{
return panel1.Controls[0];
}
set
{
panel1.Controls.Clear();
panel1.Controls.Add(value);
}
}
I click the 3 buttons randomly in my experimental program, but the text colors are retained.
You shouldn't use Parent property to hide, but Visible property instead.
If you hide a richtextbox using richTextBox.Visible = false it keeps its formatting (tested).
EDIT :
as discussed in the comments below, I suggest you to use only one RichTextBox and store several Rtf strings in a Dictionary (or Hashtable) to mimic the existence of different RichTextBox'es.
An example of what I mean can be found Here
If I have a RichTextBox and want output like the first line is a font with capital letters and bold, while the next line on the contrary, what should I do?
output like this:
MY NAME IS
Diana
My address is
China
Hey try this, it works, but you might see the text flicker for a second if the user types really fast, like holding down "Enter" and etc.
private void Form_Load(object sender, EventArgs e)
{
// append the function to the RichTextBox's TextChanged event
MyRichTextBox.TextChanged += Capitalize_Bold_FirstLine;
}
private void Capitalize_Bold_FirstLine(object sender, EventArgs e)
{
RichTextBox box = sender as RichTextBox;
if (box != null && box.Text != "")
{
// get the current selection text of the textbox
int ss = box.SelectionStart;
int sl = box.SelectionLength;
// get the position where the first line ends
int firstLineEnd = box.Text.IndexOf('\n');
if (firstLineEnd < 0)
firstLineEnd = box.Text.Length;
// split the lines
string[] lines = box.Text.Split('\n');
// capitalize the first line
lines[0] = lines[0].ToUpper();
// join them back and set the new text
box.Text = String.Join("\n", lines);
// select the first line and make it bold
box.SelectionStart = 0;
box.SelectionLength = firstLineEnd;
box.SelectionFont = new Font(box.Font, FontStyle.Bold);
// select the rest and make it regular
box.SelectionStart = firstLineEnd;
box.SelectionLength = box.Text.Length - firstLineEnd;
box.SelectionFont = new Font(box.Font, FontStyle.Regular);
// go back to what the user had selected
box.SelectionStart = ss;
box.SelectionLength = sl;
}
}
I have WPF window with a FlowDocument with several hyperlinks in it:
<FlowDocumentScrollViewer>
<FlowDocument TextAlignment="Left" >
<Paragraph>Some text here
<Hyperlink Click="Hyperlink_Click">open form</Hyperlink>
</Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
In the C# code I handle Click event to create and show a new WPF Window:
private void Hyperlink_Click(object sender, RoutedEventArgs e)
{
if (sender is Hyperlink)
{
var wnd = new SomeWindow();
//wnd.Left = ???
//wnd.Top = ???
wnd.Show();
}
}
I need this window to appear next to hyperlink's actual position. So I assume it requires assigning values to the window's Left and Top properties. But I have no idea how to obtain hyperlink position.
You can use ContentStart or ContentEnd to get a TextPointer for the start or end of the hyperlink and then call GetCharacterRect to get the bounding box relative to the FlowDocumentScrollViewer. If you get a reference to the FlowDocumentScrollViewer, you can use PointToScreen to convert it to screen coordinates.
private void Hyperlink_Click(object sender, RoutedEventArgs e)
{
var hyperlink = sender as Hyperlink;
if (hyperlink != null)
{
var rect = hyperlink.ContentStart.GetCharacterRect(
LogicalDirection.Forward);
var viewer = FindAncestor(hyperlink);
if (viewer != null)
{
var screenLocation = viewer.PointToScreen(rect.Location);
var wnd = new Window();
wnd.WindowStartupLocation = WindowStartupLocation.Manual;
wnd.Top = screenLocation.Y;
wnd.Left = screenLocation.X;
wnd.Show();
}
}
}
private static FrameworkElement FindAncestor(object element)
{
while(element is FrameworkContentElement)
{
element = ((FrameworkContentElement)element).Parent;
}
return element as FrameworkElement;
}