I have made this in code-behind :
var finishedText = new Run("Some text");
var finishedUrl = new Run("http://somewhere");
var hyperlink = new Hyperlink(finishedUrl) { NavigateUri = new Uri("http://somewhere") };
hyperlink.RequestNavigate += Hyperlink_RequestNavigate;
FinishedTextBlock.Inlines.Clear();
FinishedTextBlock.Inlines.Add(finishedText);
FinishedTextBlock.Inlines.Add(hyperlink);
FinishedTextBlock.Inlines.Add(Environment.NewLine);
FinishedTextBlock.Inlines.Add(finishedUrl);
In XAML :
<TextBlock Grid.Row="0"
x:Name="FinishedTextBlock"
Width="Auto"
Margin="10 10 0 0">
</TextBlock>
The text is not clickable.
What am I doing wrong?
The text is not clickable.
That is because your Hyperlink text is not even shown. You see the last Run that you have added to the Inlines collection, not the hyperlink itself.
You add the same Run named finishedUrl to your Hyperlink and its containing TextBlock, but you have to create a separate Run instance for the Hyperlink.
var finishedText = new Run("Some text");
var finishedUrl = "http://somewhere";
var finishedUrlRun = new Run(finishedUrl);
var hyperlink = new Hyperlink(finishedUrlRun) { NavigateUri = new Uri("http://somewhere") };
hyperlink.RequestNavigate += Hyperlink_RequestNavigate;
FinishedTextBlock.Inlines.Clear();
FinishedTextBlock.Inlines.Add(finishedText);
FinishedTextBlock.Inlines.Add(hyperlink);
FinishedTextBlock.Inlines.Add(Environment.NewLine);
var finishedUrlRun1 = new Run(finishedUrl);
FinishedTextBlock.Inlines.Add(finishedUrlRun1);
Even better, just do not add the last Run, as it is redundant, and replace the NewLine with a LineBreak to get the same layout as in your image but with a link.
FinishedTextBlock.Inlines.Clear();
FinishedTextBlock.Inlines.Add(finishedText);
FinishedTextBlock.Inlines.Add(new LineBreak());
FinishedTextBlock.Inlines.Add(hyperlink);
Unless your hyperlink is inside a webview then nothing will happen when you click on a hyperlink.
You need to handle the RequestNavigate event and do something before you'll see a web browser appear with a page in it.
If this was a wpf page inside a frame, or navigationwindow then it would also navigate without code.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.documents.hyperlink?view=netcore-3.1
Remarks
Hyperlink implements the NavigateUri property that you set
with the Uri of the content that should be navigated to when the
Hyperlink is clicked. Hyperlink navigation can only occur, however, if
either the direct or indirect parent of a Hyperlink is a navigation
host, including NavigationWindow, Frame, or any browser that can host
XBAPs (which includes Internet Explorer 6 and later versions, and
Firefox 2.0+). For more information, see the Navigation Hosts topic in
Navigation Overview.
As it is, you need an event handler.
I'm not clear whether you intend the url to appear or a string. If you want a different string then this should work:
TextBlock tb = new TextBlock();
tb.Inlines.Add(new Run {Text="Some text" } );
tb.Inlines.Add(new LineBreak());
Run linkRun = new Run("Link To Google");
Hyperlink hyper = new Hyperlink(linkRun) { NavigateUri = new Uri(#"http://www.google.com") };
hyper.RequestNavigate += (o, e) => Process.Start(new ProcessStartInfo(e.Uri.ToString()){ UseShellExecute = true });
tb.Inlines.Add(hyper);
If you want the url to appear rather than some other string then:
TextBlock tb = new TextBlock();
tb.Inlines.Add(new Run { Text = "Some text" });
tb.Inlines.Add(new LineBreak());
string url = #"http://www.google.com";
Run linkRun = new Run(url);
Hyperlink hyper = new Hyperlink(linkRun){ NavigateUri = new Uri(url) };
hyper.RequestNavigate += (o, e) => Process.Start(new ProcessStartInfo(e.Uri.ToString()) { UseShellExecute = true });
tb.Inlines.Add(hyper);
sp.Children.Add(tb);
Note
One the process start, you will often get an error on win 10 machines without that useshellexecute in the process startinfo.
In older OS ( and you would see in old SO answers ) you used to be able to just process start with a URL and it would work with the default browser.
There is a linebreak inline which is intended to add a linebreak between a series of runs in a textblock.
In XAML, it needs to be like this:
<TextBlock Grid.Row="0"
x:Name="FinishedTextBlock"
Width="Auto"
Margin="10 10 0 0">
<Run Text="Some text" />
<LineBreak/>
<Hyperlink>http://somewhere</Hyperlink>
</TextBlock>
The hyperlink in your code does not content anything. The hyperlink constructor parameter
[finishedUrl] did not initialized the hyperlink to contain the string "http://somewhere".
You can change your code something like below:
var finishedText = new Run("Some text");
var finishedUrl = new Run("http://somewhere");
var hyperlink = new Hyperlink() { NavigateUri = new Uri("http://somewhere") };
hyperlink.Inlines.Add(finishedUrl.Text);
hyperlink.RequestNavigate += Hyperlink_RequestNavigate;
FinishedTextBlock.Inlines.Clear();
FinishedTextBlock.Inlines.Add(finishedText);
FinishedTextBlock.Inlines.Add(Environment.NewLine);
FinishedTextBlock.Inlines.Add(hyperlink);
Related
I'm constructing text, and some pieces of the text should contain a hyperlink. However, these hyperlinks do not redirect to a webpage but should open a page in the UWP app (the currently running UWP app, not a new instance of it or a different app).
A HyperlinkButton can only open URL's that lead to an external browser, it can't open a page inside the app.
Using an InlineUIContainer doesn't work, I get
Exception thrown: 'System.ArgumentException' in mscorlib.ni.dll
Additional information: Value does not fall within the expected range.
With this code
List<Inline> ic = new List<Inline>();
InlineUIContainer container = new InlineUIContainer();
TextBlock tbClickable = new TextBlock();
tbClickable.Foreground = new SolidColorBrush(Colors.Red);
tbClickable.Text = label?.name;
tbClickable.Tag = label?.id;
tbClickable.Tapped += TbArtist_Tapped;
container.Child = tbClickable;
ic.Add(container);
When I use
foreach (var item in ic)
{
dTb.Inlines.Add(item);
}
Where tbCurrent is the TextBlock.
Any other ways to get a clickable link as an Inline element?
Best case scenario I can attach a Tapped/Click event handler.
But opening the page via a URI method or so is also good.
I changed to a RichTextBlock and using Blocks I could add a clickable TextBlock.
This works in UWP.
List<Block> ic = new List<Block>();
Paragraph para = new Paragraph();
InlineUIContainer iuic = new InlineUIContainer();
TextBlock hpb = new TextBlock();
hpb.Text = "link text";
hpb.Tag = "some tag to pass on to the click handler";
hpb.Tapped += ClickHandler;
hpb.TextDecorations = TextDecorations.Underline;
hpb.Foreground = new SolidColorBrush((Windows.UI.Color)page.Resources["SystemAccentColor"]);
iuic.Child = hpb;
para.Inlines.Add(iuic);
ic.Add(para);
There is no reason you could not use a HyperlinkButton for this. Quick example:
<HyperlinkButton Click="HyperlinkButton_Click" Content="Click me" />
And the Click handler:
private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(MainPage));
}
I want to display data that is on the List and also for the email. I have to Vice President and I want open link the of their email respectively to open to an external email platform. However, the code can't seems to work properly it opens only one email for the Vice President and the whole of the label containing it seems to react to it.
This is the screen shot of it.
My Xmal code
<Label Text="Vice-President:" FontSize="18" FontAttributes="Bold" XAlign="Center" TextColor="Black"/>
<StackLayout x:Name="vpDetails">
</StackLayout>
My C# Code
foreach (var item in leaderDetail)
{
Boolean IsPresident = item.IsPresident;
if (IsPresident == true)
{
lbPresidentName.Text = item.Name;
btnPresidentEmail.Text = item.Email;
var tgrPresident = new TapGestureRecognizer();
tgrPresident.Tapped += (s, e) => Device.OpenUri(new Uri("mailto:" + item.Email));
btnPresidentEmail.GestureRecognizers.Add(tgrPresident);
}
else
{
vpDetails.Children.Add(new Label { Text = item.Name ,FontSize=14, HorizontalOptions = LayoutOptions.Center});
vpDetails.Children.Add(new Label { Text = item.Email, FontSize=14, HorizontalOptions = LayoutOptions.Center, x:name="lbvPresident1Email"});
//lbvPresident1Name.Text = item.Name;
//lbvPresident1Email.Text = item.Email;
var tgrVPEmail = new TapGestureRecognizer();
tgrVPEmail.Tapped += (s, e) => Device.OpenUri(new Uri("mailto:" + item.Email));
lbvPresident1Email.GestureRecognizers.Add(tgrVPEmail);
}
}
This is because for multiple vice-presidents, you are just appending the text to the one label. This means that you first add one vice-president, and his Gesture recognizer with his e-mail and the append the text for the other and register another gesture recognizer. Then the tap gesture is recognized for the first one.
Basically what happens after the whole code runs is that the label lbvPresident1Name contains all vice-prezident names and e-mails and has multiple GestureRecognizers (one for each vice-prezident).
To overcome this issue, you should for example create a list of vice-presidents and add each vice-president as an item or add a container and create the labels for each of them on the fly.
To create a ListView in Xamarin.Forms see the documentation - https://developer.xamarin.com/samples/xamarin-forms/WorkingWithListview/
To create a container to which you can add items on the fly from the code, StackLayout component will be the best choice in your scenario. You will just put a StackLayout in place of the current label and then create Labels in the foreach loop and put them in the StackLayout using the Children.Add method.
To solve this problem you have to use the StackLayout as the container. Create a var of a Label and add into the foreach loop.
DetailedClub.xaml
<StackLayout x:Name="vpDetails"></StackLayout>
DetailedClub.xaml.cs
foreach (var item in leaderDetail)
{
//Create a new label for the Email
var VpEmail = new Label { Text = item.Email, FontSize = 14, HorizontalOptions = LayoutOptions.Center };
vpDetails.Children.Add(new Label { Text = item.Name ,FontSize=14, HorizontalOptions = LayoutOptions.Center});
vpDetails.Children.Add(VpEmail);
var tgrVPEmail = new TapGestureRecognizer();
tgrVPEmail.Tapped += (s, e) => Device.OpenUri(new Uri("mailto:" + item.Email));
VpEmail.GestureRecognizers.Add(tgrVPEmail);
}
I had a class with <toolkit:ExpanderView>-elements. Those are bound to a ViewModel and need to stay bound, because I'm already using a lot of checks like "Which VM-element is expanded and which not", etc.
The text inside of that expanded part was read from localized Strings (AppResources.resx) and added inside of the VM. But now I have to add hyperlinks to that text.
My idea was to throw away the localized strings for a while and create RichTextBoxes inside of the ViewModel. Instead of binding strings to the expanded part I would bind the RichTextBoxes.
But all methods I find suck. e.g.:
StackPanel tempGrid = new StackPanel();
StackPanel tempGrid = new StackPanel();
RichTextBox tempRtb = new RichTextBox();
Paragraph tempPara = new Paragraph();
Run tempRun = new Run();
Hyperlink tempHLink = new Hyperlink();
tempHLink.Click += Hyperlink_Click;
tempRun.Text = "line 1\nline 2\nline 3";
tempPara.Inlines.Add(tempRun);
tempRun.Text = "\nTel.: ";
tempPara.Inlines.Add(tempRun);
tempHLink.Inlines.Add("+01800 000 000 000");
tempPara.Inlines.Add(tempHLink);
tempRun.Text = "\nE-Mail: ";
tempPara.Inlines.Add(tempRun);
tempHLink.Inlines.Add("e-mail#email.com");
tempPara.Inlines.Add(tempHLink);
But it fails already when I reuse tempRun. Should I add thousands of new variables? ôo
Also the in the spec ( http://msdn.microsoft.com/en-us/library/system.windows.documents.run.aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1 ) it says that something like
Run run = new Run("string");
should work. But it doesn't with WP8 and turns to
Run run = new Run();
run.text = "string";
So everything becomes WAY longer... :(
Edit:
Yes, forgot to ask the actual question: Is there a faster way to fill a richtextbox or paragraph with lots of Run or Hyperlink-elements?
And how do I bind it in my xaml?
I need something like:
<DataTemplate>
<RichTextBox Content="{Binding MyModelViewParagraph}"/>
</DataTemplate>
to bind my paragraph from the ViewModel to the xaml.
Okay, googling further lead me to this:
ViewModel:
StackPanel tempStack = new StackPanel();
RichTextBox tempRtb = new RichTextBox();
Paragraph tempPara = new Paragraph();
//tempHLink.Click += Hyperlink_Click;
tempPara.Inlines.Add(new Run { Text = "text 1\ntext 2\n text 3" });
tempPara.Inlines.Add(new Run { Text = "\nTel.: " });
//tempInline.Add(new Hyperlink { new InlineCollection { = } }) // "+000 (0) 00 00 00-0"
tempPara.Inlines.Add(new Run { Text = "\nE-Mail: " });
//tempInline.Add(new Hyperlink { new InlineCollection { = } }) // e-mail#email.com
tempRtb.Blocks.Add(tempPara);
tempStack.Children.Add(tempRtb);
Still need to find out how exactly I give Text and Click-Event to every HyperLink but that'll be managable I guess..
xaml:
<DataTemplate>
<Grid>
<ContentPresenter Content="{Binding Content}"/>
</Grid>
</DataTemplate>
That binds my Stackpanel so I can add as many elements as I need.
Sorry for answering it by myself. Sometimes you seem to be stuck and it's better to ask then lose 2-3 more days on something that might be simple.. And sometimes asking the question leads you to a solution. In my case it was just mroe stupid googling and reading the MS-specification xD
Edit:
If someone ants to know the whole solutionf ro the Hyperlinks as well - I found out :)
tempPara.Inlines.Add(CreateHyperlinkWithContent("+00 (00) 00 00 00-0"));
With the method:
private Hyperlink CreateHyperlinkWithContent(string content)
{
Hyperlink tempHyperLink = new Hyperlink();
tempHyperLink.Inlines.Add(content);
tempHyperLink.Click += Hyperlink_Click;
tempHyperLink.Foreground = new SolidColorBrush(Color.FromArgb(0xff, 0x33, 0x66, 0x99));
tempHyperLink.MouseOverForeground = new SolidColorBrush(Color.FromArgb(0xff, 0x33, 0x66, 0x99));
return tempHyperLink;
}
This is the shortest way I could find to add Text and Links in a single Line everytime ;)
I have a HyperLink control with text in its Text property.
With the following code:
var link = new HyperLink();
var img = new HtmlGenericControl("img");
img.Attributes.Add("src", "text.png");
link.Text = "Test";
link.Controls.Add(img);
When I do this, the image is rendered inside a a tag, but the text is not rendred.
Is there a way to render both the image and the text inside the Text property without throwing a third control in to the mix?
When you put any controls into the WebControl.Controls collection, it will ignore what you have inside Text. So if you want to render both text and other child controls, you should add the text into Controls:
var link = new HyperLink();
var img = new HtmlGenericControl("img");
img.Attributes.Add("src", "text.png");
link.Controls.Add(new Literal{ Text = "Test"}); // this line will add the text
link.Controls.Add(img);
I feel this should work out for you.
var link = new HyperLink();
var img = new HtmlGenericControl("img");
var lbl = new Label();
img.Attributes.Add("src", "text.png");
lbl.Text = "Test";
link.Controls.Add(img);
link.Controls.Add(lbl);
this.Controls.Add(link);
According to the MSDN article "The HyperLink control can be displayed as text or an image." So the answer is no, I'm afraid.
I am reading in an XML file and using it to populate a WPF Form. Each entry in the XML file is used to create a button and a label.
The problem I am having is associating the button with the label:
Here is my code:
// Loop through Backups
foreach (var back in backups)
{
// Create Wrap Panel
myWrapPanel = new WrapPanel();
//myWrapPanel.Background = System.Windows.Media.Brushes.Red;
myWrapPanel.Orientation = System.Windows.Controls.Orientation.Horizontal;
myWrapPanel.Width = 600;
myWrapPanel.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
myWrapPanel.VerticalAlignment = System.Windows.VerticalAlignment.Top;
// **************************************
// Add Controls to Wrap Panel
// **************************************
// Backup Source
System.Windows.Controls.Button btnBackupSource = new System.Windows.Controls.Button();
btnBackupSource.Content = "Source";
btnBackupSource.Height = 25;
btnBackupSource.Width = 75;
btnBackupSource.Click += btnBackupSource_Click;
btnBackupSource.Name = "btnBackupSource";
myWrapPanel.Children.Add(btnBackupSource);
System.Windows.Controls.Label lblBackupSource = new System.Windows.Controls.Label();
lblBackupSource.Height = 25;
lblBackupSource.Width = 461;
lblBackupSource.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
lblBackupSource.VerticalAlignment = System.Windows.VerticalAlignment.Top;
lblBackupSource.Name = "lblBackupSource";
lblBackupSource.Content = "";
myWrapPanel.Children.Add(lblBackupSource);
The button and labels are created OK but I am struggling to associate them with each other. For example when a button is pressed the result needs to come up in the individual label which is associated with the button.
Any suggestions on how to proceed would be very welcome!
What I have currently got is each button updating a single label:
private void btnBackupSource_Click(object sender, RoutedEventArgs e)
{
SourceFolderBrowser = new System.Windows.Forms.FolderBrowserDialog();
// Allow users to creating new folders and default to the my documents folder.
SourceFolderBrowser.ShowNewFolderButton = true;
SourceFolderBrowser.RootFolder = Environment.SpecialFolder.Personal;
SourceFolderBrowser.ShowDialog();
label2.Content = SourceFolderBrowser.SelectedPath;
}
However what I am trying to do is get each button to update its associated label.
Here is my suggestion for you:
Store the label you want to associate with the button in the button's Tag property like this:
btnBackupSource.Tag = lblBackupSource;
Later when you to do something in the wired up btnBackupSource_Click you can do it like this:
var button = sender as Button;
var label = button.Tag as Label;
label.Text = "Hello";
This would be much easier by creating a DataTemplate and setting the ListBox.ItemTemplate to use the DataTemplate.
That way you do not have to create all the controls in code and need to wire up the button and label only once (preferably by databinding).
<Window.Resources>
<DataTemplate x:Key="backupView">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding BackupName}"/>
<Button Content="Source" Command="{Binding DoSomethingCommand}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding}" ItemTemplate="{StaticResource backupView}"/>
In code you would need to create a class (Backup?) with the property Name and a DoSomethingCommand. This command would modify the Name of the Backup and that would automatically being passed on to the view.