How to display grouped list of values in Xamarin? - list

I am trying to display grouped list of songs, which I get from server. Songs are grouped in sets according to their first letter. I've created two classes in this purpose SongSet and Song.
public class SongSet
{
public string FirstCharacter { get; set; }
public List<Song> ListOfSongs { get; set; } = new List<Song>();
public SongSet(string firstChar)
{
this.FirstCharacter = firstChar;
}
}
public class Song
{
public string SongTitle { get; set; }
public Song(string title)
{
this.SongTitle = title;
}
}
After received list from server I put it to list of SongSet objects.
public static List<SongSet> ListOfSongsFromServer { get; set; } = new List<SongSet>();
And here my code to display a whole list.
ListView listView = new ListView
{
IsGroupingEnabled = true,
GroupDisplayBinding = new Binding("FirstCharacter"),
ItemsSource = PlayerPageVM.ListOfSongsFromServer,
ItemTemplate = new DataTemplate(() =>
{
Label titleLabel = new Label();
titleLabel.SetBinding(Label.TextProperty, "SongTitle");
return new ViewCell
{
View = new StackLayout
{
Padding = new Thickness(0, 5),
Orientation = StackOrientation.Horizontal,
Children =
{
new StackLayout
{
VerticalOptions=LayoutOptions.Center,
Spacing=0,
Children =
{
titleLabel
}
}
}
}
};
})
};
this.Content = new StackLayout
{
Children =
{
listView
}
};
After build application I've received only list with first letters but without song titles uder them. Can anyone help me how to achievie this?

You want to display grouped list of songs, you could use CollectionView. Because it provides the easy way to show Group data.
Display grouped data:
<CollectionView ItemsSource="{Binding Animals}"
IsGrouped="true">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10">
...
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
And the problem of mode data could refer to document to make SongSet inherit from Lsit<Song> to generate a grouped data.
public class SongSet : List<Song>
{
public string FirstCharacter { get; set; }
public SongSet(string firstChar,List<Song> ListOfSongs): base(ListOfSongs)
{
this.FirstCharacter = firstChar;
}
}

Related

Integrate Blazor with Chart.js: how to pass an object

I want to display in my Blazor WebAssembly application, some graphs with Chart.js. I tried to use Chartjs.Blazor.Fork but I have few errors, for example I have opened another post about here.
So, after a day without results, I decided to start my own component. I follow the instruction I found in a blog. Basically, I have my Razor component called Chart.razor with the following code
#inject IJSRuntime JSRuntime
<canvas id="#Id"></canvas>
#code {
public enum ChartType
{
Pie,
Bar
}
[Parameter]
public string Id { get; set; }
[Parameter]
public ChartType Type { get; set; }
[Parameter]
public string[] Data { get; set; }
[Parameter]
public string[] BackgroundColor { get; set; }
[Parameter]
public string[] Labels { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Here we create an anonymous type with all the options
// that need to be sent to Chart.js
var config = new
{
Type = Type.ToString().ToLower(),
Options = new
{
Responsive = true,
Scales = new
{
YAxes = new[]
{
new { Ticks = new {
BeginAtZero=true
} }
}
}
},
Data = new
{
Datasets = new[]
{
new { Data = Data, BackgroundColor = BackgroundColor}
},
Labels = Labels
}
};
await JSRuntime.InvokeVoidAsync("setup", Id, config);
}
}
then I have my own mychart.js script to update the chart
window.setup = (id,config) => {
var ctx = document.getElementById(id).getContext('2d');
new Chart(ctx, config);
}
So, I use this code
<Chart Id="bar1" Type="#Chart.ChartType.Bar"
Data="#(new[] { " 10", "9" } )"
BackgroundColor="#(new[] { " yellow","red"} )"
Labels="#(new[] { " Fail","Ok" } )">
</Chart>
Ugly code but it is working. Now, I can display a graph in my page. Cool! What I want to display is something more complex, because I have to show a stacked bar graph with groups and the configuration is quite complicated.
I want to replace the config you can see in the page with for example a class. In this class I want to collect all configuration, like Type, Options, Data, Labels and so on, and pass them in the await JSRuntime.InvokeVoidAsync("setup", Id, config);`
For starting I created my base class like
public abstract class ConfigBase
{
protected ConfigBase(ChartType chartType)
{
Type = chartType;
}
public ChartType Type { get; }
public string CanvasId { get; } = Guid.NewGuid().ToString();
}
My problem is how to transform this class to obtain a valid object for the JavaScript to execute correctly new Chart(ctx, config);.
OK, it was quite easy. I saw a lot of different technics for that but there is a very basic simple way
await JSRuntime.InvokeVoidAsync("setup", Config.CanvasId, Config);

view model list return null object

view code:
#using HRHPMVS.Models
#model HRHPMVS.ViewModel.NationalityVM
#{
ViewBag.Title = "list";
//Layout = "~/Views/Shared/OrangeHR.cshtml";
Layout = null;
}
<h1>Details</h1>
<div>
<h1>Details</h1>
<div>
#if (Model.NationalitiesList != null)
{
foreach (var item in Model.NationalitiesList)
{
#Html.DisplayFor(m => item.Code)
}
}
</div>
</div>
controller code:
public ActionResult list()
{
ModelState.Clear();
NationRepObj.list();
return View();
}
model:
namespace HRHPMVS.Models
{
public class Nationality
{
public int ID { get; set; }
[Display(Name = "Name")]
[Required(ErrorMessage = "Name is Requirde")]
[RegularExpression(#"^[a-zA-Z]+$", ErrorMessage = "please: Use letters only ")]
public string Name { get; set; }
[Display(Name = "Code")]
[Required(ErrorMessage = "Code is Requirde")]
[RegularExpression(#"[0-9]*\.?[0-9]+", ErrorMessage = "{0} must be a Number.")]
[Range(0, int.MaxValue, ErrorMessage = "Please: enter valid integer Number")]
public int Code { get; set; }
public Nullable<short> IsActive { get; set; }
// ...
}
}
viewmodel:
namespace HRHPMVS.ViewModel
{
public class NationalityVM
{
public Nationality Nationality { get; set; }
public List<Nationality> NationalitiesList { get; set; }
// ...
}
}
viewmodellist:
namespace HRHPMVS.ViewModel
{
public class NationalityVMList
{
public List<NationalityVM> Nationalities {get;set;}
// ...
}
}
function :
public void list()
{
List<Nationality> n = new List<Nationality>();
Nationality nt = new Nationality { Code=1,Name="doodoo",ID=1,IsActive=1};
NationalityVM vm = new NationalityVM ();
List<NationalityVM> l1 = new List<NationalityVM>();
// foreach(var itm in nt)
n.Add(nt);
if (vm.NationalitiesList == null)
{
vm.NationalitiesList = new List<Nationality>();
vm.NationalitiesList.Add(nt);
}
}
I am trying to view detailed nationality in a view. I want to view it from listviewmodel but I failed. I made the viewmodel list point to view model and view model point to model but when I am trying to add nationality to list in return null value with error Null reference exception wasn't handled in user code.
I want to display nationality detail from viewmodel list
There are several issues with this code that it is hard to know what you are trying to achieve.
You have a class dubbed 'viewmodellist' called NationalityVMList. I don't believe this has a purpose. Maybe delete it.
Your view expects a model of type NationalityVM but your controller action passes nothing to it.
Your 'function' creates a list along with several unused variables and returns nothing.
Change your controller so it passes a model to the view:
public ActionResult list()
{
NationalityVM model = NationRepObj.GetNationalityVM();
ModelState.Clear();
return View(model);
}
Change your function to:
public NationalityVM GetNationalityVM()
{
NationalityVM vm = new NationalityVM();
Nationality nt = new Nationality { Code=1,Name="doodoo",ID=1,IsActive=1};
vm.NationalitiesList = new List<Nationality>();
vm.Add(nt);
return vm;
}
Hopefully this will get something working.

Sitecore Load all items into an MVC model?

I have created a bunch of custom templates to store items (such as Industries, Subindustries, etc.) in Sitecore. I now want to go about loading these into my Sitecore MVC model.
The lists are located in sitecore > Content > Lists. For example inside the Lists folder there is a folder called Country. I want to get back all the items within the Country folder and populate them as unordered list in my view.
UPDATE: I implemented the Glass.Mapper.Sc method suggested below. It is fully operational now.
This is what my working model looks like now:
using Glass.Mapper.Sc.Configuration;
using Glass.Mapper.Sc.Configuration.Attributes;
using Sitecore.Data.Items;
using Sitecore.Mvc.Presentation;
using System;
using System.Collections.Generic;
namespace Sitecore.Web.Models
{
public class Registration: IRenderingModel
{
public Rendering Rendering { get; set; }
public Item Item { get; set; }
public Item PageItem { get; set; }
public IEnumerable<CountryChildItem> CountryList { get; set; }
[SitecoreType(AutoMap = true)]
public class CountryItem
{
public virtual IEnumerable<CountryChildItem> Children { get; set; }
}
[SitecoreType(AutoMap = true)]
public class CountryChildItem
{
[SitecoreId]
public virtual Guid Id { get; set; }
[SitecoreInfo(SitecoreInfoType.Path)]
public virtual string Path { get; set; }
[SitecoreField]
public virtual string DisplayName { get; set; }
[SitecoreField]
public virtual string Abbreviation { get; set; }
}
public void Initialize(Rendering rendering)
{
Rendering = rendering;
Item = rendering.Item;
PageItem = PageContext.Current.Item;
}
}
}
and this is what my working contoller looks like:
using Glass.Mapper.Sc;
using Sitecore.Web.Models;
using System.Web.Mvc;
namespace Sitecore.Web.Controllers
{
public class RegistrationController : Controller
{
Registration registrationModel = new Registration();
public ActionResult Index()
{
ISitecoreContext sitecoreContext = new SitecoreContext();
ISitecoreService service = new SitecoreService(sitecoreContext.Database);
Registration.CountryItem countryItem = service.GetItem<Registration.CountryItem>("/sitecore/content/Lists/Country");
registrationModel.CountryList = countryItem.Children;
return View(registrationModel);
}
}
}
and a snippet of my working view:
<ul class="select-menu-options dropdown-menu">
#foreach (var country in Model.CountryList)
{
<li>#country.DisplayName</li>
}
</ul>
If I were in your position I'd look into Glassmapper for Sitecore.
It's a fairly lightweight ORM for Sitecore.
http://www.glass.lu/Mapper/Sc
I'd also suggest moving the lists located in
sitecore > Templates > User Defined > Lists > Content
to some where under either
sitecore > Content
or
sitecore > System
(whichever makes more sence)
UPDATE:
Try adding this above your class:
[SitecoreType(AutoMap = true)]
public class CountryItem
{
//...
}
If you change your CountryItem and other model classes to inherit from SearchResultItem like that:
[PredefinedQuery("TemplateID", ComparisonType.Equal, "{ID-OF-CountryItem-TEMPLATE}", typeof(ID))]
public class CountryItem : Sitecore.ContentSearch.SearchTypes.SearchResultItem
{
[IndexField("_displayname")]
public virtual string DisplayName { get; set; }
[IndexField("abbreviation")]
public string Abbreviation { get; set; }
}
You should be able to use Sitecore indexes to retrieve all the countries and other lists like that:
private static string IndexName
{
get
{
return string.Format("sitecore_{0}_index", (Context.ContentDatabase ?? Context.Database).Name);
}
}
private static string Language { get { return Context.Language.Name; } }
public IEnumerable<CountryItem> GetCountries()
{
using (var context = ContentSearchManager.GetIndex(IndexName).CreateSearchContext())
{
IQueryable<CountryItem> queryable = context.GetQueryable<CountryItem>();
queryable = queryable.Where(i => i.Language == Language);
queryable = queryable.Where(i => i.LatestVersion);
// ... maybe excluding standard values or some other filters
var searchResults = queryable.GetResults();
return queryable.ToList();
}
}
Please be aware that this is just an example. You need to test it and most probably adapt to your solution.
And as Dar Brett mentioned, you should not keep any data items under the Templates node.

How to get distinct records from a list

I have a list of type Myclass
List<Myclass> liSubjectIdDetail = new List<Myclass>();
where Myclass looks like
public class Myclass
{
public Nullable<decimal> SubjectId { get; set; }
public string SubjectName { get; set; }
}
I am adding records into liSubjectIdDetail from a table
foreach (decimal Id in VarCEMSIdDetail)
{
liSubjectIdDetail.AddRange(db.Stt.MyTable.Where(x => x.Id == Id).Select(x => new Myclass { SubjectId = x.SubjectId, SubjectName = x.SubjectName }).ToList());
}
where Id contains a list of certain Ids on the basis of which records I fetch.
Now I want to get only distinct records in this list.
I have tried it with hashtable in place of List
and I also tried
liSubjectIdDetail= liSubjectIdDetail.Distinct().ToList();
but this too, is not working. Please give me a better solution.
Thanks in advance
Try this extension method
public static class IEnumerableExtensions {
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
var seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
}
Usage:
liSubjectIdDetail= liSubjectIdDetail.DistinctBy(s => s.SubjectName).ToList();

List<T> modifies ObservableCollection<T> when List<T> indexed item is changed

I have found a work around for my issue, but I was wondering if someone can explain why this is happening.
I have a class DataCollector.
public class DataCollector
{
public string Name { get; set; }
public string Group { get; set; }
public int Count { get; set; }
public Brush Color { get; private set; }
public DataCollector(string name, string group, int count, Brush color)
{
Name = name;
Group = group;
Count = count;
Color = color;
}
}
In my code I have an ObservableCollection of DataCollector.
public ObservableCollection<DataCollector> dataGrid { get; set; }
I collect my data and use it just fine until when I went to add more features to the code.
I want to go through "dataGrid" and make a list of Groups and get a total count from all Names.
List<DataCollector> dataCollector = new List<DataCollector>();
List<string> _groupNames = new List<string>();
foreach (DataCollector dc in dataGrid)
{
int cnt = 0, index = 0;
bool groupFound = false;
if (dataCollector.Count == 0)
{
_groupNames.Add(dc.Group);
dataCollector.Add(dc));
}
else
{
foreach (string groupNames in _groupNames)
{
if (groupNames == dc.Group)
{
index = cnt;
nameFound = true;
}
cnt++;
}
if (nameFound)
{
// When I do this my dataGrid.Count increments, too
dataCollector[index].Count += dc.Count;
}
else
{
_groupNames.Add(dc.Group);
dataCollector.Add(dc);
}
}
}
To get around this I changed
dataCollector.Add(dc);
to
dataCollector.Add(new DataCollector(dc.Name, dc.Group, dc.Count, dc.Color));
Why did I have to this? Does adding "dc" to "dataCollector" create a link to "dataGrid"? If it does it doesn't make sense.
You do that because dataCollector.Add(dc) copies the reference to the memory location of an object. So changes to the data at the location is reflected in either collection as they hold pointers to the same objects.
What you could do instead is to mimic a copy constructor.
public DataCollector(DataCollector dataCollector)
{
Name = dataCollector.Name;
Group = dc.Group;
//...
}
It sorts of keep your code clean.