could you help me? I want to make a generic tree view component in blazor webassembly but I am a bit lost in how to do it, I want to be able to pass any type of object list to the component, for the moment I have done something very simple, with an object called directory load the component but I would like to replace it with Titem to be able to send any type of list
index.razor
#page "/index"
<h1>Treeview</h1>
<Treeview Directorios="directorios"></Treeview>
#code {
public Directorio[] directorios;
protected async override Task OnInitializedAsync()
{
var fall2014 = new Directorio("Fall 2014", new Directorio[] { }, new string[] { "image1.jpg", "image2.jpg", "image3.jpg" });
var summer2014 = new Directorio("Summer 2014", new Directorio[] { }, new string[] { "image10.jpg", "image20.jpg", "image30.jpg" });
var pictures = new Directorio("Pictures", new Directorio[] { fall2014, summer2014 }, new string[] { });
var music = new Directorio("Music", new Directorio[] { }, new string[] { "song1.mp3", "song2.mp3" });
directorios = new Directorio[] { pictures, music };
}
}
component.razor
<ul>
#foreach (var dir in Directorios)
{
<li>
<span #onclick="#dir.toggle">#dir.getIcon()</span>
<span>#dir.nombre</span>
#if (dir.expanded)
{
<div>
<ul>
#foreach (var archivo in dir.archivos)
{
<li>#archivo</li>
}
</ul>
<Treeview Directorios="#dir.directorios"></Treeview>
</div>
}
</li>
}
</ul>
#code {
[Parameter] public Directorio[] Directorios { get; set; }
}
directory.cs
public class Directorio
{
public bool expanded = false;
public string nombre;
public string[] archivos;
public Directorio[] directorios;
public Directorio(string nombre, Directorio[] directorios, string[] archivos)
{
this.nombre = nombre;
this.directorios = directorios;
this.archivos = archivos;
}
public void toggle()
{
expanded = !expanded;
}
public string getIcon()
{
if (expanded)
{
return "-";
}
return "+";
}
}
Try this one.
In future perhaps I will make a combotree and share with you here.
if you are able to do same, do not hesitate to post it here.
1.Treeview.razor
#typeparam Tvalue
#inherits TreeviewBase<Tvalue>
<ul class="parentUl">
#if (AllItems != null)
{
#foreach (var Pitem in AllItems)
{
if (GetPropertyValue(Pitem, ParentId) == ""|| Convert.ToInt32(GetPropertyValue(Pitem, ParentId)) == 0)
{
if (Convert.ToBoolean(GetPropertyValue(Pitem, HasChildren)))
{
<li>
<span #onclick="#(()=>SpanToggle(Pitem))" class="#_caretcss[Convert.ToInt32(#GetPropertyValue(Pitem, Id))]">#GetPropertyValue(Pitem, Text)</span>
<ul class="#_nestedcss[Convert.ToInt32(#GetPropertyValue(Pitem, Id))]">
#foreach (var Citem in AllItems)
{
if (GetPropertyValue(Pitem, Id) == GetPropertyValue(Citem, ParentId))
{
if (Convert.ToBoolean(GetPropertyValue(Citem, HasChildren)))
{
<li>
<span #onclick="#(()=>SpanToggle(Citem))" class="#_caretcss[Convert.ToInt32(#GetPropertyValue(Citem, Id))]">#GetPropertyValue(Citem, Text)</span>
<ul class="#_nestedcss[Convert.ToInt32(#GetPropertyValue(Citem, Id))]">
#foreach (var C1item in AllItems)
{
if (GetPropertyValue(Citem, Id) == GetPropertyValue(C1item, ParentId))
{
if (Convert.ToBoolean(GetPropertyValue(C1item, HasChildren)))
{
<li>
<span #onclick="#(()=>SpanToggle(C1item))" class="#_caretcss[Convert.ToInt32(#GetPropertyValue(C1item, Id))]">#GetPropertyValue(C1item, Text)</span>
<ul class="#_nestedcss[Convert.ToInt32(#GetPropertyValue(C1item, Id))]">
#foreach (var C2item in AllItems)
{
if (GetPropertyValue(C1item, Id) == GetPropertyValue(C2item, ParentId))
{
if (Convert.ToBoolean(GetPropertyValue(C2item, HasChildren)))
{
<li>
<span #onclick="#(()=>SpanToggle(C2item))" class="#_caretcss[Convert.ToInt32(#GetPropertyValue(C2item, Id))]">#GetPropertyValue(C1item, Text)</span>
</li>
}
else
{
<li>#GetPropertyValue(C2item, Text)</li>
}
}
}
</ul>
</li>
}
else
{
<li>#GetPropertyValue(C1item, Text)</li>
}
}
}
</ul>
</li>
}
else
{
<li>#GetPropertyValue(Citem, Text)</li>
}
}
}
</ul>
</li>
}
else
{
<li>#GetPropertyValue(Pitem, Text)</li>
}
}
}
}
</ul>
2.style.css
<style type="text/css">
/*css reference W3schools. "with small modification."*/
/* css begin*/
.parentUl li ul {
border-left: dashed 2px black;
height: fit-content;
border-start-end-radius: 2px;
}
ul, .parentUl {
list-style-type: none;
}
.parentUl ul li {
position: relative;
}
.parentUl ul li:before {
content: "";
position: absolute;
top: 13px;
left: -40px;
width: 40px;
height: 1px;
border-bottom: dashed 2px black;
}
.parentUl {
margin: 0;
padding: 0;
}
.caret {
cursor: pointer;
-webkit-user-select: none; /* Safari 3.1+ */
-moz-user-select: none; /* Firefox 2+ */
-ms-user-select: none; /* IE 10+ */
user-select: none;
}
.caret::before {
content: "\25B6";
color: black;
display: inline-block;
margin-right: 6px;
transition: all 0.45s;
}
.caret-down::before {
-ms-transform: rotate(60deg); /* IE 9 */
-webkit-transform: rotate(60deg); /* Safari */
transform: rotate(60deg);
transition: all 0.45s;
}
.nested {
display: none;
transition: all 0.45s;
}
.active {
display: block;
transition: all 0.45s;
}
/*css end*/
</style>
3.TreeviewBase.cs
public partial class TreeviewBase<Tvalue>:ComponentBase
{
[Parameter]
public List<Tvalue> DataSource { get; set; }
[Parameter]
public string Id { get; set; }
[Parameter]
public string ParentId { get; set; }
[Parameter]
public string HasChildren { get; set; }
[Parameter]
public string Text { get; set; }
[Parameter]
public string Expanded { get; set; }
protected List<Tvalue> AllItems;
protected Dictionary<int, bool> _caretDown= new Dictionary<int, bool>();
protected Dictionary<int, string> _caretcss=new Dictionary<int,string>();
protected Dictionary<int, string> _nestedcss=new Dictionary<int,string>();
protected override Task OnInitializedAsync()
{
//asigning to its new instance to avoid exceptions.
AllItems = new List<Tvalue>();
AllItems = DataSource.ToArray().ToList();
if (AllItems != null)
{
foreach (var item in AllItems)
{
var _id = Convert.ToInt32(GetPropertyValue(item, Id));
//initializing fields with default value.
_caretDown.Add(_id, true);
_caretcss.Add(_id, "caret");
_nestedcss.Add(_id, "nested");
}
}
return base.OnInitializedAsync();
}
protected override Task OnParametersSetAsync()
{
//This will check if the first item in the
// list/collection has a "parentId" then remove the "parentId" from it.
//Because we use the first item as a reference in the razor file, so it must not have "parentId".
var Parem = AllItems.First();
switch (GetPropertyType(Parem, ParentId))
{
case "Int32":
if (Convert.ToInt32(GetPropertyValue(Parem, ParentId)) > 0)
{
SetPropertyValue<int>(Parem, ParentId, 0);
}
break;
case "String":
if (GetPropertyValue(Parem, ParentId) != "")
{
SetPropertyValue<string>(Parem, ParentId, "");
}
break;
default:
break;
}
return base.OnParametersSetAsync();
}
protected void SpanToggle(Tvalue item)
{
var _clckdItemid = Convert.ToInt32(GetPropertyValue(item, Id));
_caretcss[_clckdItemid] = _caretDown[_clckdItemid] ? "caret caret-down" : "caret";
_nestedcss[_clckdItemid] = _caretDown[_clckdItemid] ? "active" : "nested";
_caretDown[_clckdItemid] = !_caretDown[_clckdItemid];
}
#region reflection methodes to get your property type, propert value and also set property value
protected string GetPropertyValue(Tvalue item, string Property)
{
if (item != null)
{
return item.GetType().GetProperty(Property).GetValue(item, null).ToString();
}
return "";
}
protected void SetPropertyValue<T>(Tvalue item, string Property, T value)
{
if (item != null)
{
item.GetType().GetProperty(Property).SetValue(item, value);
}
}
protected string GetPropertyType(Tvalue item, string Property)
{
if (item != null)
{
return item.GetType().GetProperty(Property).PropertyType.Name;
}
return null;
}
#endregion
}
Index.razor
<Treeview Tvalue="MailItem" DataSource="#MyFolder" Id="Id" Text="FolderName" ParentId="ParentId"
Expanded="Expanded" HasChildren="HasSubFolder"></Treeview>
#code{
protected class MailItem
{
public int Id { get; set; }
public string ParentId { get; set; }
public bool HasSubFolder { get; set; }
public string FolderName { get; set; }
public bool Expanded { get; set; }
}
List<MailItem> MyFolder = new List<MailItem>();
protected override Task OnInitializedAsync()
{
MyFolder.Add(new MailItem { Id = 1, FolderName = "Inbox", HasSubFolder = true, Expanded = true, ParentId = "" });
MyFolder.Add(new MailItem { Id = 2, FolderName = "Category", ParentId = "1", HasSubFolder = true, Expanded = true });
MyFolder.Add(new MailItem { Id = 3, FolderName = "Primary", ParentId = "2", HasSubFolder = false, Expanded = true });
MyFolder.Add(new MailItem { Id = 4, FolderName = "Social", ParentId = "6", HasSubFolder = false, Expanded = true });
MyFolder.Add(new MailItem { Id = 5, FolderName = "Promotion", ParentId = "6", HasSubFolder = false, Expanded = true });
MyFolder.Add(new MailItem { Id = 6, FolderName = "Demo", ParentId = "2", HasSubFolder = true, Expanded = true });
return base.OnInitializedAsync();
}
}
This is the logics behind a generic Treeview Component in Blazor.
Kindly click on this image to view the working example:
Related
I'm tired to search on web a solution for this. Basicly i am using InputBase to extend the normal inputbox to a custom component. For single selection in its ok, but turns complicate when i have mutiple selection "select multiple="multiple""
So here is the code:
File: XDropDownMultiSelect.razor
#using System.Linq.Expressions
#typeparam T
#inherits InputBase<T>
#if (!string.IsNullOrEmpty(Label))
{
<label class="form-label">#Label</label>
}
<select #bind="CurrentValue" class="form-control select2 #CssClass" id="#Id" #attributes="AdditionalAttributes" multiple>
#if (DropdownValues != null)
{
foreach (var cursor in DropdownValues)
{
<option value="#cursor.Key">#cursor.Value</option>
}
}
</select>
#code {
[Inject] public IJSRuntime _js { get; set; }
[Parameter, EditorRequired] public string Id { get; set; }
[Parameter] public string Label { get; set; }
[Parameter] public Expression<Func<T>> ValidationFor { get; set; }
[Parameter] public bool ShowDefaultOption { get; set; } = true;
[Parameter] public Dictionary<string, string> DropdownValues { get; set; }
[Parameter] public string Selected { get; set; }
protected override bool TryParseValueFromString(string value, out T result, out string validationErrorMessage)
{
if (typeof(T) == typeof(string))
{
result = (T)(object)value;
validationErrorMessage = null;
return true;
}
else if (typeof(T) == typeof(string[]))
{
result = (T)(object)(new string[] { value });
validationErrorMessage = null;
return true;
}
else if (typeof(T) == typeof(Guid))
{
Guid.TryParse(value, out var parsedValue);
result = (T)(object)parsedValue;
validationErrorMessage = null;
return true;
}
else if (typeof(T).IsEnum)
{
try
{
result = (T)Enum.Parse(typeof(T), value);
validationErrorMessage = null;
return true;
}
catch (ArgumentException)
{
result = default;
validationErrorMessage = $"The {FieldIdentifier.FieldName} field is not valid.";
return false;
}
}
throw new InvalidOperationException($"{GetType()} does not support the type '{typeof(T)}'.");
}
}
I use "CurrentValue" instead of "CurrentValueAsString" because is an array no string, and if i set CurrentValueAsString will have a render json problem...
Now i call by simple:
<XDropDownMultiSelect #bind-Value="usersSelected" Id="test" DropdownValues="usersAll" />
#code{
public string[] usersSelected { get; set; } = new [] { "user1" };
public string[] usersAll{ get; set; } = new [] { "user1", "user2", "user3" };
Its working, but dosen´t bind the new selection values to my selectValues object.
i found how to use CurrentValueAsString
protected override string FormatValueAsString(T? value)
{
if( value != null)
{
return value.ToJson(); //this is extension my to convert any object to json format.
}
return base.FormatValueAsString(value);
}
But this not update the source model with new selections.
I have a list of pages, each using the same Blazor page component, of which the content is generated dynamically based on the page name. The content is split across different tabs. Due to some Blazor rendering issue, when navigating between pages, not everything is updated, resulting in the update of my tab content, but my tab headers are not.
Everything is done in .NET Core 3.1 LTS, as I am not able to upgrade to .NET 5 yet due to various other constraints.
Given, as an example, the following page content:
pageContent.Add("page-1", new Dictionary<string, string>
{
{ "tab a", "This is page 1, tab a" },
{ "tab b", "This is page 1, tab b" },
{ "tab c", "This is page 1, tab c" }
});
pageContent.Add("page-2", new Dictionary<string, string>
{
{ "tab d", "This is page 2, tab d" },
{ "tab e", "This is page 2, tab e" }
});
The result is the following:
As you can see, on page 2 the content is updated, but the tab headers are not. However, if I move back from page 2 to page 1, the content of page 1 is displayed, but now with the tab headers of page 2 are shown.
My tab control is based on various samples found online, and although it contains a lot more functionality, I tried to reduce it to a very basic working example which reproduces the problem.
PageContentService.cs
public Dictionary<string, string> GetPageContent(string pageName)
=> pageContent.ContainsKey(pageName) ? pageContent[pageName] : new Dictionary<string, string>();
DynamicPage.razor
#page "/dynamic/{PageName}"
<MyDynamicContent PageName="#PageName"/>
#code {
[Parameter]
public string PageName { get; set; }
}
MyDynamicContent.razor
#if (isLoading)
{
<p>Loading...</p>
}
else
{
<MyTabs SelectedTab="#selectedTabTitle" OnSelectedTabChanged="OnSelectedTabChanged">
#foreach (var (tabId, tabContent) in currentPage)
{
<MyTabItem Title="#tabId">
#tabContent
</MyTabItem>
}
</MyTabs>
}
#code {
private bool isLoading;
private string selectedTabTitle;
private Dictionary<string, string> currentPage;
[Parameter]
public string PageName { get; set; }
[Inject]
public IPageContentService PageContentService { get; set; }
protected override void OnParametersSet()
{
base.OnParametersSet();
isLoading = true;
currentPage = PageContentService.GetPageContent(PageName);
if (currentPage != null && currentPage.Count > 0)
{
selectedTabTitle = currentPage.First().Key;
}
isLoading = false;
}
public void OnSelectedTabChanged(string title)
=> selectedTabTitle = title;
}
MyTabs.razor
<CascadingValue Value="#(this)" IsFixed="true">
<CascadingValue Value="#selectedTabName">
<div>
<ul class="nav nav-tabs">
#foreach (var item in tabItems)
{
var aCss = "nav-link";
if (item.Active)
{
aCss += " active show";
}
<li class="nav-item">
<a class="#aCss" tabindex="0" #onclick="async () => await item.OnTabClicked()">
#item.Title
</a>
</li>
}
</ul>
<div class="tab-content">
#ChildContent
</div>
</div>
</CascadingValue>
</CascadingValue>
#code {
private readonly List<MyTabItem> tabItems = new List<MyTabItem>();
private string selectedTabName;
[Parameter]
public string SelectedTab
{
get => selectedTabName;
set
{
if (selectedTabName != value)
{
selectedTabName = value;
OnSelectedTabChanged.InvokeAsync(value);
}
}
}
[Parameter]
public EventCallback<string> OnSelectedTabChanged { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
public IReadOnlyList<MyTabItem> TabItems
=> tabItems;
public async Task ChangeSelectedTab(string title)
{
SelectedTab = title;
await InvokeAsync(StateHasChanged);
}
public async Task AddTab(MyTabItem tabItem)
{
if (tabItems.All(x => x.Title != tabItem.Title))
{
tabItems.Add(tabItem);
await InvokeAsync(StateHasChanged);
}
}
public async Task RemoveTab(MyTabItem tabItem)
{
if (tabItems.Any(x => x.Title == tabItem.Title))
{
tabItems.Remove(tabItem);
await InvokeAsync(StateHasChanged);
}
}
}
MyTabItem.razor
#implements IAsyncDisposable
#{
var css = "tab-pane";
if (Active)
{
css += " active show";
}
}
<div class="#css">
#if (Active)
{
#ChildContent
}
</div>
#code {
[Parameter]
public string Title { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
[CascadingParameter]
public MyTabs ParentTabs { get; set; }
public bool Active
=> ParentTabs.SelectedTab == Title;
public async Task OnTabClicked()
{
if (ParentTabs != null)
{
await ParentTabs.ChangeSelectedTab(Title);
}
}
protected override async Task OnInitializedAsync()
{
if (ParentTabs != null)
{
await ParentTabs.AddTab(this);
}
await base.OnInitializedAsync();
}
public ValueTask DisposeAsync()
=> ParentTabs != null ? new ValueTask(ParentTabs.RemoveTab(this)) : new ValueTask(Task.CompletedTask);
}
My question is very simple: Why is my tab content updating, but my tab headers are not? And how do I get them to update when navigating between pages?
Although adding the #key property to each list isn't a bad idea, as #MisterMagoo suggested in the comments, it wasn't wat ultimately fixed it.
The trick was to make every async method synchronous.
MyTabs.razor
public void ChangeSelectedTab(string title)
{
SelectedTab = title;
StateHasChanged;
}
public void AddTab(MyTabItem tabItem)
{
if (tabItems.All(x => x.Title != tabItem.Title))
{
tabItems.Add(tabItem);
StateHasChanged;
}
}
public void RemoveTab(MyTabItem tabItem)
{
if (tabItems.Any(x => x.Title == tabItem.Title))
{
tabItems.Remove(tabItem);
StateHasChanged;
}
}
MyTabItem.razor
public void OnTabClicked()
{
ParentTabs?.ChangeSelectedTab(Title);
}
protected override void OnInitialized()
{
ParentTabs?.AddTab(this);
base.OnInitialized();
}
public void Dispose()
{
ParentTabs?.RemoveTab(this);
}
So the async methods seem to really change how the render pipeline behaves, and do things seemingly out of order. I guess that makes a bit sense, and might have some benefits too it, but it isn't what I needed in this scenario.
I used jquery select2 in blazor server side how i can bind selected value
<InputSelect class="form-control select2" #bind-Value="#purchaseSearch.PriorityId" id="search-priorityId">
<option value="">All</option>
#foreach (var priority in priorities)
{
<option value="#priority.Id">#priority.Name</option>
}
</InputSelect>
I has created a custom component select with select2 library for blazor.
I hope this is example for you.
- Select2.razor:
#typeparam TValue
#inherits InputBase<TValue>
#if (!string.IsNullOrWhiteSpace(Label))
{
<label class="form-control-label" for="#Id">
#Label
#if (Required)
{
<font color="red">(*)</font>
}
</label>
}
else
{
<LabelFor FieldIdentifier="FieldIdentifier"></LabelFor>
}
<select id="#Id" class="form-control select2" style="width: 100%;" >
<option #key="null" value="null">--- Chọn ---</option>
#if (Datasource != null)
#foreach (var item in Datasource)
{
if (item.Key == Value?.ToString())
{
<option #key="#item.Key" value="#item.Key" selected="selected">
#((MarkupString)#item.Value)
</option>
}
else
{
<option #key="#item.Key" value="#item.Key">
#((MarkupString)#item.Value)
</option>
}
}
</select>
<div class="form-control-validation">
<CustomValidationMessage Field="FieldIdentifier" TValue="string" />
</div>
Select2.razor.cs
public partial class SelectWithFilter<TValue> : InputBase<TValue>
{
[Parameter] public string Id { get; set; }
[Parameter] public string Label { get; set; }
[Parameter] public bool Required { get; set; }
//[Parameter] public Expression<Func<string>> ValidationFor { get; set; }
[Parameter] public ICollection<KeyValuePair<string, string>> Datasource { get; set; }
[Inject] IJSRuntime JSRuntime { get; set; }
public DotNetObjectReference<SelectWithFilter<TValue>> DotNetRef;
protected override bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage)
{
if (value == "null")
{
value = null;
}
if (typeof(TValue) == typeof(string))
{
result = (TValue)(object)value;
validationErrorMessage = null;
return true;
}
else if (typeof(TValue) == typeof(int) || typeof(TValue) == typeof(int?))
{
int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedValue);
result = (TValue)(object)parsedValue;
validationErrorMessage = null;
return true;
}
throw new InvalidOperationException($"{GetType()} does not support the type '{typeof(TValue)}'.");
}
protected override void OnInitialized()
{
base.OnInitialized();
DotNetRef = DotNetObjectReference.Create(this);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("select2Component.init", Id);
await JSRuntime.InvokeVoidAsync("select2Component.onChange", Id, DotNetRef, "Change_SelectWithFilterBase");
}
}
[JSInvokable("Change_SelectWithFilterBase")]
public void Change(string value)
{
if (value == "null")
{
value = null;
}
if (typeof(TValue) == typeof(string))
{
CurrentValue = (TValue)(object)value;
}
else if (typeof(TValue) == typeof(int))
{
int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedValue);
CurrentValue = (TValue)(object)parsedValue;
}
else if (typeof(TValue) == typeof(int?))
{
if (value == null)
{
CurrentValue = (TValue)(object)null;
}
else
{
int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int parsedValue);
CurrentValue = (TValue)(object)parsedValue;
}
}
}
}
js:
window.select2Component = {
init: function (Id) {
//Initialize Select2 Elements
$('#' + Id).select2();
},
onChange: function (id, dotnetHelper, nameFunc) {
$('#' + id).on('select2:select', function (e) {
dotnetHelper.invokeMethodAsync(nameFunc, $('#' + id).val());
});
},
}
this is my code
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage<ContentModels.HomePage>
#using ContentModels = Umbraco.Web.PublishedContentModels;
#using System.Web.Script.Serialization;
#using Newtonsoft.Json.Linq;
#using System.Collections.Generic;
#using Umbraco.Core.Models.PublishedContent;
#using Umbraco.Core.PropertyEditors;
#using Newtonsoft.Json;
#using System.Linq;
#{
Layout = "Master1.cshtml";
}
<!-- Top menu -->
#*#Html.Partial("TopMenu")*#
<!-- End top menu -->
<!-- Main nav -->
#Html.Partial("MainMenu")
<!-- Main nav ends -->
#Html.Partial("PromoCarousel")
<div class="landing-page">
#CurrentPage.GetGridHtml("bodyText")
</div>
<div class="col-lg-12">
#Umbraco.Field("promo")
</div>
#{
if (CurrentPage.HasValue("promo"))
{
var promoListValue = CurrentPage.GetPropertyValue("promo");
var serializer = new JavaScriptSerializer();
string json = serializer.Serialize(promoListValue);
var testmodel = serializer.DeserializeObject(json);
#* foreach (var item in promoListValue)
{
<span>#item </span>
}
#Html.Raw(json)
#Html.Raw(Json.Encode(json))
*#
#Html.Raw(testmodel)
var obj = Model.Content.GetPropertyValue<MyLookAlikeArchetypeModel>("promo");
<span>#obj </span>
foreach(var item in obj)
{
<p>#item.img</p>
<p>#item.alias</p>
}
}
}
<div class="container _borderless landingpage">
</div>
#functions {
public class MyLookAlikeArchetypeFieldSet
{
[JsonProperty("alias")]
public string alias { get; set; }
[JsonProperty("img")]
public string img { get; set; }
}
public class MyLookAlikeArchetypeModel
{
private List<MyLookAlikeArchetypeFieldSet> _Items;
public MyLookAlikeArchetypeModel()
{
_Items = new List<MyLookAlikeArchetypeFieldSet>();
}
public MyLookAlikeArchetypeModel(List<MyLookAlikeArchetypeFieldSet> list)
{
_Items = list;
}
public IEnumerator<MyLookAlikeArchetypeFieldSet> GetEnumerator()
{
return _Items.GetEnumerator();
}
public bool Any()
{
return _Items.Any();
}
}
public class MyLookAlikeArchetypeModelPropertyConverter : IPropertyValueConverter
{
public object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
{
return source;
}
public object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview)
{
try
{
var list = JsonConvert.DeserializeObject<List<MyLookAlikeArchetypeFieldSet>>(source as string);
return new MyLookAlikeArchetypeModel(list);
}
catch
{
return new MyLookAlikeArchetypeModel();
}
}
public object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview)
{
return null;
}
public bool IsConverter(PublishedPropertyType propertyType)
{
return propertyType.PropertyEditorAlias.Equals("My.Plugin.Package.Manifest.Alias");
}
}
}
but the obj returning as empty
var obj = Model.Content.GetPropertyValue<MyLookAlikeArchetypeModel>("promo");
the response for CurrentPage.GetPropertyValue("promo");
[ { "alias": "1", "content": "1", "img": "/media/1069/509253678.jpg" }, { "alias": "Slide 2", "content": "2", "img": "/media/1074/636609180.jpg" } ]
any help will be appreciated
Why don't you just deserialise it.
something like this... (sorry not tested this)
var object = JsonConvert.DeserializeObject<MyLookAlikeArchetypeModel> (archetypeValueAsString);
foreach (var item in object)
{
var Img = item.GetValue<string>("img");
}
I'm doing a project in ASP.net, but I have a specific design that I need.
Unfortantley, this design works on <ul> and <li> only.
The CSS Code of it is the following :
.nav-tabs.nav-stacked.nav-coupon-category {
margin-bottom: 30px;
-webkit-box-shadow: 0 3px 1px rgba(0,0,0,0.15);
box-shadow: 0 3px 1px rgba(0,0,0,0.15);
}
.nav-tabs.nav-stacked.nav-coupon-category > li > a {
text-transform: uppercase;
font-size: 13px;
z-index: 1;
-webkit-border-radius: 0;
border-radius: 0;
background: #fff;
border-left: none;
border-right: none;
border: none;
-webkit-transition: 0.2s;
-moz-transition: 0.2s;
-o-transition: 0.2s;
-ms-transition: 0.2s;
transition: 0.2s;
height: 40px;
line-height: 40px;
padding: 0px 0px 0px 55px;
position: relative;
margin: 0;
color: #666;
}
.nav-tabs.nav-stacked.nav-coupon-category > li > a .fa {
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
-o-transform: translate3d(0, 0, 0);
-ms-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
font-size: 18px;
position: absolute;
display: block;
width: 40px;
height: 40px;
background: #fbfbfb;
top: 0;
left: 0;
text-align: center;
line-height: 40px;
border-right: 1px solid #ededed;
}
and the index.aspx part looks like this :
<asp:ListBox CssClass="nav nav-tabs nav-stacked nav-coupon-category" ID="Step1CatList" runat="server" onselectedindexchanged="ListCat_SelectedIndexChanged">
<asp:ListItem Selected="True" Text="Second Hand" Value="Items"></asp:ListItem>
<asp:ListItem Text="Cars" Value="Cars"></asp:ListItem>
<asp:ListItem Text="Rea Estate" Value="RealEstate"></asp:ListItem>
</asp:ListBox>
As you can see, I tries using the CssClass, but it didn't work.
I would be glad if someone knows any solution to this problem.
I currently have this need as well. Since the listbox is a select, it doesn't look right on ipad. Keep in mind, I don't usually create server controls, though I know they can be helpful, so this is quick, dirty and unfinished, mainly due to unfamiliarity. Also note that I am currently working with it from client-side(jquery), and have not really tested the postback functionality. However, this should give you an idea.
public class ItemEventArgs : EventArgs
{
#region Properties
public int Index { get; set; }
public string Text { get; set; }
public string Value { get; set; }
#endregion Properties
}
public class UnorderedItem
{
#region Constructors
public UnorderedItem(string text, string value)
{
Text = text;
Value = value;
}
#endregion Constructors
#region Properties
public string Text { get; set; }
public string Value { get; set; }
#endregion Properties
}
[DefaultProperty("Text")]
[ToolboxData("<{0}:UnorderedList runat=server></{0}:UnorderedList>")]
public class UnorderedList : WebControl, IPostBackEventHandler//, IScriptControl
{
#region Fields
private ScriptManager scriptManager;
#endregion Fields
#region Constructors
public UnorderedList()
{
Items = new List<UnorderedItem>();
}
#endregion Constructors
#region Events
public event EventHandler<ItemEventArgs> ItemClick;
#endregion Events
#region Properties
[Bindable(true)]
[Category("Appearance")]
[Localizable(true)]
[DefaultValue("")]
public string ItemAltCssClass { get; set; }
[Bindable(true)]
[Category("Appearance")]
[Localizable(true)]
[DefaultValue("")]
public string ItemCssClass { get; set; }
[Bindable(true)]
[Category("Appearance")]
public List<UnorderedItem> Items { get; set; }
[Bindable(true)]
[DefaultValue("")]
[Category("Client Events")]
[Localizable(true)]
public string OnClientItemClick { get; set; }
[Bindable(true)]
[Category("Appearance")]
[Localizable(true)]
[DefaultValue("")]
public int SelectedIndex { get; set; }
[Bindable(true)]
[Category("Appearance")]
[Localizable(true)]
[DefaultValue("")]
public string SelectedItemCssClass { get; set; }
[Bindable(true)]
[Category("Appearance")]
[Localizable(true)]
[DefaultValue("")]
public string SelectedValue { get; set; }
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
public string Text { get; set; }
#endregion Properties
#region Methods
public void ClearSelection()
{
Text = null;
SelectedValue = null;
SelectedIndex = -1;
}
//public IEnumerable<ScriptDescriptor> GetScriptDescriptors()
//{
// return new ScriptReference[] { };
//}
//public IEnumerable<ScriptReference> GetScriptReferences()
//{
// return new ScriptDescriptor[] { };
//}
public void RaisePostBackEvent(string eventArgument)
{
ItemEventArgs args = new ItemEventArgs();
args.Index = Convert.ToInt32(eventArgument);
UnorderedItem item = Items[args.Index];
args.Text = item.Text;
args.Value = item.Value;
SelectedValue = args.Value;
SelectedIndex = args.Index;
Text = args.Text;
if (ItemClick != null)
{
ItemClick(this, args);
}
}
protected virtual void OnItemClick(ItemEventArgs e)
{
EventHandler<ItemEventArgs> handler = ItemClick;
if (handler != null)
{
handler(this, e);
}
}
protected override void OnPreRender(EventArgs e)
{
//if (!DesignMode)
//{
// scriptManager = ScriptManager.GetCurrent(Page);
// if (scriptManager == null)
// {
// throw new HttpException("A scriptmanager control must exist on the current page.");
// }
// scriptManager.RegisterScriptControl(this);
//}
base.OnPreRender(e);
}
protected override void Render(HtmlTextWriter writer)
{
//if (!this.DesignMode)
//{
// scriptManager.RegisterScriptDescriptors(this);
//}
var itemCssClass = false;
var itemAltCssClass = false;
if (!string.IsNullOrEmpty(ItemCssClass))
{
itemCssClass = true;
}
if (!string.IsNullOrEmpty(ItemAltCssClass))
{
itemAltCssClass = true;
}
writer.WriteBeginTag("ul");
if (!string.IsNullOrEmpty(CssClass))
{
writer.WriteAttribute("class", CssClass);
}
if (!string.IsNullOrEmpty(AccessKey))
{
writer.WriteAttribute("AccessKey", AccessKey);
}
if (Style != null && !string.IsNullOrEmpty(Style.Value))
{
writer.WriteAttribute("style", Style.Value);
}
writer.WriteAttribute("value", string.IsNullOrEmpty(SelectedValue) ? Items[0].Value : SelectedValue);
writer.WriteAttribute("index", SelectedIndex.ToString());
writer.WriteAttribute("id", this.ClientID);
writer.Write(HtmlTextWriter.TagRightChar);
var itemClass = string.Empty;
var prefix = string.Concat(ClientID, "_Item");
UnorderedItem item;
for (var i = 0; i < Items.Count; i++)
{
itemClass = string.Empty;
item = Items[i];
writer.WriteBeginTag("li");
writer.WriteAttribute("index", i.ToString());
writer.WriteAttribute("value", item.Value);
writer.WriteAttribute("onclick", String.Format("{0}{1}{2}{3}{4}{5}{6}{7}{8}", "SetSelectedValue(this, '", item.Text, "','", item.Value, "',", i, ");", OnClientItemClick ?? string.Empty, Page.ClientScript.GetPostBackEventReference(this, i.ToString())));
if (i % 2 == 0)
{
if (itemCssClass)
{
itemClass = ItemCssClass;
}
}
else
{
if (itemAltCssClass)
{
itemClass = ItemAltCssClass;
}
}
if (SelectedIndex == i)
{
itemClass = String.Format("{0}{1}{2}", itemClass, " ", SelectedItemCssClass).Trim();
}
if (itemClass != string.Empty)
{
writer.WriteAttribute("class", ItemCssClass);
}
writer.Write(HtmlTextWriter.TagRightChar);
writer.Write(item.Text);
writer.WriteEndTag("li");
}
writer.WriteEndTag("ul");
//base.Render(writer);
}
protected override void RenderContents(HtmlTextWriter writer)
{
base.RenderContents(writer);
}
#endregion Methods
}
Usage:
<ww:UnorderedList runat="server" ID="lstResourceIds" CssClass="lstResourceIds" style="Height:535px;width:250px;" ItemCssClass="lstResourceIdsItem" ItemAltCssClass="lstResourceIdsAltItem" AccessKey="L" meta:resourcekey="lstResourceIds" OnClientItemClick="return false;" />