Display data that is on the List Xamarin.Forms - c#

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);
}

Related

Hyperlink in TextBlock is is not clickable

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);

How to add a TapGestureRecognizer with c# to created items from JSON?

so Im trying to Deserialize a JSON file and creat some items from it, so far everything is working well. My problem is that i want to add a TapGestureRecognizer to each item created and i don't know how to do so.
List<Categories> categorie = JsonConvert.DeserializeObject<List<Categories>>(categoriejson);
Device.BeginInvokeOnMainThread(() =>
{
foreach (Categories c in categorie)
{
Image image = new Image
{
Source = ImageSource.FromUri(new Uri(c.imageUrl))
};
image.HeightRequest = 105;
CategorieLayout.Children.Add(image);
so I just want to add a tap gesture to each created image. I was thinking about checking the x:name given by default to each image, but i failed to do that to ... any ideas are going to be much appreciated.
something like this should work - I'm doing this from memory so the syntax may not be perfect
Image image = new Image
{
Source = ImageSource.FromUri(new Uri(c.imageUrl))
};
var tap = new TappedGestureRecognizer();
tap.Tapped += TappedHandler;
image.GestureRecognizers.Add(tap);
If you are trying to deserialize a JSON file and creat some items from it,I suggest you use a ScrollView to wrap the content.It is likely that one page will not be displayed completely and scrolling is required.YOu can do like this:
public TestPage1()
{
StackLayout stackLayout = new StackLayout{};
ScrollView scroll = new ScrollView();
scroll.Content = stackLayout;
foreach (Categories c in categorie) {
var image = new Image
{
Source = ImageSource.FromUri(new Uri(c.imageUrl))
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand,
};
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.Tapped += Image_OnTapped;
image.ClassId = c.imageUrl;//attach data to image's `ClassId`
image.GestureRecognizers.Add(tapGestureRecognizer);
stackLayout.Children.Add(image);
}
Content = scroll;
}
Method Image_OnTapped
private void Image_OnTapped(object sender, EventArgs e)
{
// retrieve parameter from sender's ClassId
var parm = ((Image)sender).ClassId;
}
Note:
The sender of the Tapped event will be the control which the gesturerecognizer is attached to. in your case, an Image. You can attach your data to one of the Image's properties in order to access it from your event handler.
image.ClassId = c.imageUrl;//attach data to image's `ClassId`
And get value from method Image_OnTapped like this:
var parm = ((Image)sender).ClassId;

Inline Hyperlink navigate to Page

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));
}

justify text in label xamarin forms

Am trying to justify text in Label Xamarin Forms but couldnt , how to justify the text (not styling justify only) same like a picture without using webview
I have attached my code but its justify left only
string strText="MY LONG TEXT IS GOING HERE";
var lblMSG = new Label {TextColor = Color.Black };
lblMSG.Text=strText;
lblMSG.LineBreakMode = LineBreakMode.WordWrap;
lblMSG.HorizontalOptions = LayoutOptions.Fill;
lblMSG.HorizontalTextAlignment = TextAlignment.Start;
lblMSG.VerticalTextAlignment = TextAlignment.Center;
StackLayout stk= new StackLayout { Children = { lblMSG}, BackgroundColor = Color.White ,HorizontalOptions =LayoutOptions.FillAndExpand }
Instead of using a label. I used a HtmlWebViewSource.
An example is below:
XAML:
<StackLayout x:Name="webViewLayout"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<!-- view codebehind for WebView for overview text -->
</StackLayout>
Code Behind of XAML (viewModel.Model.Content = strText in your case):
var browser = new WebView();
browser.HorizontalOptions = LayoutOptions.FillAndExpand;
browser.VerticalOptions = LayoutOptions.FillAndExpand;
var source = new HtmlWebViewSource();
var text = "<html>" +
"<body style=\"text-align: justify;\">" +
String.Format("<p>{0}</p>", viewModel.Model.Content) +
"</body>" +
"</html>";
source.Html = text ;
browser.Source = source;
webViewLayout.Children.Add(browser);
If the question is about stretching the multi-line text to full width (text must touch left and right sides of the block), you can use platform renderer. See this reply.

Linking a collection of buttons to a collection of labels

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.

Categories