I have a small problem, I have created a private chat message system using c#. Now what I need is a way to send a clickable link to other person.
When selecting a person from the list, I press invite button and a message comes to the messagebox like "to User1: join from this link"
private void InvBtn_Click(object sender, RoutedEventArgs e)
{
selectedUser = UsersListBox.SelectedItem.ToString();
if (selectedUser != login)
{
MessageBox.Show("Select other user than yourself");
return;
}
else
{
Msg.Text = selectedUser + " join from this 'link' ";
}
}
After sending the other person gets the message to RichTextBox saying
From user2: join from this link
There is no need for open a website, but other other form where will be more details.
You need to create custom MessageBox with Hyperlink button.
Try this out, here u need to set the height and width property properly....and make the constructor to accept the arguments so that users can design it the way they want.
public class CustomMessageBox
{
public CustomMessageBox()
{
Window w = new Window();
DockPanel panel = new DockPanel();
TextBlock tx = new TextBlock();
Paragraph parx = new Paragraph();
Run run1 = new Run("Text preceeding the hyperlink.");
Run run2 = new Run("Text following the hyperlink.");
Run run3 = new Run("Link Text.");
Hyperlink hyperl = new Hyperlink(run3);
hyperl.NavigateUri = new Uri("http://search.msn.com");
tx.Inlines.Add(hyperl);
panel.Children.Add(tx);
w.Content = panel;
w.Show();
}
}
Source : https://social.msdn.microsoft.com/Forums/vstudio/en-US/57fcd28b-6e9e-4529-a583-892c8f6d7cc8/hyperlink-in-wpf-message-box?forum=wpf
First you need to come up with a way to include your special markup in the text message. You can either pack the entire message in an existing container format (XML, JSON, etc.) or to keep things simple include special markers within the text, for example:
Hi User1, join from [This link:12345].
The same way you could include markup for other things like bold (**bold**), italics (*italics*), or actual hyperlinks to websites.
On the other side, you will need a parser that detects this special markup and replaces it with a clickable link. In the following example I'm using Regex to find and replace all text in the format [Text:Command].
private IEnumerable<Inline> Parse(string text)
{
// Define the format of "special" message segments
Regex commandFinder = new Regex(#"\[(?<text>.+)\:(?<command>.+)]");
// Find all matches in the message text
var matches = commandFinder.Matches(text);
// remember where to split the string so we don't lose other
// parts of the message
int previousMatchEnd = 0;
// loop over all matches
foreach (Match match in matches)
{
// extract the text fore it
string textBeforeMatch = text.Substring(previousMatchEnd, match.Index - previousMatchEnd);
yield return new Run(textBeforeMatch);
previousMatchEnd = match.Index + match.Length;
// extract information and create a clickable link
string commandText = match.Groups["text"].Value;
string command = match.Groups["command"].Value;
// it would be better to use the "Command" property here,
// but for a quick demo this will do
Hyperlink link = new Hyperlink(new Run(commandText));
link.Click += (s, a) => { HandleCommand(command); };
yield return link;
}
// return the rest of the message (or all of it if there was no match)
if (previousMatchEnd < text.Length)
yield return new Run(text.Substring(previousMatchEnd));
}
In the method where you receive the message, you can simply integrate it like this:
// Where you receive the text
// This probably is just a `txtOutput.Text += ` until now
private void OnTextReceived(string text)
{
txtOutput.Inlines.AddRange(Parse(text));
}
// the method that gets invoked when a link is clicked
// and you can parse/handle the actual command
private void HandleCommand(string command)
{
MessageBox.Show("Command clicked: " + command);
}
The message Hi User1, join from [this link:1234567890] will show up as Hi User1, join from this link and will invoke HandleCommand("1234567890") when clicked.
Related
I have two if-else conditions which will display some text on a Windows Forms textbox. This textbox has text set to it from a different thread other than the UI thread.
Context:
delegate void SetTextCallback(string text);
private void SetText(string text)
{
if (this.txtMessages.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.txtMessages.Text = text;
}
}
private void setTextFunc()
{
if(this condition is true)
{
setText("Hello from text 1" + Environment.NewLine);
}
else
{
setText("Hello from text 2" + Environment.NewLine);
}
}
Now the problem is that when Hello from text 2 text gets displayed, it overwrites the first text. I want it to go below and keep the first text.
How can I achieve this?
I thought about using a list collection as so:
List<string> listOfStrings = new List<string>();
listOfStrings.Add("Hello from text 1");
listOfStrings.Add("Hello from text 2");
Then iterating through this list collection. Will that do?
foreach(string x in listOfStrings)
{
MessageBox.Show(x);
}
Any suggestions? I considered a RichTextBox but seeing as I won't be editing the text at runtime, I see no need for it.
The problem you have is that:
this.txtMessages.Text = text;
In your setText method replaces the content of the Text property of your text field. If you want to add to the existing content, you should replace the code with something along the lines of:
this.txtMessages.Text = this.txtMessages.Text + text;
As your code suggests (thanks to the addition of Environment.NewLine in setTextFunc) that you always want to delimit the newly added text with a line-break, consider adding a new function along the lines of:
private void AppendText(string text)
{
// Code to trigger the Invoke here
else
{
this.txtMessages.Text = this.txtMessages.Text + Environment.newLine + text;
}
}
Doing this also allows you to encapsulate the behaviour of "add text with a new line" into a single method - at the moment your setTextFunc method has a really small amount of repeated code (adding Environment.NewLine) which you can elide by doing this.
In my activity I am dynamically creating controls based off of sqlite data. Each item will have a button with a click event that needs to send that rows ID to the new activity.
I have looked at the following page and can get this to work on a button that already exists in my layout. But it doesn't seem to work when the button is dynamically created. Is there something additional I need to do with the button for this to work?
https://developer.xamarin.com/recipes/android/fundamentals/activity/pass_data_between_activity/
Here is the code that creates the button dynamically:
foreach (Tasks item in table)
{
TableRow row = new TableRow(this);
TextView txtTask = new TextView(this);
txtTask.Text = item.Name;
row.AddView(txtTask);
tableLayout.AddView(row);
row = new TableRow(this);
Button btnEdit = new Button(this);
btnEdit.Text = "Edit Record";
btnEdit.SetWidth(300);
btnEdit.Click += delegate
{
Intent viewTask = new Intent(this, typeof(UpdateTaskActivity));
viewTask.PutExtra("TaskId", item.Id);
StartActivity(viewTask);
};
row.AddView(btnEdit);
tableLayout.AddView(row);
}
In the OnCreate method of UpdateTaskActivity I have:
string test = Intent.GetStringExtra("TaskId") ?? "error";
if (test != "error")
{
//Do Stuff
}
But when I put a breakpoint down, my string is always null. I did put a breakpoint to make sure the correct ID is being pulled.
Why does this work with a built in button but does not work with my dynamic one?
Just to avoid confusion, in my startup screen I have a test button, and my main activity has the following code for that button. This code works fine, because the button isn't dymaically created:
//Test button
Button btnTest = FindViewById<Button>(Resource.Id.btnTest);
btnTest.Click += delegate
{
var activity2 = new Intent(this, typeof(UpdateTaskActivity));
activity2.PutExtra("TaskId", "1");
StartActivity(activity2);
};
The main issue is that you are adding an int to the extras:
// item.Id is an int type
viewTask.PutExtra("TaskId", item.Id);
And then you are trying to get it as a string:
var test = Intent.GetStringExtra("TaskId");
There are two ways to get the value, without having to add the value as a string. Either get an int value:
var test = Intent.GetIntExtra("TaskId", 0);
if (test != 0)
{
// Do Stuff
}
Or, you can first check for the extra, if you don't want to rely on the default value:
if (Intent.HasExtra("TaskId"))
{
var test = Intent.GetIntExtra("TaskId", 0);
// Do Stuff
}
Is there any way to place multiple links in the LinkLabel control in Windows Forms?
If I just set it like this
this.linkLabel.Text = "";
foreach (string url in urls)
{
this.linkLabel.Text += url + Environment.NewLine;
}
it merges it into one link.
Thanks in advance.
Yes, though there isn't a way that I can tell to do it directly from the designer, but it is easy to manage via code:
var linkLabel = new LinkLabel();
linkLabel.Text = "(Link 1) and (Link 2)";
linkLabel.Links.Add(1, 6, "Link data 1");
linkLabel.Links.Add(14, 6, "Link data 2");
linkLabel.LinkClicked += (s, e) => Console.WriteLine(e.Link.LinkData);
Basically, the Links collection on the label can host a bunch of links in the LinkLabel. The LinkClicked event contains a reference to the specific link that was clicked so you can access the link data you associated with the link, among other things.
The designer only exposes a LinkArea property which defaults to include all of the text of the LinkLabel. The first Link you add to the Links collection will automatically change the LinkArea property to reflect the first link in the collection.
Something a little closer to what you're asking would look like this:
var addresses = new List<string> {
"http://www.example.com/page1",
"http://www.example.com/page2",
"http://www.example.com/page3",
};
var stringBuilder = new StringBuilder();
var links = new List<LinkLabel.Link>();
foreach (var address in addresses)
{
if (stringBuilder.Length > 0) stringBuilder.AppendLine();
// We cannot add the new LinkLabel.Link to the LinkLabel yet because
// there is no text in the label yet, so the label will complain about
// the link location being out of range. So we'll temporarily store
// the links in a collection and add them later.
links.Add(new LinkLabel.Link(stringBuilder.Length, address.Length, address));
stringBuilder.Append(address);
}
var linkLabel = new LinkLabel();
// We must set the text before we add the links.
linkLabel.Text = stringBuilder.ToString();
foreach (var link in links)
{
linkLabel.Links.Add(link);
}
linkLabel.AutoSize = true;
linkLabel.LinkClicked += (s, e) => {
System.Diagnostics.Process.Start((string)e.Link.LinkData);
};
I'm attaching the URL itself as the LinkData to the link's I'm creating in the loop so I can extract it out as a string when the LinkClicked event is fired.
I have a text with hyperlinks in it.
To not irritate the reader I want to have the normal text and the links on the same line. Right now every Inline element starts a new line.
It displays:
Please visit
http://google.com
to continue.
I want:
Please visit http://google.com to continue.
I've also noticed, that the hyperlink Hit area cover the hole inline element and not just the text.
My problem is identical than described and solved here:
Add clickable hyperlinks to a RichTextBox without new paragraph
The problem is, that it seems than something like a flowdocument for wp8 doesn't exist.
I need to create the inline elements programatically.
EDIT 1:
Here my code how I add the inline elements:
int index = 0;
rt = new RichTextBox() { };
while (true)
{
Paragraph para = new Paragraph();
if (item.text.Substring(index).IndexOf("<") == 0)
{
//TRUE when link
//I extract the URL and the linktext, and also update the index
Hyperlink hyper = new Hyperlink();
hyper.Click += new RoutedEventHandler((sender,e) => Hyperlink_Click(sender,e,URL));
hyper.Inlines.Add(linktext);
para.Inlines.Add(hyper);
}
else if (item.text.Substring(index).Contains("<"))
{
//TRUE when text, item.text contains a link
// I extract the text and update index
Run run = new Run() { Text = text };
para.Inlines.Add(run);
}
else
{
//TRUE when only text is left
Run run = new Run() { Text = item.text.Substring(index) };
para.Inlines.Add(run);
rt.Blocks.Add(para);
break;
}
// REMOVE: rt.Blocks.Add(para);
}
rt.SetValue(Grid.RowProperty, MainViewer.RowDefinitions.Count - 1);
MainViewer.Children.Add(rt);
EDIT 2
I still couldn't solve this Problem, does no one know a solution? I saw what I want in an App before, so it must be possible.
EDIT 3
I've created for every inline element a new paragraph. I've fixed my code above, it is working now
Paragraph p = new Paragraph();
p.Inlines.Add("Plase visit ");
var link = new Hyperlink();
link.Inlines.Add("google.com ");
p.Inlines.Add(link);
p.Inlines.Add("to continue");
rtb.Blocks.Add(p);
this works fine for me.
PS
If you want to show some html in you app, you can use HTMLTextBox or HTMLViewer from http://msptoolkit.codeplex.com/
I have TextBox which allow insert only numeric values (filtering), But when I paste copied text it's allow any kind of symbol. How can I prevent or filter text before pasting?
You could backup your text before any manual input and then when the input provided isn't valid restore the previous text like so:
_backupText = string.Empty;
doNotPasteTextBox.TextInputStart += (sender, e) =>
{
int textParsed;
if(int.TryParse(e.Text,out textParsed))
{
_backupText = doNotPasteTextBox.Text.Insert(doNotPasteTextBox.SelectionStart, e.Text);
}else
{
e.Handled = true;
}
};
doNotPasteTextBox.TextChanged += (sender, e) =>
{
int textParsed;
int selectionStart = doNotPasteTextBox.SelectionStart;
if(!int.TryParse(doNotPasteTextBox.Text, out textParsed))
{
doNotPasteTextBox.Text = _backupText;
}
doNotPasteTextBox.SelectionStart = selectionStart;
};
I wouldn't recommend trying to capture the control keys or anything because when you're on a mac or on linux then you're screwed.
Adjust my sample and pour it inside a new textbox control to make it cleaner but you get the idea.
You could use Clipboard.GetText() to get the text that is inserted into the textbox, but this will pop up a message, and the user must give the application access to the Clipboard.
If its no problem for you then i would use this.