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

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;

Related

Create Multiple Labels in Data Template

I have created a data template to use within a list view. This will later be expanded to add more content to each item in this list view. At the moment all the items that are bound to the observable collection are working as expected, except for one.
In each instance of the data template the bound properties are height, RouteName and routeStops. The height and RouteName are working fine but I'm not sure how to bind the routeStops correctly.
For each one of the RouteNames there are multiple stops, so for each data template use there must be one label that has the RouteName and multiple labels for each stop on the route (using routeStops).
I am not entirely sure how to achieve this, I can only seem to bind one stop to one label. I want to create them dynamically to allow for any amount of stops.
So the code behind that creates the data template (Just the constructor):
public MainRoutePageViewDetail(MessagDatabase database)
{
InitializeComponent();
BindingContext = mainroutepageviewmodel = new MainRoutePageViewModel(database,Navigation);
StackLayout mainstack = new StackLayout();
var routelisttemplate = new DataTemplate(() => {
ViewCell viewcell = new ViewCell();
stacklayout = new StackLayout();
stacklayout.SetBinding(StackLayout.HeightRequestProperty,"height");
viewcell.View = stacklayout;
// labels for template
var nameLabel = new Label { FontAttributes = FontAttributes.Bold, BackgroundColor = Color.LightGray };
nameLabel.HorizontalOptions = LayoutOptions.Center;
nameLabel.VerticalOptions = LayoutOptions.Center;
nameLabel.SetBinding(Label.TextProperty, "RouteName");
//inforLabel.SetBinding(Label.TextProperty, "Stops");
stacklayout.Children.Add(nameLabel);
StackLayout nextstack = new StackLayout();
var nameLabel2 = new Label { FontAttributes = FontAttributes.Bold, BackgroundColor = Color.Red };
nameLabel2.HorizontalOptions = LayoutOptions.Center;
nameLabel2.VerticalOptions = LayoutOptions.Center;
nameLabel2.SetBinding(Label.TextProperty, "routeStops");
nextstack.Children.Add(nameLabel2);
stacklayout.Children.Add(nextstack);
return viewcell;
});
ListView listviewofroutes = new ListView();
mainstack.Children.Add(listviewofroutes);
listviewofroutes.SetBinding(ListView.ItemsSourceProperty, "routeLabels");
listviewofroutes.ItemTemplate = routelisttemplate;
listviewofroutes.HasUnevenRows = true;
Content = mainstack;
}// end of constructor
This is bound to an ObservableCollection in the view model. Im going to leave this out as its irrelevant because the bindings work fine.
This calls down to functions in the model that collect data from SQL tables.
The function in the model that collects data:
public List<RouteInfo> getrouteInfo()
{
var DataBaseSelection = _connection.Query<RouteInfoTable>("Select * From [RouteInfoTable]");
List<RouteInfo> dataList = new List<RouteInfo>();
for (var i = 0; i < DataBaseSelection.Count; i++)
{
var DataBaseSelection2 = _connection.Query<RouteStopsTable>("Select StopOnRoute From [RouteStopsTable] WHERE RouteName = ? ",DataBaseSelection[i].RouteName);
dataList.Add(new RouteInfo
{
ID = DataBaseSelection[i].ID,
RouteName = DataBaseSelection[i].RouteName,
Stops = DataBaseSelection[i].Stops,
DayOf = DataBaseSelection[i].DayOf,
IsVisible = DataBaseSelection[i].IsVisible,
routeStops = DataBaseSelection2[i].StopOnRoute,
height = 200
});
}
return dataList;
}
The first table (RouteInfoTable) gets RouteName and some other information and the second table gets the stops on the route using the RouteName as a key. This is all added to a list of RouteInfo instances.
DataBaseSelection2 grabs all of the stops on the route but only one of them displays. I know why this is but I dont know how to display all three.
The Table definitions and class definitions as well as the selections from the tables are not an issue. I have debugged these and they are getting the correct information I just dont know how to display it on the front end in the way I want to. Here is a visual of what I mean if its getting complicated:
The best I can do is one route stop not all three.
An ideas how to achieve this?
Sorry if its complicated.
You can use Grouping in ListView to achieve this visual. Basically in this case you will be defining two DataTemplate(s) -
GroupHeaderTemplate for RouteName
ItemTemplate for StopsOnRoute.

Display data that is on the List Xamarin.Forms

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

Changing Xamarin Label in ListCell does not work

I have a problem with a ListView. I want each Cell to have a label and a switch but the text of the label does not appear.
Here is my code:
public class FilterPage : ContentPage
{
public FilterPage()
{
List<FilterCell> listContent = new List<FilterCell>();
foreach(string type in Database.RestaurantTypes)
{
FilterCell fc = new FilterCell();
fc.Text = type;
listContent.Add(fc);
}
ListView types = new ListView();
types.ItemTemplate = new DataTemplate(typeof(FilterCell));
types.ItemsSource = listContent;
var layout = new StackLayout();
layout.Children.Add(types);
Content = layout;
}
}
public class FilterCell : ViewCell
{
private Label label;
public Switch CellSwitch { get; private set; }
public String Text{ get { return label.Text; } set { label.Text = value; } }
public FilterCell()
{
label = new Label();
CellSwitch = new Switch();
var layout = new StackLayout
{
Padding = new Thickness(20, 0, 0, 0),
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = { label, CellSwitch }
};
View = layout;
}
}
If I enter a fixed Text in the FilterCell-Constructor it works fine (e.g.: label.Text = "Hello World")
When I create a Method for the ItemSelected-Event and read out the SelectedItem.Text Property I get the text I assigned as Value but it's never displayed. Only the switch is displayed when I try to run this Code.
Thanks for your help
Niko
Ohh boy. This code looks like a rape (sorry I had to say this).
Now let's see what's wrong:
The reason is you are mixing up data and view heavily.
The line
types.ItemTemplate = new DataTemplate(typeof(FilterCell));
means: "For each item in the list (ItemsSource) create a new filter cell". The FilterCells that you create in the loop are never displayed.
The easy fix
public class FilterPage : ContentPage
{
public FilterPage()
{
var restaurantTypes = new[] {"Pizza", "China", "German"}; // Database.RestaurantTypes
ListView types = new ListView();
types.ItemTemplate = new DataTemplate(() =>
{
var cell = new SwitchCell();
cell.SetBinding(SwitchCell.TextProperty, ".");
return cell;
});
types.ItemsSource = restaurantTypes;
Content = types;
}
}
There is a standard cell type that contains a label and a switch SwitchCell, use it.
As ItemsSource of your list, you have to use your data. In your case the list of restaurant types. I just mocked them with a static list.
The DataTemplate creates the SwitchCell and sets the Databinding for the Text property. This is the magic glue between View and data. The "." binds it to the data item itself. We use it, because our list contains items of strings and the Text should be exactly the string. (read about Databinding: https://developer.xamarin.com/guides/xamarin-forms/getting-started/introduction-to-xamarin-forms/#Data_Binding )
I striped away the StackLayout that contained the list. You can directly set the list as Content of the page.
Lesson
use standard controls, if possible
You should always try to remember to keep data and view apart from each other and use data binding to connect to each other.
Try to avoid unnecessary views.

Xamarin.forms SwitchCell Text Does not appear fully

I am newer with Xamarin.Forms. Is there a way to display all text an a SwitchCell? The long text get's cut off.
NewsLetterSwitchCell = new SwitchCell
{
Text="abcdefghijklmnopqrstvwxyz123456789aabbccdeeffgghhjjkkllmmmnnooppqqrrsssrtg",
};
NewsLetterSwitchCell.OnChanged += switcher_Toggled;
TableView tb = new TableView();
tb.Root = new TableRoot() {
new TableSection("Enter Optional Information (* Required Fields)") {
NewsLetterSwitchCell
}
};
Content = new StackLayout
{
Children = { tb }
};
This Picture shows the result from my mobile. It contains only the first letters.
You need to write your own custom renderer for this purpose .
Below link can help you .
Forum link for custom switch

How To select and object of label , If it is a child of Grid in C#

I have a program in which I have to change text of label (on the click of button) which is a child of a grid
public class XLabel
{
Grid uiGrid = null;
TextBlock textblock = null;
string emptyString = "";
Public void createLabel()
{
uiGrid.Children.Add(textblock);
grid.Children.Add(uiGrid);
}
public void cleartext()
{
textblock.Text = emptyString;
}
}
In other class I have a method to clear text
public void clearText()
{
XLabel obj = new XLabel();
obj.cleartext(indexi);
}
How to select specific label to clear text from specific grid if there are many grids and each having one label .
The Grid object has properties like Name or Tag, that can be used for searching.
If you create grids programmatically, you should create a unique property for each, then in your clearText method you just receive all Grid objects from XLabel object and search for the one with proper name/tag.
To get a list of labels from grid, you could use lambda like that:
List<UIElement> list =
YourGrid.Children.Where(o => o.GetType() == typeof(Label)).ToList();
To extend Olter's answer,
Create your Textblock and Grid like this
Grid uiGrid = new Grid() { Name = "uiGrid"+1 };
TextBlock textblock = new TextBlock() { Name = "textBlock"+1 };
Each time change the number you add to the grid and textblock and somehow plan to keeptrack of that number.
Then when you want to clear the text,
(this.FindName("textBlock"+1) as TextBlock).Text = "";

Categories