I have tried to make a page in my app where all controls are generated dynamically via C# code behind. I am using a Nuget Packages, DLToolkit, flowlist to create a flow list.
I have already used this package in my project before using Xaml, and it fully works.
However when I try to create a datatemplate in code behind, it just displays a blank control, however when hovering above this control, you can see there's actually items in it.
My question is: How can I make a datatemplate with databindings in code behind?
Here is an example and works in Xaml:
<flv:FlowListView x:Name="flvList" BackgroundColor="White" FlowColumnCount="3" FlowItemsSource="{Binding LstItemSource}" HasUnevenRows="True">
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
<StackLayout BackgroundColor="White" Padding="2" HorizontalOptions="FillAndExpand">
<Frame Margin="20" Padding="0" HeightRequest="175" OutlineColor="Gray" BackgroundColor="White" CornerRadius="10" HasShadow="True" IsClippedToBounds="True">
<Frame.Content>
<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" >
<Image Aspect="AspectFill" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="All" Source="{Binding BgImage}" />
<BoxView Color="Black" Opacity=".5" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="All"/>
<StackLayout Margin="20" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
<Label Text="{Binding SubTitle}" FontSize="Medium" TextColor="#66FFFFFF"/>
<Label Text="{ Binding Title}" FontSize="Large" TextColor="White" />
</StackLayout>
</AbsoluteLayout>
</Frame.Content>
</Frame>
</StackLayout>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
However, in this project the controls are generated, so there is no Xaml code involved.
This is an example of the code that I've tried in code behind, but doesn't work:
#region Datatemplate
var dataTemplate = new DataTemplate(() =>
{
var StackLayout = new StackLayout { BackgroundColor = Color.Pink, Padding = 2, HorizontalOptions = LayoutOptions.FillAndExpand };
#region children/content for frame
AbsoluteLayout absoluteLayout = new AbsoluteLayout { HorizontalOptions = LayoutOptions.FillAndExpand, VerticalOptions = LayoutOptions.FillAndExpand };
#region content for AbsoluteLayout
var imgBg = new Image();
AbsoluteLayout.SetLayoutBounds(imgBg , new Rectangle(1, 1, 1, 1));
AbsoluteLayout.SetLayoutFlags(imgBg , AbsoluteLayoutFlags.All);
imgBg .SetBinding(Image.SourceProperty, "BgImage");
absoluteLayout.Children.Add(imgBg );
var overlayBox = new BoxView { Color = Color.Black, Opacity = 0.5 };
AbsoluteLayout.SetLayoutBounds(overlayBox, new Rectangle(1, 1, 1, 1));
AbsoluteLayout.SetLayoutFlags(overlayBox, AbsoluteLayoutFlags.All);
absoluteLayout.Children.Add(overlayBox);
#region InnerStackpanel
StackLayout innerStackVoorAbsoluteLayout = new StackLayout { Margin = new Thickness(20), VerticalOptions = LayoutOptions.CenterAndExpand, HorizontalOptions = LayoutOptions.CenterAndExpand };
var lblTitel = new Label { FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)), TextColor = Color.White };
var lblSubTitel = new Label { FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)), TextColor = Color.White };
//Bindings
lblTitel.SetBinding(Label.TextProperty, "Title");
lblSubTitel.SetBinding(Label.TextProperty, "SubTitle");
innerStackVoorAbsoluteLayout.Children.Add(lblSubTitel);
innerStackVoorAbsoluteLayout.Children.Add(lblTitel);
absoluteLayout.Children.Add(innerStackVoorAbsoluteLayout);
#endregion
#endregion
#endregion
Frame frame = new Frame();
frame.Content = absoluteLayout;
StackLayout.Children.Add(frame);
return StackLayout;
});
#endregion
FlowListView lstRelatieLijst = new FlowListView();
lstRelatieLijst.ItemsSource = lstRelatieItems;
lstRelatieLijst.FlowColumnTemplate = dataTemplate;
lstRelatieLijst.BackgroundColor = Color.LightGoldenrodYellow;
lstRelatieLijst.FlowColumnCount = 1;
lstRelatieLijst.HasUnevenRows = true;
#endregion
Can someone give me some advice how I can become something similar like the upper Xaml in code behind, please?
I already tried the following sources but unfortunately I doesn't work as expected. I hoped to see the same result or something similar like the XAML code. But after following their info,the FLowListView appears to be empty:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/data-templates/creating
https://www.codeproject.com/Questions/516614/createplusdatatemplatepluscodeplusbehind
You should use
flowList.SetBinding(FlowListView.FlowItemsSourceProperty, "List"); instead of ItemsSource
Here is the working sample....
using DLToolkit.Forms.Controls;
using System;
using Xamarin.Forms;
namespace FlowListTest
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
LoadUI();
BindingContext = new BContext();
}
private void LoadUI()
{
var dataTemplate = new DataTemplate(() =>
{
var image = new Image();
image.SetBinding(Image.SourceProperty, "BgImage");
var titleLabel = new Label
{
FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)),
TextColor = Color.White,
};
titleLabel.SetBinding(Label.TextProperty, "Title");
var subTitleLabel = new Label
{
FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)),
TextColor = Color.White,
};
subTitleLabel.SetBinding(Label.TextProperty, "Subtitle");
return new StackLayout
{
BackgroundColor = Color.Pink,
Padding = 2,
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = {
new Frame {
Content = new AbsoluteLayout {
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
Children = {
image,
new StackLayout {
Margin = new Thickness(20),
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
Children = {
titleLabel,
subTitleLabel
}
}
}
}
}
}
};
});
var flowList = new FlowListView();
flowList.SetBinding(FlowListView.FlowItemsSourceProperty, "List");
flowList.FlowColumnTemplate = dataTemplate;
flowList.BackgroundColor = Color.LightGoldenrodYellow;
flowList.FlowColumnCount = 1;
flowList.HasUnevenRows = true;
var button = new Button { Text = "Add" };
button.Clicked += Button_Clicked
;
Content = new StackLayout
{
Children = {
button,
flowList
}
};
}
private void Button_Clicked(object sender, EventArgs e)
{
(BindingContext as BContext).Add();
}
}
public class Foo
{
public string BgImage { get; set; }
public string Title { get; set; }
public string Subtitle { get; set; }
}
public class BContext
{
public FlowObservableCollection<Foo> List { get; set; }
public BContext()
{
List = new FlowObservableCollection<Foo>
{
new Foo {
BgImage = "http://via.placeholder.com/350x150",
Title = "Title",
Subtitle = "SubTitle"
},
new Foo {
BgImage = "http://via.placeholder.com/350x150",
Title = "Title1",
Subtitle = "SubTitle1"
}
};
}
public void Add()
{
List.Add(new Foo
{
BgImage = "http://via.placeholder.com/350x150",
Title = "Title" + List.Count,
Subtitle = "SubTitle" + List.Count
});
}
}
}
Related
I've written a Xamarin App in C# that registers barcode scans. The picker gathers articles, scans them and put them into boxes for the customer.
In the app the picker has the possibility to view all articles per box. All he has to do is toggle a switch on the page. Dynamically a grid with articles is created per box and added to the UI. This loading takes a lot of time, it often times out or is not complete. I tried to make the UI more responsive by using async/await, Tasks and Device.BeginInvokeOnMainThread. What I would like to see is that the boxes are faster loaded and displayed 1 by 1 on the UI instead of all at the end. Can anyone help me with this?
The XAML:
<ContentPage Title="Box" x:Name="cp2">
<StackLayout Orientation="Vertical" HeightRequest="20">
<StackLayout Orientation="Horizontal" Margin="0" Spacing="0">
<Label VerticalOptions="Center" Text="Boxnr:"/>
<Entry x:Name="txtBoxNr" VerticalOptions="Center" HorizontalOptions="StartAndExpand" WidthRequest="200"
Text="{Binding CurrBoxNr}" FontSize="Small" IsEnabled="{Binding BoxNrEnabled}" Completed="txtBoxNr_Completed"/>
</StackLayout>
<StackLayout x:Name="txtOverviewTitle" Orientation="Vertical" Margin="0,10,0,0" Spacing="0" IsVisible="False">
<StackLayout Orientation="Horizontal" Margin="0,0,0,0" Spacing="0" BackgroundColor="Maroon">
<Label VerticalOptions="Start" HorizontalOptions="FillAndExpand"
Text="Scan overview" FontSize="22" TextColor="White" HorizontalTextAlignment="Center"/>
<Switch x:Name="swOverview" VerticalOptions="Center" Toggled="swOverview_Toggled" IsToggled="False"/>
</StackLayout>
<StackLayout x:Name="Loader" Orientation="Vertical" IsVisible="false">
<Label x:Name="message1" Text="Loading overview. Please wait..." TextColor="Maroon" IsVisible="false"/>
<Label x:Name="message2" Text="Closing overview. Please wait..." TextColor="Maroon" IsVisible="false"/>
<ActivityIndicator x:Name="ailoader" Color="Maroon" IsRunning="false"/>
</StackLayout>
<Label x:Name="txtUsedBoxes" Margin="10,10,10,5" VerticalOptions="Start" Text="" IsVisible="false"/>
<BoxView x:Name="bvLine" HeightRequest="1" HorizontalOptions="FillAndExpand" VerticalOptions="End" BackgroundColor="Maroon" IsVisible="false"/>
</StackLayout>
<ScrollView x:Name="scrloverview">
<StackLayout x:Name="Overview" Orientation="Vertical" IsVisible="false"/>
</ScrollView>
</StackLayout>
</ContentPage>
XAML.cs:
private void swOverview_Toggled(object sender, ToggledEventArgs e)
{
if (e.Value == true)
GetOverview();
else
RemoveOverview();
}
private async void GetOverview()
{
message1.IsVisible = true;
ailoader.IsRunning = true;
Loader.IsVisible = true;
#region 1. Clear all children under the StackLayout
Overview.Children.Clear();
#endregion
#region 2. Display the overview title and the used boxes (without box 0)
IEnumerable<decimal> boxes = viewModel.Scanlist
.Where(c => c.Box > 0)
.OrderBy(c => c.Box)
.Select(c => c.Box).Distinct();
string strboxes = "Used Boxes: ";
for (int i = 0; i < boxes.Count(); i++)
{
if (boxes.ElementAt(i) > 0)
{
strboxes += (boxes.ElementAt(i)).ToString("N0");
if (i < boxes.Count() - 1)
strboxes += ", ";
}
}
txtUsedBoxes.Text = strboxes;
#endregion
#region 3. Insert the overview of the scanned articles per box
foreach (decimal box in boxes)
{
await Task.Run(async () =>
{
try
{
Device.BeginInvokeOnMainThread(() => AddGridBox(box));
}
catch (Exception exp)
{
await DisplayAlert("Error", exp.Message, "OK");
}
});
}
#endregion
#region 4. Insert the overview of the unscanned articles in box 0
await Task.Run(async () =>
{
try
{
Device.BeginInvokeOnMainThread(() => AddGrid0());
}
catch (Exception exp)
{
await DisplayAlert("Error", exp.Message, "OK");
}
});
Loader.IsVisible = false;
message1.IsVisible = false;
ailoader.IsRunning = false;
#endregion
txtUsedBoxes.IsVisible = true;
bvLine.IsVisible = true;
Overview.IsVisible = true;
}
private async void RemoveOverview()
{
Loader.IsVisible = true;
message2.IsVisible = true;
ailoader.IsRunning = true;
Overview.IsVisible = false;
txtUsedBoxes.Text = "";
txtUsedBoxes.IsVisible = false;
bvLine.IsVisible = false;
await Task.Run(async () =>
{
try
{
Device.BeginInvokeOnMainThread(() => {
Overview.Children.Clear();
});
}
catch (Exception exp)
{
await DisplayAlert("Error", exp.Message, "OK");
}
});
message2.IsVisible = false;
ailoader.IsRunning = false;
Loader.IsVisible = false;
}
private void AddGrid0()
{
IEnumerable<Scanning> selection0 = viewModel.Scanlist
.Where(c => c.Box == 0)
.OrderBy(c => c.Article);
if (selection0 != null && selection0.Count() > 0)
{
decimal scanneditems0 = selection0.Sum(e => e.Scanned);
decimal quantity0 = selection0.Sum(e => e.Quantity);
Label boxnr0 = new Label
{
Margin = new Thickness(10, 10, 0, 0),
FontAttributes = FontAttributes.Bold,
Text = "Unscanned articles : Quantity: " + quantity0.ToString("N0")
};
Grid grdbox0 = new Grid();
AddHeaders(grdbox0, selection0.Count() + 2, true);
AddContent(grdbox0, selection0, true);
Overview.Children.Add(boxnr0);
Overview.Children.Add(grdbox0);
}
}
private void AddGridBox(decimal box)
{
IEnumerable<Scanning> selection = viewModel.Scanlist
.Where(c => c.Box == box)
.OrderBy(c => c.Article);
decimal scanneditems = selection.Sum(e => e.Scanned);
decimal quantity = selection.Sum(e => e.Quantity);
Label boxnr = new Label
{
Margin = new Thickness(10, 0, 0, 0),
FontAttributes = FontAttributes.Bold,
Text = "Box " + box.ToString("N0") + ": Scanned: " + scanneditems.ToString("N0")
};
Grid grdbox = new Grid();
AddHeaders(grdbox, selection.Count() + 2, false);
AddContent(grdbox, selection, false);
Overview.Children.Add(boxnr);
Overview.Children.Add(grdbox);
}
private void AddHeaders(Grid grid, int rowcount, bool notscanned)
{
#region 1. Set grid margins and spacing
grid.Margin = new Thickness(30, 0, 30, 20);
grid.ColumnSpacing = 10;
grid.RowSpacing = 0;
grid.BackgroundColor = Color.BlanchedAlmond;
#endregion
#region 2. Set row & column definitions
//Row definitions
RowDefinitionCollection rdrow = new RowDefinitionCollection();
for (int i = 0; i < rowcount; i++)
{
RowDefinition rowdef = new RowDefinition();
rowdef.Height = (i == 1 ? new GridLength(1) : new GridLength(20)); // GridLength.Auto;
rdrow.Add(rowdef);
}
grid.RowDefinitions = rdrow;
//Column definitions
ColumnDefinitionCollection rdcol = new ColumnDefinitionCollection();
AddColumnDefinition(rdcol, new GridLength(60)); //location
AddColumnDefinition(rdcol, new GridLength(70)); //article
AddColumnDefinition(rdcol, GridLength.Star); //description
AddColumnDefinition(rdcol, new GridLength(35)); //quantity
if (!notscanned)
AddColumnDefinition(rdcol, new GridLength(35)); //scanned
grid.ColumnDefinitions = rdcol;
#endregion
#region 3. Add grid header
//Add the labels to the header
AddHeaderLabelToGrid(grid, 0, 0, "Location", 12, TextAlignment.End, FontAttributes.Bold);
AddHeaderLabelToGrid(grid, 1, 0, "Article", 12, TextAlignment.Start, FontAttributes.Bold);
AddHeaderLabelToGrid(grid, 2, 0, "Description", 12, TextAlignment.Start, FontAttributes.Bold);
if (!notscanned)
{
AddHeaderLabelToGrid(grid, 3, 0, "Qty", 12, TextAlignment.End, FontAttributes.Bold);
AddHeaderLabelToGrid(grid, 4, 0, "Scn", 12, TextAlignment.End, FontAttributes.Bold, new Thickness(0, 0, 10, 0));
}
else
AddHeaderLabelToGrid(grid, 3, 0, "Qty", 12, TextAlignment.End, FontAttributes.Bold, new Thickness(0, 0, 10, 0));
//Add the line to the header
BoxView bx = new BoxView
{
HeightRequest = 1,
Color = Color.Black,
VerticalOptions = LayoutOptions.End,
HorizontalOptions = LayoutOptions.FillAndExpand
};
grid.Children.Add(bx, 0, 1);
if (notscanned)
Grid.SetColumnSpan((BindableObject)bx, 4);
else
Grid.SetColumnSpan((BindableObject)bx, 5);
#endregion
}
private void AddColumnDefinition(ColumnDefinitionCollection rdcol, GridLength width)
{
ColumnDefinition coldef = new ColumnDefinition();
coldef.Width = width;
rdcol.Add(coldef);
}
private void AddContent(Grid grid, IEnumerable<Scanning> selection, bool notscanned)
{
for (int i = 0; i < selection.Count(); i++)
{
Scanning scn = selection.ElementAt(i);
#region 2. Add the data to the different columns
AddLabelToGrid(grid, 0, i + 2, scn.Location, 12, TextAlignment.End, notscanned, Color.Black);
AddLabelToGrid(grid, 1, i + 2, scn.Article, 12, TextAlignment.Start, notscanned, Color.Black);
AddLabelToGrid(grid, 2, i + 2, scn.Description, 12, TextAlignment.Start, notscanned, Color.Black);
if (!notscanned)
{
AddLabelToGrid(grid, 3, i + 2, scn.Quantity.ToString("N0"), 12, TextAlignment.End, notscanned, Color.Black);
AddLabelToGrid(grid, 4, i + 2, scn.Scanned.ToString("N0"), 12, TextAlignment.End, notscanned, Color.Black, new Thickness(0, 0, 10, 0));
}
else
{
AddLabelToGrid(grid, 3, i + 2, scn.Quantity.ToString("N0"), 12, TextAlignment.End, notscanned, Color.Black, new Thickness(0, 0, 10, 0));
}
#endregion
}
}
private void AddHeaderLabelToGrid(Grid grid, int column, int row, string text, int fontsize, TextAlignment alignment, FontAttributes fontattributes, Thickness? margin = null)
{
Label lbl = new Label
{
Text = text,
FontSize = fontsize,
FontAttributes = fontattributes,
HorizontalTextAlignment = alignment
};
if (margin != null)
lbl.Margin = (Thickness)margin;
grid.Children.Add(lbl, column, row);
}
private void AddLabelToGrid(Grid grid, int column, int row, string text, int fontsize, TextAlignment alignment, bool notscanned, Color textcolor, Thickness? margin = null)
{
Label lbl = new Label
{
Text = text,
FontSize = fontsize,
HorizontalTextAlignment = alignment
};
if (!notscanned)
lbl.TextColor = textcolor;
if (margin != null)
lbl.Margin = (Thickness)margin;
grid.Children.Add(lbl, column, row);
}
Scanlist = Collection of Scanning objects
private ObservableCollection<Scanning> _scanlist { get; set; }
public ObservableCollection<Scanning> Scanlist
{
get
{
return _scanlist;
}
set
{
_scanlist = value;
OnPropertyChanged("Scanlist");
}
}
The Model:
using Newtonsoft.Json;
using System.ComponentModel;
using System.Runtime.CompilerServices;
/* *********************************************************************************************
* Class: Scanning
* ----------------------
* These classes are used to hold the pickinglist data of this application.
* Scanning is the Model in the MVVM and implements the INotifyPropertyChanged interface.
***********************************************************************************************/
namespace BarcodeScanning
{
public class Scanning : INotifyPropertyChanged
{
private decimal _scanned;
//private decimal _bo;
private decimal _box;
private bool _showdelete;
private bool _showedit = false;
private bool _showchangebox;
private bool _containsbo;
private bool _showmanco;
[JsonProperty("pickexped")]
public string Pickexped { get; set; }
[JsonProperty("pickngn")]
public decimal Pickngn { get; set; }
[JsonProperty("orderdoc")]
public string Orderdoc { get; set; }
[JsonProperty("location")]
public string Location { get; set; }
[JsonProperty("article")]
public string Article { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("ean")]
public string EAN { get; set; }
[JsonProperty("quantity")]
public decimal Quantity { get; set; }
[JsonProperty("scanned")]
public decimal Scanned {
get { return _scanned; }
set
{
_scanned = value;
OnPropertyChanged("Scanned");
}
}
[JsonProperty("box")]
public decimal Box {
get { return _box; }
set
{
_box = value;
OnPropertyChanged("Box");
}
}
[JsonProperty("ster")]
public string Ster { get; set; }
public bool ShowEdit
{
get { return _showedit; }
set
{
_showedit = value;
OnPropertyChanged("ShowEdit");
}
}
public bool ShowDelete
{
get { return _showdelete; }
set
{
_showdelete = value;
OnPropertyChanged("ShowDelete");
}
}
public bool ShowChangeBox
{
get { return _showchangebox; }
set
{
_showchangebox = value;
OnPropertyChanged("ShowChangeBox");
}
}
public bool ShowManco
{
get { return _showmanco; }
set
{
_showmanco = value;
OnPropertyChanged("ShowManco");
}
}
[JsonProperty("containsbo")]
public bool ContainsBo
{
get { return _containsbo; }
set
{
_containsbo = value;
OnPropertyChanged("ContainsBo");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I suspect that Xamarin doesn't begin display update immediately, because it tries to optimize the case where several UI changes are made at once; it prefers to do a display update with all those changes together, to save time.
You can force a layout cycle:
Device.BeginInvokeOnMainThread(() =>
{
AddGrid0();
// Overview is the x:Name of layout to which children are being added.
Overview.ForceLayout();
});
CAVEAT: If you display the boxes one at a time, the total time taken will likely increase. Consider only forcing every 3rd box or so.
Without using XAML does anyone have an example where they create a ControlTemplate for an element?
Example:
<ControlTemplate>
<Grid>
<//Inner grid elements>
</Grid>
</ControlTemplate>
to C# code
var grid = new Grid();
//This isnt how you do it Im stuck here
var ControlTemplate = new ControlTemplate(grid)
You could refer to the code below.
public class Page1 : ContentPage
{
public Page1()
{
ControlTemplate controlTemplate = new ControlTemplate(typeof(Grid1));
ControlTemplate = controlTemplate;
}
}
public class Grid1 : ContentView
{
public Grid1()
{
Grid grid = new Grid()
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
RowDefinitions =
{
new RowDefinition{Height =GridLength.Auto},
new RowDefinition{Height =GridLength.Auto},
},
ColumnDefinitions =
{
new ColumnDefinition{ Width= GridLength.Auto},
new ColumnDefinition{ Width= GridLength.Auto},
},
};
grid.Children.Add(new Label
{
Text = "column0,row0",
}, 0, 0);
grid.Children.Add(new Label
{
Text = "column1,row1"
}, 1, 1);
Content = grid;
}
}
Gently, how can I achieve this in xaml? It must be pretty damn easy but can't wrap my mind around it :/
This is what I'm trying to achieve :
This is my view.xaml
<StackLayout x:Name="stackExample">
<StackLayout Margin="10,10,10,5" >
<Label Text="Info" />
<BoxView Color="#29abe2" WidthRequest ="100" HeightRequest="1" />
</StackLayout>
<StackLayout x:Name="optionsContentStack" >
</StackLayout>
</StackLayout>
this is my view.xaml.cs
foreach (var ex in example)
{
var labelHtmlInst = new Label { Text = ex.Info, FontSize = 15, TextColor = Color.FromHex("#222222"), HorizontalOptions = LayoutOptions.Center, VerticalOptions=LayoutOptions.Center, HorizontalTextAlignment = TextAlignment.Start };
optionsContentStack.Children.Add(labelHtmlInst);
var labelDs = new Label { Text = ex.Info, FontSize = 10, TextColor = Color.FromHex("#999999"), HorizontalOptions=LayoutOptions.Start, HorizontalTextAlignment = TextAlignment.Start, Margin = new Thickness(0, 0, 0, 10) };
optionsContentStack.Children.Add(labelDs);
var boxviewDs = new BoxView { Color = Color.FromHex("#f7931e"), WidthRequest = 100, HeightRequest = 1, HorizontalOptions = LayoutOptions.Start };
optionsContentStack.Children.Add(boxviewDs);
}
I want to create a listview in Xamarin portable,
the item source of the listview is List<String> and my datatemplate of the listview is prepared by this function,
private DataTemplate createItemtemplate()
{
try
{
Label lbl_binding = new Label()
{
TextColor = Color.Red,
FontSize = 16,
VerticalTextAlignment = TextAlignment.Center,
HorizontalOptions = LayoutOptions.CenterAndExpand,
};
lbl_binding.SetBinding(Label.TextProperty, ".");
StackLayout stkBottom = new StackLayout()
{
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.CenterAndExpand,
VerticalOptions = LayoutOptions.Center,
Padding = new Thickness(0),
};
stkBottom.Children.Add(lbl_binding);
ViewCell vc = new ViewCell() {
View = stkBottom
};
DataTemplate Dp = new DataTemplate(() =>
{
return vc;
});
return Dp;
}
catch (Exception ex)
{
return null;
}
}
Now, my list view is populated, but all the labels are filled with last item, I mean the no of items are rightly populated, but all the items are filled with the last item only.
what i am doing wrong here?
lstAdmin = new ListView()
{
ItemTemplate = createItemtemplate(),
};
lstadmin.Itemsource = source;
Change your listview from List to ObservableCollection. This should do the work.
Below code useful for you as reference
Take Class members:
public class Dummy
{
public string Id { get; set; }
public string Img { get; set; }
public string Title { get; set; }
public string SubTitle { get; set; }
public string Count { get; set; }
public string Status { get; set; }
}
Create One ObservableCollectionList:
ObservableCollection<Dummy> productItems = new
ObservableCollection<Dummy>();
Add Items to the ObservableCollectionList:
productItems.Add(new Dummy() { Name = "0", Img = "Avatar.png",
Title = "Total Books", SubTitle = "Desc", Count = "50", Status =
"Total" });
productItems.Add(new Dummy() { Id = "1", Img = "Avatar.png", Title
= "Out of Stock Books", SubTitle = "Desc", Count = "40", Status =
"OutStock" });
Declare ListView:
ListView listview = new ListView()
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
SeparatorVisibility = SeparatorVisibility.None,
RowHeight = 30,
};
listview.ItemTemplate = new DataTemplate(typeof(cell));
listview.ItemSelected += listviewItemSelected;
Take ViewCell and design your UI in ViewCell and assign binding
public class cell : ViewCell
{
public cell()
{
Image img = new Image()
{
VerticalOptions = LayoutOptions.StartAndExpand,
};
img.SetBinding(Image.SourceProperty, new Binding("Img"));
Label lbltitle = new Label()
{
VerticalOptions = LayoutOptions.Start,
TextColor = Color.Black
};
lbltitle.SetBinding(Label.TextProperty, new
Binding("Title"));
Label lbldesc = new Label()
{
VerticalOptions = LayoutOptions.Start,
TextColor = Color.Black
};
lbldesc.SetBinding(Label.TextProperty, new
Binding("SubTitle"));
StackLayout lblstack = new StackLayout()
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = { lbltitle, lbldesc },
};
BoxView boxEdit = new BoxView()
{
VerticalOptions = LayoutOptions.Start,
HorizontalOptions = LayoutOptions.End,
Color = Color.Black,
HeightRequest = 20,
WidthRequest = 10
};
tapGestureEdit.Tapped += tapGestureEditTapped;
Label lblCount = new Label()
{
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
TextColor = Color.Black
};
lblCount.SetBinding(Label.TextProperty, new
Binding("Count"));
StackLayout stackCount = new StackLayout()
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.EndAndExpand,
Children = { boxEdit, lblCount },
};
StackLayout stackMain = new StackLayout()
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = { img, lblstack, stackCount },
Orientation = StackOrientation.Horizontal,
Margin = new Thickness(10, 10, 10, 10)
};
View = stackMain;
}
}
public class AdminCell : ViewCell
{
public AdminCell()
{
Label lbl_binding = new Label()
{
TextColor = Color.FromRgb(30, 144, 255),
FontSize = 16,
VerticalTextAlignment = TextAlignment.Center,
HorizontalOptions = LayoutOptions.CenterAndExpand,
};
lbl_binding.SetBinding(Label.TextProperty, ".");
StackLayout stkBottom = new StackLayout()
{
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.CenterAndExpand,
VerticalOptions = LayoutOptions.Center,
Padding = new Thickness(0),
};
stkBottom.Children.Add(lbl_binding);
View = stkBottom;
}
}
this code is working for me removed the template and use this cell, i still don't understand why the data template is not working
I had the same issue and after some tests I've figured out how to not get always the last item.
You should put the Label creation ,and all the other elements you want put as ItemTemplate, inside the lambda of the DataTemplate like this (example code) :
DataTemplate itemTemplate = new DataTemplate(() =>
{
Label label = new Label()
{
Margin = new Thickness(45, 0, 0, 0),
HorizontalOptions = LayoutOptions.Start,
FontSize = (double)Xamarin.Forms.Application.Current.Resources["BodyFontSize"],
HeightRequest = 20
};
label.SetBinding(Label.TextProperty, "Name");
ViewCell templateCell = new ViewCell()
{
View = label
};
return templateCell;
});
Hope that helps (helped in my case).
I would like to know how to put a stacklayout into a grid case of xamarin forms and repeat tha pattern.
I decleare in my xaml my grid and the stacklayout.
<Grid x:Name="MyGrid" RowSpacing="0" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackLayout x:Name="MyLayout">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
</StackLayout.GestureRecognizers>
</StackLayout>
</Grid>
Then in my c# i wannt to create
var test = new List<Product>
{
new Product() {Desc = "DESC", Brand= "HelloWorld", Price= "30", Uri = "UriToPicture" },
new Product() {Desc = "DESC2", Brand= "HelloWorld2", Price= "30", Uri = "UriToPicture" }
};
Image PicProd = new Image { Aspect = Aspect.AspectFit };
PicProd.Source = FileImageSource.FromUri(new Uri(test[0].Uri));
Label Name= new Label { Text = test[0].Marque.ToString(), TextColor = Color.Black, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center };
Label Desc = new Label { Text = test[0].Desc.ToString(), TextColor = Color.Black, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center };
Label Price = new Label { Text = test[0].Prix.ToString(), TextColor = Color.Black, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center };
MyLayout.Children.Add(PicProd);
MyLayout.Children.Add(Name);
MyLayout.Children.Add(Desc);
MyLayout.Children.Add(Price);
MyGrid.Children.Add(MyLayout, 0, 0);
MyGrid.Children.Add(MyLayout, 0, 1);
Content = MyGrid;
So I have two stacklayout elements that i want to display on two colomns(side by side) but i don't succed to display them correctly
You can set the Grid.Row and Grid.Column on elements to position them inside the grid. These are 0-based indexers. So in your case you could set the StackLayout as follows:
<StackLayout x:Name="MyLayout" Grid.Row="0" Grid.Column="0">
And in the next you could put:
<StackLayout x:Name="MyLayout" Grid.Row="0" Grid.Column="1">
UPDATE:
You cannot add the same instance of a control to a page twice, which is why only one of your items is showing up. You should instantiate 2 actual instances of your StackLayout. Remove the StackLayout from your XAML and declare it in code-behind. Then create 2 separate label instances and add those to the appropriate StackLayout. It should look something like this:
var test = new List<Product>
{
new Product() {Text1 = "DESC", Text2= "HelloWorld" },
new Product() {Text1 = "DESC2", Text2= "HelloWorld2" }
};
MyGrid.BackgroundColor = Color.Yellow;
var Name = new Label { Text = test[0].Text1.ToString(), TextColor = Color.Black, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center };
var Desc = new Label { Text = test[0].Text2.ToString(), TextColor = Color.Black, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center };
var MyLayout = new StackLayout();
MyLayout.BackgroundColor = Color.Red;
MyLayout.Children.Add(Name);
MyLayout.Children.Add(Desc);
MyGrid.Children.Add(MyLayout, 0, 0);
var Name2 = new Label { Text = test[0].Text1.ToString(), TextColor = Color.Black, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center };
var Desc2 = new Label { Text = test[0].Text2.ToString(), TextColor = Color.Black, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center };
var MyLayout2 = new StackLayout();
MyLayout2.BackgroundColor = Color.Blue;
MyLayout2.Children.Add(Name2);
MyLayout2.Children.Add(Desc2);
MyGrid.Children.Add(MyLayout2, 1, 0);
Another thing to keep in mind, the parameters for the Children.Add call want the column as the first parameter and then the row. Additionally you could use this extension method to simplify the process of adding children with spans etc.
public static class GridExtension
{
public static void AddChild(this Grid grid, View view, int row, int column, int rowspan = 1, int columnspan = 1)
{
if (row < 0) throw new ArgumentOutOfRangeException(nameof(row));
if (column < 0) throw new ArgumentOutOfRangeException(nameof(column));
if (rowspan <= 0) throw new ArgumentOutOfRangeException(nameof(rowspan));
if (columnspan <= 0) throw new ArgumentOutOfRangeException(nameof(columnspan));
if (view == null) throw new ArgumentNullException(nameof(view));
Grid.SetRow((BindableObject)view, row);
Grid.SetRowSpan((BindableObject)view, rowspan);
Grid.SetColumn((BindableObject)view, column);
Grid.SetColumnSpan((BindableObject)view, columnspan);
grid.Children.Add(view);
}
}
So I have two Stacklayout elements that i want to display on two colomns(side by side) but i don't succed to display them correctly
The problem is that you don't have two Stacklayout elements. Instead, you have one single instance that you pass two times to the grid using Add method.
If you want a second Stacklayout side by side, first create a new instance of a Stacklayout either in code or in XAML:
<StackLayout x:Name="MySecondLayout">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
</StackLayout.GestureRecognizers>
</StackLayout>
And pass it to your grid using Add method:
//... Populate "MySecondLayout" as you did for "MyLayout"
// Add your two layouts to the grid
MyGrid.Children.Add(MyLayout, 0, 0);
MyGrid.Children.Add(MySecondLayout, 0, 1);
If you want to use the exact same Stacklayout twice side by side, you must create two different instances of a Stacklayout.