ASP.NET GridView can`t find properties - asp.net

I have an ObjectDataSource, bind to static class state with all methods: select, update, insert, delete to work with instances of serviceRecord class. It looks fine. But when I bind it with GridView, GridView can`t find any serviceRecord property and auto generate columns. Without auto generation i have an exception: HttpException (0x80004005): A field or property with the name 'serviceStart' was not found on the selected data source.
<div style="text-align: center">
<asp:ObjectDataSource ID="ServiceRecordsDataSource" runat="server"
DataObjectTypeName="LaretsState.serviceRecord" TypeName="LaretsState.state"
DeleteMethod="deleteRecord" InsertMethod="addRecord"
SelectMethod="getRecords" UpdateMethod="updateRecord" >
</asp:ObjectDataSource>
<asp:GridView ID="GridView" runat="server" AutoGenerateColumns="False"
ObjectDataSourceId ="ServiceRecordsDataSource" DataSourceID="ServiceRecordsDataSource">
<Columns>
<asp:BoundField DataField="serviceStart" DataFormatString="dd:MM:yyyy hh:mm" HeaderText="Date and time of service" />
<asp:BoundField DataField="serviceDuration" DataFormatString="mm" HeaderText="Duration" />
<asp:BoundField DataField="creationTime" DataFormatString="dd.MM.yyyy hh.mm" HeaderText="Creation time" />
</Columns>
</asp:GridView>
The classes are:
namespace LaretsState
{
public static class state
{
public static actualState actualState
{ get { return new actualState(getActualState(), getNextRecord()); } }
private static List<serviceRecord> _plan = new List<serviceRecord>();
static state() { }
public static List<serviceRecord> getRecords()
{
return _plan;
}
public static void updateRecord(serviceRecord record)
{
serviceRecord newRecord = record;
lock (_plan)
{
serviceRecord oldRecord = _plan.Where(r=> r.id== record.id).FirstOrDefault();
if (oldRecord == null)
{ throw new ArgumentException("В плане отсутствует запись с id " + record.id, "recordid"); }
var ColissionRecords = GetCollisionRecords(newRecord);
if (ColissionRecords.Count() > 1 ||
ColissionRecords.Count() == 1 && !ColissionRecords.Contains(oldRecord))
{ throw new Exception("На предложенное время уже запланировано обслуживание"); }
oldRecord = newRecord;
//_plan.Remove(oldRecord);
//_plan.Add(newRecord);
}
}
public static void deleteRecord(serviceRecord record)
{
_plan.Remove(record);
}
public static void addRecord(serviceRecord record)
{
lock (_plan)
{
var ColissionRecords = GetCollisionRecords(record);
if (ColissionRecords.Count() > 1 )
{ throw new Exception("На предложенное время уже запланировано обслуживание"); }
_plan.Add(record);
}
}
private static List<serviceRecord> GetCollisionRecords (serviceRecord record)
{
return _plan.Where(r => r.serviceStart <= record.serviceStart.Add(record.serviceDuration)
&& r.serviceStart.Add(r.serviceDuration) > record.serviceStart).ToList();
}
private static serviceRecord getNextRecord()
{
DateTime nowdate = DateTime.Now;
if (_plan.Count() == 0) return null;
return _plan
.Where(r => r.serviceStart > nowdate)
.OrderBy(r => r.serviceStart)
.First();
}
private static serviceState getActualState()
{
DateTime nowDateTime = DateTime.Now;
var recordsInProgress = _plan.Where(r => r.serviceStart <= nowDateTime
&& r.serviceStart.Add(r.serviceDuration) > nowDateTime).Count();
if (recordsInProgress >0)
{ return serviceState.OnService; }
else
{ return serviceState.Normal; }
}
}
}
}
namespace LaretsState
{
[DataContract]
public class serviceRecord
{
private static int lastid = 0;
[DataMember]
public DateTime serviceStart;
[DataMember]
public TimeSpan serviceDuration;
[DataMember]
public readonly DateTime creationTime;
[DataMember]
public readonly int id;
public serviceRecord (DateTime serviceStart, TimeSpan serviceDuration)
:this()
{
this.serviceStart = serviceStart;
this.serviceDuration = serviceDuration;
}
public serviceRecord()
{
this.creationTime = DateTime.Now;
this.id = ++lastid;
}
}
}
Is it somthing wrong with a classes?

I found! In serviceRecord class I must use properties, not fields:
[DataMember]
public DateTime serviceStart { get; set; }
[DataMember]
public TimeSpan serviceDuration { get; set; }
[DataMember]
public DateTime creationTime { get;}
[DataMember]
public int id { get;}

Related

Can't get "invalid" class added to custom component when validation fails

I have created a custom Select Component which inherits from InputBase. Everything works expect for validation. I'm not sure how to ensure the "invalid" class is added to my custom form compoenent below as it does with other Blazor form compoenent such as InputText:
Select.razor
<div id="select-#Id" class="component-select" #ref="ReferenceToDiv">
#foreach (var option in Options)
{
<div #onclick="() => OnSelect(option)" class="option">#option.Text</div>
}
</div>
Select.razor.cs
namespace Accounting.Web.Components.Forms
{
public partial class SelectOption
{
public string Text { get; set; } = default!;
public string Value { get; set; } = default!;
}
public partial class Select<TValue> : InputBase<TValue>
{
[Parameter]
public ElementReference ReferenceToDiv { get; set; }
[Parameter]
public string Id { get; set; } = String.Empty;
[Parameter]
public List<SelectOption> Options { get; set; }
[Parameter]
public string PlaceholderText { get; set; } = "Select...";
[Parameter]
public EventCallback<Select<TValue>> OnSelected { get; set; } = default!;
public SelectOption SelectedOption { get; set; } = default!;
protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out TValue result, [NotNullWhen(false)] out string? validationErrorMessage)
{
if (BindConverter.TryConvertTo<TValue>(value, null, out result))
{
validationErrorMessage = null;
return false;
}
else
{
validationErrorMessage = "Err : Select value";
return false;
}
}
public void OnSelect(SelectOption? option)
{
TValue tmpValue;
option.Value = ""; // this line is to test the validation, it should always fail and add 'invalid' clas to the component classes
BindConverter.TryConvertTo<TValue>(option.Value, null, out tmpValue);
CurrentValue = tmpValue;
SelectedOption = option;
}
}
}
Index.razor
<EditForm Model="#AddDto" class="card card-body bg-light mt-5" OnValidSubmit="OnValidSubmit">
<DataAnnotationsValidator />
<Accounting.Web.Components.Forms.Select
Id="InvestmentEntitySelect"
Options="#entityOptions" #bind-Value="AddDto.InvestmentEntityId">
</Accounting.Web.Components.Forms.Select>
<button type="submit" class="btn btn-primary full-width">Add</button>
</EditForm>
Try adding #CssClass to the class - <div id="select-#Id" class="component-select #CssClass" #ref="ReferenceToDiv">.
The way it works in ComponentBase is:
protected string CssClass
{
get
{
var fieldClass = EditContext?.FieldCssClass(FieldIdentifier);
return AttributeUtilities.CombineClassNames(AdditionalAttributes, fieldClass) ?? string.Empty;
}
}
where EditContext?.FieldCssClass(FieldIdentifier) is an extension method that returns the valid/invalid css based on whether there's a validation message for the field defined by FieldIdentifier. The method combines this css string with the css supplied in the component attributes class.
This is used in an implementation (this is InputSelect) like this:
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "select");
builder.AddMultipleAttributes(1, AdditionalAttributes);
builder.AddAttributeIfNotNullOrEmpty(2, "class", CssClass);
builder.AddAttribute(3, "multiple", _isMultipleSelect);
//...
For the record the actual GetFieldCssClass code is:
public virtual string GetFieldCssClass(EditContext editContext, in FieldIdentifier fieldIdentifier)
{
var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
if (editContext.IsModified(fieldIdentifier))
{
return isValid ? "modified valid" : "modified invalid";
}
else
{
return isValid ? "valid" : "invalid";
}
}

Xamarin Forms: Selected items get cleared when perform search in listview

I have done the fetching of contacts from the phone using this blog.
Now I am trying to add the selection of contacts. Using a switch I have done the selection. But the selected contacts are clearing when performing a search operation.
xaml
<Switch
Toggled="OnToggledEvent"
HorizontalOptions="EndAndExpand"
VerticalOptions="CenterAndExpand"/>
xaml.cs
public List<Contact> contactList;
public MainPage(IContactsService contactService)
{
InitializeComponent();
contactList = new List<Contact>();
BindingContext = new ContactsViewModel(contactService);
}
void OnToggledEvent(object sender, EventArgs args)
{
ViewCell cell = (sender as Xamarin.Forms.Switch).Parent.Parent as ViewCell;
if (cell.BindingContext is Contact)
{
Contact contact = cell.BindingContext as Contact;
if (contact != null)
{
if (contact != null && !contactList.Contains(contact))
{
contactList.Add(contact);
}
else if (contact != null && contactList.Contains(contact))
{
contactList.Remove(contact);
}
}
}
Debug.WriteLine("contactList:>>" + contactList.Count);
}
ContactsViewModel
public class ContactsViewModel : INotifyPropertyChanged
{
IContactsService _contactService;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string Title => "Contacts";
string search;
public string SearchText
{
get { return search; }
set
{
if (search != value)
{
search = value;
OnPropertyChanged("SearchText");
if (string.IsNullOrEmpty(SearchText))
{
FilteredContacts = new ObservableCollection<Contact>(Contacts);
}
else
{
FilteredContacts = new ObservableCollection<Contact>(Contacts?.ToList()?.Where(s => !string.IsNullOrEmpty(s.Name) && s.Name.ToLower().Contains(SearchText.ToLower())));
}
}
}
}
public ObservableCollection<Contact> Contacts { get; set; }
ObservableCollection<Contact> filteredContacts;
public ObservableCollection<Contact> FilteredContacts
{
get { return filteredContacts; }
set
{
if (filteredContacts != value)
{
filteredContacts = value;
OnPropertyChanged("FilteredContacts");
}
}
}
public ContactsViewModel(IContactsService contactService)
{
_contactService = contactService;
Contacts = new ObservableCollection<Contact>();
Xamarin.Forms.BindingBase.EnableCollectionSynchronization(Contacts, null, ObservableCollectionCallback);
_contactService.OnContactLoaded += OnContactLoaded;
LoadContacts();
FilteredContacts = Contacts;
}
void ObservableCollectionCallback(IEnumerable collection, object context, Action accessMethod, bool writeAccess)
{
// `lock` ensures that only one thread access the collection at a time
lock (collection)
{
accessMethod?.Invoke();
}
}
private void OnContactLoaded(object sender, ContactEventArgs e)
{
Contacts.Add(e.Contact);
}
async Task LoadContacts()
{
try
{
await _contactService.RetrieveContactsAsync();
}
catch (TaskCanceledException)
{
Console.WriteLine("Task was cancelled");
}
}
}
I am adding the selected contact to a list when toggling the switch. If again click the switch I will remove the contact from the list. But the problem is when searching for a contact, already selected contacts get clear. I try to fix this using IsToggled property of switch, but no luck.
I have added a sample project here for the reference.
The itemsource updates every time you search , you should add a property inside model to log the status of the switch and implement INotifyPropertyChanged .
Model
public class Contact : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string Name { get; set; }
public string Image { get; set; }
public string[] Emails { get; set; }
public string[] PhoneNumbers { get; set; }
private bool isToggled;
public bool IsToggled {
get {
return isToggled;
} set {
isToggled = value;
OnPropertyChanged();
}
}
}
in Xaml
<Switch IsToggled="{Binding IsToggled} //... >"
Modify the method OnToggledEvent as below
void OnToggledEvent(object sender, EventArgs args)
{
var s = sender as Xamarin.Forms.Switch;
var model = s.BindingContext as Contact;
if(model != null)
{
if (model.IsToggled && !contactList.Contains(model))
{
contactList.Add(model);
}
else if (!model.IsToggled && contactList.Contains(model))
{
contactList.Remove(model);
}
Debug.WriteLine("contactList:>>" + contactList.Count);
}
}

ASP.NET maintain control state that has collection items

Interesting. I have a control QuickContacts with Contacts collection. When I declare Contacts of type List<Contact> it works perfectly, but when use ContactsList<Contact> which is a Generic that implements IList<T> it gives "Parser Error : Type 'Controls.ContactList'1[[Controls.Contact, Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' does not have a public property named 'Contact'."
Here, this code works perfectly with List<Contact>
[DefaultProperty("Contacts"),
ParseChildren(true, "Contacts"),
ToolboxData("<{0}:QuickContacts runat=server></{0}:QuickContacts>")]
public class QuickContacts : WebControl
{
private List<Contact> contactsList;
[Category("Behavior"),
Description("The contacts collection."),
DesignerSerializationVisibility(
DesignerSerializationVisibility.Content),
PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public List<Contact> Contacts
{
get
{
if (contactsList == null)
{
contactsList = new List<Contact>();
}
return contactsList;
}
}
I want to provide a Contacts collection that maintains state so I replace using List<Contacts> with custom ContactsList that implements IList<T>, IStateManager. When use IList<T> instead of List<T>, it doesn't work. Here is the
public class ContactList<T> : IList<T>, IStateManager
{
Then I use it as following:
[DefaultProperty("Contacts"),
ParseChildren(true, "Contacts"),
ToolboxData("<{0}:QuickContacts runat=server></{0}:QuickContacts>")]
public class QuickContacts : WebControl
{
private ContactList<Contact> contactsList;
[Category("Behavior"),
Description("The contacts collection."),
DesignerSerializationVisibility(
DesignerSerializationVisibility.Content),
PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public ContactList<Contact> Contacts
{
get
{
if (contactsList == null)
{
contactsList = new ContactList<Contact>();
}
return contactsList;
}
}
"Parser Error : Type 'Controls.ContactList'1[[Controls.Contact, Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' does not have a public property named 'Contact'."
Parser Error at <cc1:Contact Name=
<asp:Content ID="Content2" ContentPlaceHolderID="Main" runat="server">
<cc1:QuickContacts ID="QuickContacts1" runat="server" BorderStyle="Solid" BorderWidth="1px">
<cc1:Contact Name="someone" Email="someone#example.com"
Phone="(555) 555-0100" />
I read many posts about List<T> vs IList<T> but that still doesn't answer the question. What difference between List<T> and a class that implements IList<T> which causes this error?
Here is the answer, when reviewed List<T> implementation at Microsoft http://referencesource.microsoft.com
[DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))]
[DebuggerDisplay("Count = {Count}")]
[Serializable]
public class List<T> : IList<T>, System.Collections.IList, IReadOnlyList<T>
{
It implements System.Collections.IList too. And this was the point.
And here is the full working code.
[DefaultProperty("Contacts"),
ParseChildren(true, ChildrenAsProperties = true, DefaultProperty = "Contacts"),
ToolboxData("<{0}:QuickContacts runat=server></{0}:QuickContacts>")]
public class QuickContacts : WebControl
{
private StateManagedCollection<Contact> contactsList;
[Category("Behavior"),
Description("The contacts collection."),
DesignerSerializationVisibility(
DesignerSerializationVisibility.Content),
PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public StateManagedCollection<Contact> Contacts
{
get
{
if (contactsList == null)
{
contactsList = new StateManagedCollection<Contact>();
}
return contactsList;
}
}
protected override void RenderContents(HtmlTextWriter writer)
{
Table t = CreateContactsTable();
if (t != null)
{
t.RenderControl(writer);
}
}
private Table CreateContactsTable()
{
Table t = null;
if (contactsList != null && contactsList.Count > 0)
{
t = new Table();
foreach (Contact item in contactsList)
{
Contact aContact = item as Contact;
if (aContact != null)
{
TableRow row = new TableRow();
TableCell c1 = new TableCell();
c1.Text = aContact.Name;
row.Controls.Add(c1);
TableCell c2 = new TableCell();
c2.Text = aContact.Email;
row.Controls.Add(c2);
TableCell c3 = new TableCell();
c3.Text = aContact.Phone;
row.Controls.Add(c3);
t.Controls.Add(row);
}
}
}
return t;
}
protected override void LoadViewState(object savedState)
{
if (savedState != null)
{
Pair p = savedState as Pair;
base.LoadViewState(p.First);
contactsList.LoadViewState(p.Second);
}
}
protected override object SaveViewState()
{
Pair p = new Pair(base.SaveViewState(), contactsList.SaveViewState());
return p;
}
protected override void TrackViewState()
{
base.TrackViewState();
contactsList.TrackViewState();
}
}
And here is the StateManagedCollection
public class StateManagedCollection<T> : IList<T>, System.Collections.IList, IReadOnlyList<T>, IStateManager
where T : StateManagedClass, new()
{
private List<T> lst = default(List<T>);
private Boolean isTrackingViewState = default(Boolean);
private Boolean saveAll = default(Boolean);
public StateManagedCollection()
{
lst = new List<T>();
isTrackingViewState = false;
saveAll = false;
}
public void SetMarked()
{
for (int i = 0; i < lst.Count; i++)
{
lst[i].SetMarked();
}
}
public bool IsTrackingViewState
{
get { return isTrackingViewState; }
}
public void LoadViewState(object state)
{
if (state != null)
{
if (state is List<Object>)
{
// all items were saved
List<Object> allItems = (List<Object>)state;
saveAll = true;
Int32 count = lst.Count;
for (int i = 0; i < allItems.Count; i++)
{
if (i < count)
lst[i].LoadViewState(allItems[i]);
else
{
T item = new T();
item.LoadViewState(allItems[i]);
lst.Add(item);
}
}
}
else if (state is Dictionary<Int32, Object>)
{
Dictionary<Int32, Object> changedItems = (Dictionary<Int32, Object>)state;
foreach (KeyValuePair<Int32, Object> item in changedItems)
{
if (item.Key < lst.Count)
lst[item.Key].LoadViewState(item.Value);
}
}
}
}
public object SaveViewState()
{
if (saveAll)
{
List<Object> allItems = new List<Object>();
foreach (var item in lst)
{
item.SetMarked();
allItems.Add(item.SaveViewState());
}
return allItems;
}
else
{
Dictionary<Int32, Object> changedItems = new Dictionary<Int32, Object>();
for (int i = 0; i < lst.Count; i++)
{
Object state = lst[i].SaveViewState();
if (state != null)
{
changedItems.Add(i, state);
}
}
return changedItems;
}
}
public void TrackViewState()
{
isTrackingViewState = true;
for (int i = 0; i < lst.Count; i++)
{
lst[i].TrackViewState();
}
}
public int IndexOf(T item)
{
return lst.IndexOf(item);
}
public void Insert(int index, T item)
{
lst.Insert(index, item);
if (isTrackingViewState)
saveAll = true;
}
public void RemoveAt(int index)
{
lst.RemoveAt(index);
if (isTrackingViewState)
saveAll = true;
}
public T this[int index]
{
get
{
return lst[index];
}
set
{
lst[index] = value;
}
}
public void Add(T item)
{
lst.Add(item);
if (isTrackingViewState)
saveAll = true;
}
public void Clear()
{
lst.Clear();
if (isTrackingViewState)
saveAll = true;
}
public bool Contains(T item)
{
return lst.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
lst.CopyTo(array, arrayIndex);
}
public int Count
{
get { return lst.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
Boolean rslt = lst.Remove(item);
if (isTrackingViewState)
saveAll = true;
return rslt;
}
public IEnumerator<T> GetEnumerator()
{
return lst.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int Add(object value)
{
Add((T)value);
return Count - 1;
}
public bool Contains(object value)
{
return Contains((T)value);
}
public int IndexOf(object value)
{
return IndexOf((T)value);
}
public void Insert(int index, object value)
{
Insert(index, (T)value);
}
public bool IsFixedSize
{
get { return ((IList)lst).IsFixedSize; }
}
public void Remove(object value)
{
Remove((T)value);
}
object IList.this[int index]
{
get
{
return lst[index];
}
set
{
lst[index] = (T)value;
}
}
public void CopyTo(Array array, int index)
{
CopyTo((T[])array, index);
}
public bool IsSynchronized
{
get { return ((ICollection)lst).IsSynchronized; }
}
public object SyncRoot
{
get { return ((ICollection)lst).SyncRoot; }
}
}
And the StateManagedClass
public abstract class StateManagedClass : IStateManager
{
private Boolean isTrackingViewState = default(Boolean);
private StateBag viewState = default(StateBag);
public StateManagedClass()
{
isTrackingViewState = false;
viewState = new StateBag(false);
}
public virtual StateBag ViewState
{
get
{
return viewState;
}
}
public bool IsTrackingViewState
{
get { return isTrackingViewState; }
}
public void LoadViewState(object state)
{
if (state != default(object))
{
((IStateManager)viewState).LoadViewState(state);
}
}
public object SaveViewState()
{
Object savedState = default(Object);
if (viewState != null)
{
savedState =
((IStateManager)viewState).SaveViewState();
}
return savedState;
}
public void TrackViewState()
{
isTrackingViewState = true;
if (viewState != default(StateBag))
{
((IStateManager)viewState).TrackViewState();
}
}
public void SetMarked()
{
viewState.SetDirty(true);
}
}
And the Contact class
public class Contact : StateManagedClass
{
[
Category("Behavior"),
DefaultValue(""),
Description("Name of contact"),
NotifyParentProperty(true)
]
public String Name
{
get
{
Object s = ViewState["Name"];
return (s == null) ? String.Empty : (String)s;
}
set
{
ViewState["Name"] = value;
}
}
[
Category("Behavior"),
DefaultValue(""),
Description("Email address of contact"),
NotifyParentProperty(true)
]
public String Email
{
get
{
Object s = ViewState["Email"];
return (s == null) ? String.Empty : (String)s;
}
set
{
ViewState["Email"] = value;
}
}
[
Category("Behavior"),
DefaultValue(""),
Description("Phone number of contact"),
NotifyParentProperty(true)
]
public String Phone
{
get
{
Object s = ViewState["Phone"];
return (s == null) ? String.Empty : (String)s;
}
set
{
ViewState["Phone"] = value;
}
}
}
Test the control:
<%# Page Title="" Language="C#" MasterPageFile="~/RTL.Master" AutoEventWireup="true" CodeBehind="WebForm15.aspx.cs" Inherits="Controls.WebForm15" %>
<%# Register Assembly="Controls" Namespace="Controls" TagPrefix="cc1" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="Main" runat="server">
<cc1:QuickContacts ID="QuickContacts1" runat="server" BorderStyle="Solid" BorderWidth="1px">
<cc1:Contact Name="someone" Email="someone#example.com"
Phone="(555) 555-0100" />
<cc1:Contact Name="jae" Email="jae#fourthcoffee.com"
Phone="(555) 555-0101" />
<cc1:Contact Name="lene" Email="lene#contoso.com"
Phone="(555) 555-0102" />
</cc1:QuickContacts>
<br />
<asp:Button runat="server" ID="Button1" Text="Add" OnClick="Button1_Click"></asp:Button>
<asp:Button runat="server" Text="Refresh"></asp:Button>
<br />
<br />
<asp:HyperLink ID="HyperLink1" NavigateUrl="~/WebForm15.aspx"
runat="server">
Reload Page</asp:HyperLink>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="script" runat="server">
</asp:Content>
And the codebehind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Controls
{
public partial class WebForm15 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
SkolaControlsWorkings.Controls.Contact contact = new SkolaControlsWorkings.Controls.Contact();
contact.Name = "Name";
contact.Email = "Email#mai.com";
contact.Phone = "(111) 111-1111";
QuickContacts1.Contacts.Add(contact);
Button1.Visible = false;
}
}
}
Now you can see how pressing the "Add" button causes new contact item to be added to the list, and how the new added contacts are maintained through page postbacks.
This is should be a complete working example, have fun.
Here is the MSDN original example Web Control Collection Property Example

Steps to map classes using ValueInjector

Quickly getting to the problem the mapping does not occur for the following code. Could someone explain why? or what i should do for the mapping to occur?
var parent = new Parent();
parent.ChildOne.Add(new ChildOne() { Name = "Child One" });
parent.ChildTwo.Add(new ChildTwo() { Name = "Child Two" });
AnotherParent anotherParent = new AnotherParent();
anotherParent.InjectFrom<LoopValueInjection>(parent);
Required Class are below
Anothher child one
public class AnotherChildOne
{
public string Name { get; set; }
}
Another child two
public class AnotherChildTwo
{
public string Name { get; set; }
}
Another Parent
public class AnotherParent
{
public ICollection<AnotherChildOne> ChildOne { get; set; }
public ICollection<AnotherChildTwo> ChildTwo { get; set; }
public AnotherParent()
{
ChildOne = new Collection<AnotherChildOne>();
ChildTwo = new Collection<AnotherChildTwo>();
}
}
Child Two
public class ChildTwo
{
public string Name { get; set; }
}
Child One
public class ChildOne
{
public string Name { get; set; }
}
Parent
public class Parent
{
public ICollection<ChildOne> ChildOne { get; set; }
public ICollection<ChildTwo> ChildTwo { get; set; }
public Parent()
{
ChildOne = new Collection<ChildOne>();
ChildTwo = new Collection<ChildTwo>();
}
}
I believe by default Value Injector will only inject the properties with the same name of the same type. You can get around this using a tweak to the CloneInjection sample from the Value Injector documentation as described here with this code:
public class CloneInjection : ConventionInjection
{
protected override bool Match(ConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name && c.SourceProp.Value != null;
}
protected override object SetValue(ConventionInfo c)
{
//for value types and string just return the value as is
if (c.SourceProp.Type.IsValueType || c.SourceProp.Type == typeof(string)
|| c.TargetProp.Type.IsValueType || c.TargetProp.Type == typeof(string))
return c.SourceProp.Value;
//handle arrays
if (c.SourceProp.Type.IsArray)
{
var arr = c.SourceProp.Value as Array;
var clone = Activator.CreateInstance(c.TargetProp.Type, arr.Length) as Array;
for (int index = 0; index < arr.Length; index++)
{
var a = arr.GetValue(index);
if (a.GetType().IsValueType || a.GetType() == typeof(string)) continue;
clone.SetValue(Activator.CreateInstance(c.TargetProp.Type.GetElementType()).InjectFrom<CloneInjection>(a), index);
}
return clone;
}
if (c.SourceProp.Type.IsGenericType)
{
//handle IEnumerable<> also ICollection<> IList<> List<>
if (c.SourceProp.Type.GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable)))
{
var t = c.TargetProp.Type.GetGenericArguments()[0];
if (t.IsValueType || t == typeof(string)) return c.SourceProp.Value;
var tlist = typeof(List<>).MakeGenericType(t);
var list = Activator.CreateInstance(tlist);
var addMethod = tlist.GetMethod("Add");
foreach (var o in c.SourceProp.Value as IEnumerable)
{
var e = Activator.CreateInstance(t).InjectFrom<CloneInjection>(o);
addMethod.Invoke(list, new[] { e }); // in 4.0 you can use dynamic and just do list.Add(e);
}
return list;
}
//unhandled generic type, you could also return null or throw
return c.SourceProp.Value;
}
//for simple object types create a new instace and apply the clone injection on it
return Activator.CreateInstance(c.TargetProp.Type)
.InjectFrom<CloneInjection>(c.SourceProp.Value);
}
}
If you include the above CloneInjection code you will want to do this:
anotherParent.InjectFrom<CloneInjection>(parent);
instead of:
anotherParent.InjectFrom<LoopValueInjection>(parent);

LINQ to XML & bind to Gridview

I am reading data from XML like this:
(Contacts.xml)
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--LINQ to XML Contacts XML Example-->
<?MyApp 123-44-4444?>
<contacts>
<contact>
<name>Patrick Hines</name>
<phone type="home">206-555-0144</phone>
<phone type="work">425-555-0145</phone>
<address>
<street1>123 Main St</street1>
<city>Mercer Island</city>
<state>WA</state>
<postal>68042</postal>
</address>
</contact>
<contact>
<name>Gretchen Rivas</name>
<phone type="mobile">206-555-0163</phone>
<address>
<street1>123 Main St</street1>
<city>Mercer Island</city>
<state>WA</state>
<postal>68042</postal>
</address>
</contact>
</contacts>
My code for getting data from XML file:
(contacts.cs)
public class Contact
{
//static members
public static string fLocation;
//private members
private string name;
private List<PhoneNumber> pNumber;
private Adress cAdress;
//public members
public Adress CAdress
{
get { return cAdress; }
}
public List<PhoneNumber> PNumber
{
get { return pNumber; }
}
public string Name
{
get { return name; }
}
//Constructor
public Contact(string _name, List<PhoneNumber> _pNumber,Adress _cAdress)
{
name = _name;
pNumber = _pNumber;
cAdress = _cAdress;
}
public static List<Contact> Get()
{
List<Contact> output = new List<Contact>();
XDocument data = XDocument.Load(fLocation);
var query = from c in data.Descendants("contact")
orderby c.Element("name").Value
select c;
foreach (var item in query)
{
List<PhoneNumber> pNumber = new List<PhoneNumber>();
foreach (var PhoneNumbers in item.Elements("phone"))
{
pNumber.Add(new PhoneNumber(PhoneNumbers.Value,PhoneNumbers.Attribute("type").Value));
}
Adress cAdress = new Adress(item.Element("address").Element("street1").Value,
item.Element("address").Element("city").Value,
item.Element("address").Element("state").Value,
item.Element("address").Element("postal").Value);
output.Add(new Contact(item.Element("name").Value,pNumber,cAdress));
}
return output;
}
//subclasses
public class Adress
{
private string street;
private string city;
private string state;
private string postal;
public string Postal
{
get { return postal; }
}
public string State
{
get { return state; }
}
public string City
{
get { return city; }
}
public string Street
{
get { return street; }
}
public Adress(string _street,string _city, string _state, string _postal)
{
street = _street;
city = _city;
state = _state;
postal = _postal;
}
}
public class PhoneNumber
{
private string number;
private string numberType;
public string NumberType
{
get { return numberType; }
}
public string Number
{
get { return number; }
}
public PhoneNumber(string _number, string _phoneNumberType)
{
number = _number;
numberType = _phoneNumberType;
}
}
}
And I want to somehow bind to Gridview:
(default.aspx)
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataSourceID="ObjectDataSource1">
<Columns>
<asp:BoundField DataField="Name" HeaderText="Name" ReadOnly="True"
SortExpression="Name" />
<asp:DynamicField DataField="CAdress" HeaderText="CAdress" />
<asp:DynamicField DataField="PNumber" HeaderText="PNumber" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="Get"
TypeName="LINQtoXML_WebForms.Contact"></asp:ObjectDataSource>
Any ideas how to bind for example Contact.PhoneNumber.number + "-" Contact.PhoneNumber.numberType to colomn?
i.e.: 774-6655-252 - mobile
Thanks.
When I run it, it shows me an error:
Could not determine a MetaTable. A MetaTable could not be determined for the data source 'ObjectDataSource1' and one could not be inferred from the request URL. Make sure that the table is mapped to the dats source, or that the data source is configured with a valid context type and table name, or that the request is part of a registered DynamicDataRoute.
I think you either have to add meta information to your class or use the approach I'd use - write a method yourself instead of using objectdatasource object.
For example Linq to XML
public List<Contact> GetContacts()
{
XDocument doc = XDocument.Load("path_to_some_file.xml");
var contacts = from o in doc.Descendants("contact")
select new Contact()
{
Name = (string)o.Element("Name"),
Phone = (string)o.Element("Phone")
};
return contacts.ToList();
}
and then just bind it in code-behind to your control:
GridView1.DataSource = GetContacts();
GridView1.DataBind();
Hope you get the idea.

Resources