Sorting ObservableCollection<> manually - c#

I have following ObservableCollection in my class, I am binding data to my listbox using that Collection in my Windows phone 7 application
public ObservableCollection<CustomClass> myList = new ObservableCollection<CustomClass>();
My Custom Class
public class CustomClass
{
public string Id { get; set; }
public string Name { get; set; }
public string EventName { get; set; }
public string EventDate get
{
return EventDate;
}
set
{
if (value != null)
{
DateTime eventDate = DateTime.Parse(value);
int today = DateTime.Now.Day;
if (eventDate.Day <= today + 1 & eventDate.Day >= today - 2)
{
if (eventDate.Day == today)
EventDate = "Today";
else if (eventDate.Day == (today + 1))
EventDate = "Tomorrow";
else if (eventDate.Day == (today - 1))
EventDate = "Yesterday";
else if (eventDate.Day >= (today - 2))
EventDate = "Just Passed";
}
else
{
EventDate = value;
}
}
}
}
Now i want to sort myList according to the data in the EventDate
The Data in the EventDate will be one of the following in all the cases
Just Passed
Yesterday
Tomorrow
Today
Date //Format "MMM/dd"
Custom Collection must be sorted according to the above order only
i am getting data from different sources so sorting is not possible at the time of binding data into the collection
Is it possible??

Since your CustomClass doesn't implement INotifyPropertyChange, I'm assuming you'll only have to sort at insert (when adding to the collection). So IMHO, the easiest thing to do (similiar to Randolf Rincón-Fadul's solution) is to subclass, then override the Add method.
public class ComparingObservableCollection<T> : ObservableCollection<T>
where T : IComparable<T>
{
protected override void InsertItem(int index, T item)
{
int i = 0;
bool found = false;
for (i = 0; i < Items.Count; i++)
{
if (item.CompareTo(Items[i]) < 0) {
found = true;
break;
}
}
if (!found) i = Count;
base.InsertItem(i, item);
}
}
Then all you have to do is implement IComparable<CustomClass> on CustomClass like this:
public class CustomClass : IComparable<CustomClass>
{
public string Id { get; set; }
public string Name { get; set; }
public string EventName { get; set; }
public string EventDate { get
{
return EventDate;
}
set
{
if (value != null)
{
DateTime eventDate = DateTime.Parse(value);
int today = DateTime.Now.Day;
if (eventDate.Day <= today + 1 & eventDate.Day >= today - 2)
{
if (eventDate.Day == today)
EventDate = "Today";
else if (eventDate.Day == (today + 1))
EventDate = "Tomorrow";
else if (eventDate.Day == (today - 1))
EventDate = "Yesterday";
else if (eventDate.Day >= (today - 2))
EventDate = "Just Passed";
}
else
{
EventDate = value;
}
}
}
private int Order { get {
switch(EventDate) {
case "Just Passed": return 1;
case "Yesterday": return 2;
case "Tomorrow": return 3;
case "Today": return 4;
default: return 5;
}
}
}
public int CompareTo(CustomClass other) {
return this.Order.CompareTo(other.Order);
}
}

Taken a look here??
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/5909dbcc-9a9f-4260-bc36-de4aa9bbd383/
A few decent answers towards the bottom.

You can always subclass:
/// <summary>
/// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed and allows sorting.
/// </summary>
/// <typeparam name="T">The type of elements in the collection.</typeparam>
public class SortableObservableCollection<T> : ObservableCollection<T>
{
/// <summary>
/// Sorts the items of the collection in ascending order according to a key.
/// </summary>
/// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
/// <param name="keySelector">A function to extract a key from an item.</param>
public void Sort<TKey>(Func<T, TKey> keySelector)
{
InternalSort(Items.OrderBy(keySelector));
}
/// <summary>
/// Sorts the items of the collection in ascending order according to a key.
/// </summary>
/// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
/// <param name="keySelector">A function to extract a key from an item.</param>
/// <param name="comparer">An <see cref="IComparer{T}"/> to compare keys.</param>
public void Sort<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
{
InternalSort(Items.OrderBy(keySelector, comparer));
}
/// <summary>
/// Moves the items of the collection so that their orders are the same as those of the items provided.
/// </summary>
/// <param name="sortedItems">An <see cref="IEnumerable{T}"/> to provide item orders.</param>
private void InternalSort(IEnumerable<T> sortedItems)
{
var sortedItemsList = sortedItems.ToList();
foreach (var item in sortedItemsList)
{
Move(IndexOf(item), sortedItemsList.IndexOf(item));
}
}
}
And then sort using a lambda expression
((SortableObservableCollection<CustomClass>)MyList).Sort(s => s.EventDate);

Related

Dynamically adding properties to an object in c#

I have a problem:
public class PaginationSet
{
public int TotalItemCount { get; set; }
public int Page { get; set; }
public int Amount { get; set; }
public string Sort { get; set; }
public string Order { get; set; }
/// <summary>
/// This is used to store all the above information in, while still maintaining the automated index count from the internal for loop link builder.
///
/// Don't forget to pass the index into this!
/// </summary>
public Func<int, object> PaginationLinkData
{
get
{
return index => new
{
page = index, // This is the internal pointer part that is used currently by Bootstrap pagination function
amount = this.Amount,
sort = this.Sort,
order = this.Order
};
}
}
}
this.Sort and this.Order are sometimes null. If they are, I would like to not place them inside the returning Func<int,object>... How do i go about doing this?
It might look something like this:
public Func<int, object> PaginationLinkData
{
get
{
Func<int,object> something = index => new
{
page = index, // This is the internal pointer part that is used currently by Bootstrap pagination function
amount = this.Amount
};
if( this.Sort != null )
{
something.sort = this.Sort,
something.order= this.Order
}
return something;
}
}
}
Try using expando object...
public Func<int, object> PaginationLinkData
{
get
{
return index =>
{
dynamic obj = new ExpandoObject();
obj.page = index;
obj.amount = this.Amount;
if (this.Sort != null)
{
obj.sort = this.Sort;
}
if (this.Order != null)
{
obj.order = this.Order;
}
return obj;
};
}
}
Can't you just do something like this?
public Func<int, object> PaginationLinkData
{
get
{
if( this.Sort != null )
{
return index => new
{
page = index, // This is the internal pointer part that is used currently by Bootstrap pagination function
amount = this.Amount,
sort = this.Sort,
order = this.Order
};
}
else
{
return index => new
{
page = index, // This is the internal pointer part that is used currently by Bootstrap pagination function
amount = this.Amount,
};
}
}
}
I am guessing you are serializing to JSon somewhere. If so and you can use dynamic why not:
/// <summary>
/// This is used to store all the above information in, while still maintaining the automated index count from the internal for loop link builder.
///
/// Don't forget to pass the index into this!
/// </summary>
public Func<int, object> PaginationLinkData
{
get
{
dynamic res = new ExpandoObject();
res.amount = Amount;
if (Sort != null) res.sort = Sort;
if (Order != null) res.order = Order;
return index =>
{
res.page = index;
return res;
};
}
}

LongListSelector acting strange

I’m experiencing a problem with the longlistselector witch is a bit strange... I’m reading a list asynchronously on a multi Pivot page, and if I don’t change the pivot until the resulto f the list Reading, it will create the contacts list successfuly ( in a longlistselector on the pivot item number 3 ) and when I go to that pivot the contacts list is displayed withou any problems, but when I open the Page and change the pivot before the longlistselector is created when the asychronous function returns the results and fills the longlistselector on pivot no.3 the contacts wont get updated ( when I go to pivot 3 no contacts are shown)...
I’ll post my code so you can have a clearer picture of the situation and maybe figure out what is happening.
The code is based in the PhoneToolkit LongListSelector example (buddies list)
public partial class Feeds : PhoneApplicationPage
{
bool isLoading = false;
bool loadingFilmates = true;
public Feeds()
{
InitializeComponent();
// ...
Loaded += FeedsPage_Loaded;
SystemTray.ProgressIndicator = new ProgressIndicator();
DataContext = this;
getFilmatesList();
longlistFilmates.SelectionChanged += FilmateSelectionChanged;
// ...
}
private async void getFilmatesList()
{
Feed userFilmates = await Feed.GetFilmates(App.Current.AppUser, App.Current.WSConfig, 0, "", 10000); // read feeds from webservice
Filmates = AlphaKeyGroup<StayObjectFilmates>.CreateGroups(AllFilmates.GetCurrent(userFilmates), CultureInfo.CurrentUICulture, (p) => { return p.Name; }, true);
//longlistFilmates.Visibility = System.Windows.Visibility.Collapsed;
longlistFilmates.Visibility = System.Windows.Visibility.Visible;
longlistFilmates.UseLayoutRounding = true;
pivotFeed.Visibility = System.Windows.Visibility.Collapsed;
pivotFeed.Visibility = System.Windows.Visibility.Visible;
}
}
Notice that I’ve even tried changing the Visibility property when it loads to force a re-render on the screen and it didn’t work.
This is the StayObjectFilmates class:
public class StayObjectFilmates
{
public string Img { get; private set; }
public string Name { get; private set; }
public string UserId { get; private set; }
public string Id { get; set; }
public User user { get; set; }
public StayObjectFilmates()
{
//Img = "";
//Name = "";
//Id = "";
}
public StayObjectFilmates(string p_img, string p_name, string p_Id)
{
Img = p_img;
Name = p_name;
UserId = p_Id;
}
public StayObjectFilmates(User p_user)
{
user = p_user;
}
public static string GetNameKey(StayObjectFilmates filmate)
{
char key = char.ToLower(filmate.Name[0]);
if (key < 'a' || key > 'z')
{
key = '#';
}
return key.ToString();
}
public static int CompareByName(object obj1, object obj2)
{
StayObjectFilmates p1 = (StayObjectFilmates)obj1;
StayObjectFilmates p2 = (StayObjectFilmates)obj2;
int result = p1.Name.CompareTo(p2.Name);
if (result == 0)
{
result = p1.Img.CompareTo(p2.Img);
}
return result;
}
}
This is the AllFilmates class:
public class AllFilmates : IEnumerable<StayObjectFilmates>
{
private static Dictionary<int, StayObjectFilmates> _filmateLookup;
private static AllFilmates _instance;
private Feed filmates;
// public List<StayObjectFilmates> Filmates { get; private set; }
public static AllFilmates GetCurrent(Feed p_filmates)
{
if (_instance == null)
{
_instance = new AllFilmates();
}
_instance.filmates = p_filmates;
return _instance;
}
public static AllFilmates Current
{
get
{
return _instance ?? (_instance = new AllFilmates());
}
}
public StayObjectFilmates this[int index]
{
get
{
StayObjectFilmates filmate;
_filmateLookup.TryGetValue(index, out filmate);
return filmate;
}
}
#region IEnumerable<StayObjectFilmates> Members
public IEnumerator<StayObjectFilmates> GetEnumerator()
{
EnsureData();
return _filmateLookup.Values.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
EnsureData();
return _filmateLookup.Values.GetEnumerator();
}
#endregion
private void EnsureData()
{
if (_filmateLookup == null)
{
_filmateLookup = new Dictionary<int, StayObjectFilmates>();
if (filmates != null)
{
int i = 0;
foreach (var item in filmates.itemsList)
{
User friend = item as User;
string userphoto = (friend.photo == null) ? "Images/avatar.jpg" : friend.photo;
StayObjectFilmates f = new StayObjectFilmates(userphoto, friend.fullName, i.ToString());
_filmateLookup[i] = f;
i++;
}
}
}
}
}
And this is the AlphaKeyGroup.cs file :
public class AlphaKeyGroup<T> : List<T>
{
private const string GlobeGroupKey = "\uD83C\uDF10";
/// <summary>
/// The delegate that is used to get the key information.
/// </summary>
/// <param name="item">An object of type T</param>
/// <returns>The key value to use for this object</returns>
public delegate string GetKeyDelegate(T item);
/// <summary>
/// The Key of this group.
/// </summary>
public string Key { get; private set; }
/// <summary>
/// Public constructor.
/// </summary>
/// <param name="key">The key for this group.</param>
public AlphaKeyGroup(string key)
{
Key = key;
}
public AlphaKeyGroup(IGrouping<string, T> grouping)
{
Key = grouping.Key;
this.AddRange(grouping);
}
/// <summary>
/// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
/// </summary>
/// <param name="slg">The </param>
/// <returns>Theitems source for a LongListSelector</returns>
private static List<AlphaKeyGroup<T>> CreateGroups(SortedLocaleGrouping slg)
{
List<AlphaKeyGroup<T>> list = new List<AlphaKeyGroup<T>>();
foreach (string key in slg.GroupDisplayNames)
{
if (key == "...")
{
list.Add(new AlphaKeyGroup<T>(GlobeGroupKey));
}
else
{
list.Add(new AlphaKeyGroup<T>(key));
}
}
return list;
}
/// <summary>
/// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
/// </summary>
/// <param name="items">The items to place in the groups.</param>
/// <param name="ci">The CultureInfo to group and sort by.</param>
/// <param name="getKey">A delegate to get the key from an item.</param>
/// <param name="sort">Will sort the data if true.</param>
/// <returns>An items source for a LongListSelector</returns>
public static List<AlphaKeyGroup<T>> CreateGroups(IEnumerable<T> items, CultureInfo ci, GetKeyDelegate getKey, bool sort)
{
SortedLocaleGrouping slg = new SortedLocaleGrouping(ci);
List<AlphaKeyGroup<T>> list = CreateGroups(slg);
foreach (T item in items)
{
int index = 0;
if (slg.SupportsPhonetics)
{
//check if your database has yomi string for item
//if it does not, then do you want to generate Yomi or ask the user for this item.
//index = slg.GetGroupIndex(getKey(Yomiof(item)));
}
else
{
index = slg.GetGroupIndex(getKey(item));
}
if (index >= 0 && index < list.Count)
{
list[index].Add(item);
}
}
if (sort)
{
foreach (AlphaKeyGroup<T> group in list)
{
group.Sort((c0, c1) => { return ci.CompareInfo.Compare(getKey(c0), getKey(c1)); });
}
}
return list;
}
}
The FilmatesInGroup.cs and FilmatesByName.cs is the same as PeopleInGroup.cs and PeopleByFirstName.cs in the PhoneToolKit example with the names adapted.
The longlistFilmates LongListSelector Object is inserted directly inside the PivotItem no.3 ( no Grid and no ScrollView )
Thanks in advance for any help!

Overriding properties of abstract class

ODS List is a collection of abstract classes that implement a filtered select method for a paged/sorted object datasource. I have defined three absract classes that represent the filter, the returned data and the methods that produce these results:
[Serializable]
public abstract class ListFilter
{
// Define filter properties
}
[Serializable]
public abstract class ListData
{
// Define properties to be returned from the query
}
public abstract class ListMethods
{
public int ListCount(ListFilter filter)
{
var rows = listQuery();
rows = listFilter(rows, filter);
return rows.Count();
}
/// <summary>
/// List returns a page of data
/// </summary>
/// <param name="filter"></param>
/// <param name="sortType"></param>
/// <param name="startRowIndex"></param>
/// <param name="maximumRows"></param>
/// <returns></returns>
public IEnumerable<ListData> List(ListFilter filter, string sortType, int startRowIndex, int maximumRows)
{
var rows = listQuery();
rows = listFilter(rows, filter);
rows = listSort(rows, sortType);
return rows.Distinct().Skip(startRowIndex).Take(maximumRows).ToList();
}
public abstract IQueryable<ListData> listQuery();
public virtual IQueryable<ListData> listFilter(IQueryable<ListData> rows, ListFilter filter)
{
return rows;
}
public virtual IQueryable<ListData> listSort(IQueryable<ListData> rows, string sortType)
{
bool sortDescending = false;
if (!string.IsNullOrEmpty(sortType))
{
string[] values = sortType.Split(' ');
sortType = values[0];
if (values.Length > 1)
{
sortDescending = values[1] == "DESC";
}
}
if (!string.IsNullOrEmpty(sortType))
{
if (sortDescending)
{
rows = rows.OrderBy(sortType + " DESC");
}
else
{
rows = rows.OrderBy(sortType);
}
}
return rows;
}
}
My implementation hits a problem when I try to cast the ListData to the explicit returned data.
[Serializable]
public class EmployeeData : ODSList.ListData
{
public int EmployeeId { get; set; }
public int? ReportsToId { get; set; }...
}
public override IQueryable<ListData> listQuery()
{
var dc = new NorthwindDataContext();
var allrows = from emp in dc.Employees
select new EmployeeData()
{
EmployeeId = emp.EmployeeID,
ReportsToId = emp.ReportsTo, ...
};
return (IQueryable<ListData>)allrows; <-- PROBLEM ENCOUNTERED HERE
}
The diagnostic I hit is:
Unable to cast object of type 'System.Data.Linq.DataQuery1[BusinessLayer.EmployeeData]' to type 'System.Linq.IQueryable1[ODSList.ListData]'.
What version of .Net are you using? If you're on a version earlier than 4.0 the types specified in a generic interface are invariant, meaning you can't cast from IQueryable to IQueryable.
Check out the MSDN article here.
edit: After a bit of experimenting and finding this SO post, I found that something like the following should work for you:
public override IQueryable<ListData> listQuery()
{
var dc = new NorthwindDataContext();
var allrows = from emp in dc.Employees
select new EmployeeData()
{
EmployeeId = emp.EmployeeID,
ReportsToId = emp.ReportsTo, ...
} as ListData;
return allrows.OfType<ListData>();
}
You can try
return allrows.AsQueryable();
and I'm not sure but you may need to do
return allrows.Cast<ListData>().AsQueryable(); // if EmployeeData inherits from ListData; it is not required.
// but i'm not sure if ListData is the same as ODSList.ListData

Sending own data collection in WCF

I have own collection DataContracts:
[DataContract]
public class DzieckoAndOpiekun
{
public DzieckoAndOpiekun() { }
public DzieckoAndOpiekun(string dzImie, string dzNazwisko, string opImie, string opNazwisko)
{
this.DzieckoImie = dzImie;
this.DzieckoImie = dzNazwisko;
this.OpiekunImie = opImie;
this.OpiekunNazwisko = opNazwisko;
}
/// <summary>
/// Field for Dziecko's Surname
/// </summary>
private string dzieckoNazwisko;
/// <summary>
/// Field for Dziecko's Name
/// </summary>
private string dzieckoImie;
/// <summary>
/// Field for Opiekun's Surname
/// </summary>
private string opiekunNazwisko;
/// <summary>
/// Field for Opiekun's Name
/// </summary>
private string opiekunImie;
/// <summary>
/// Gets or sets the dziecko nazwisko.
/// </summary>
/// <value>The dziecko nazwisko.</value>
[DataMember]
public virtual string DzieckoNazwisko
{
get { return this.dzieckoNazwisko; }
set { this.dzieckoNazwisko = value; }
}
/// <summary>
/// Gets or sets the dziecko imie.
/// </summary>
/// <value>The dziecko imie.</value>
[DataMember]
public virtual string DzieckoImie
{
get { return this.dzieckoImie; }
set { this.dzieckoImie = value; }
}
/// <summary>
/// Gets or sets the opiekun nazwisko.
/// </summary>
/// <value>The opiekun nazwisko.</value>
[DataMember]
public virtual string OpiekunNazwisko
{
get { return this.opiekunNazwisko; }
set { this.opiekunNazwisko = value; }
}
/// <summary>
/// Gets or sets the opiekun imie.
/// </summary>
/// <value>The opiekun imie.</value>
[DataMember]
public virtual string OpiekunImie
{
get { return this.opiekunImie; }
set { this.opiekunImie = value; }
}
}
[DataContract]
public class DzieckoAndOpiekunCollection : IEnumerable<DzieckoAndOpiekun>, IList<DzieckoAndOpiekun>
{
[DataMember]
List<DzieckoAndOpiekun> collection = new List<DzieckoAndOpiekun>();
[DataMember]
public List<DzieckoAndOpiekun> Collection
{
get { return collection; }
set { collection = value; }
}
public IEnumerator<DzieckoAndOpiekun> GetEnumerator()
{
return collection.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new System.NotImplementedException();
}
public int IndexOf(DzieckoAndOpiekun item)
{
return collection.IndexOf(item);
}
public void Insert(int index, DzieckoAndOpiekun item)
{
collection.Insert(index, item);
}
public void RemoveAt(int index)
{
collection.RemoveAt(index);
}
public DzieckoAndOpiekun this[int index]
{
get
{
return collection[index];
}
set
{
collection[index] = value;
}
}
public void Add(DzieckoAndOpiekun item)
{
this.collection.Add(item);
}
public void AddRange(IList<DzieckoAndOpiekun> collection)
{
this.collection.AddRange(collection);
}
public void Clear()
{
collection.Clear();
}
public bool Contains(DzieckoAndOpiekun item)
{
return collection.Contains(item);
}
public void CopyTo(DzieckoAndOpiekun[] array, int arrayIndex)
{
this.collection.CopyTo(array, arrayIndex);
}
public int Count
{
get { return this.collection.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(DzieckoAndOpiekun item)
{
return this.collection.Remove(item);
}
}
And use it in my ServiceContract implementation:
public DzieckoAndOpiekunCollection GetChildAndOpiekunByFirstnameLastname(string firstname, string lastname)
{
DataTransfer.ChargeInSchoolEntities db = new DataTransfer.ChargeInSchoolEntities();
DzieckoAndOpiekunCollection result = new DzieckoAndOpiekunCollection();
if (firstname == null && lastname != null)
{
IQueryable<DzieckoAndOpiekun> resultV = from p in db.Dziecko
where lastname == p.Nazwisko
select new DzieckoAndOpiekun(
p.Imie,
p.Nazwisko,
p.Opiekun.Imie,
p.Opiekun.Nazwisko);
List<DzieckoAndOpiekun> l = resultV.ToList();
result.AddRange(l);
}
if (firstname != null && lastname != null)
{
IQueryable<DzieckoAndOpiekun> resultV = from p in db.Dziecko
where firstname == p.Imie && lastname == p.Nazwisko
select new DzieckoAndOpiekun(
p.Imie,
p.Nazwisko,
p.Opiekun.Imie,
p.Opiekun.Nazwisko);
List<DzieckoAndOpiekun> l = resultV.ToList();
result.AddRange(l);
}
if (firstname != null && lastname == null)
{
IQueryable<DzieckoAndOpiekun> resultV = from p in db.Dziecko
where firstname == p.Imie
select new DzieckoAndOpiekun(
p.Imie,
p.Nazwisko,
p.Opiekun.Imie,
p.Opiekun.Nazwisko);
List<DzieckoAndOpiekun> l = resultV.ToList();
result.AddRange(l);
}
return result;
}
Do I something wrong in creating own data contract and sending it ? I ask cause I have errors when consuming method :/ :
An exception occurred during the operation, making the result invalid.
Check InnerException for exception
details.
at
System.ComponentModel.AsyncCompletedEventArgs.RaiseExceptionIfNecessary()
at
SecretaryAppNav.ClientService.GetChildAndOpiekunByFirstnameLastnameCompletedEventArgs.get_Result() at
SecretaryAppNav.Views.FindChild.Client_GetChildAndOpiekunByFirstnameLastnameCompleted(Object
sender,
GetChildAndOpiekunByFirstnameLastnameCompletedEventArgs
e)
at SecretaryAppNav.ClientService.Service1Client.OnGetChildAndOpiekunByFirstnameLastnameCompleted(Object
state)
I can't guarantee that this would work, but you are sending the collection twice:
[DataContract]
public class DzieckoAndOpiekunCollection :
IEnumerable<DzieckoAndOpiekun>, IList<DzieckoAndOpiekun>
{
[DataMember]
List<DzieckoAndOpiekun> collection = new List<DzieckoAndOpiekun>();
[DataMember]
public List<DzieckoAndOpiekun> Collection
{
get { return collection; }
set { collection = value; }
}
...
You should probably only place the DataMember attribute on the field or the property, but not both, since they are exposing the same value.
I solve the problem with CollectionDataContract attribute ;)

System.Version not serialized

I've got a class with a System.Version property, which looks like this:
Version
Build: 111
Major: 1
MajorRevision: 0
Minor: 1
MinorRevision: 10
Revision: 10
When I serialize the class, version is always empty:
<Version />
The Client class looks like:
[Serializable]
public class Client
{
public string Description;
public string Directory;
public DateTime ReleaseDate;
public Version Version;
}
System.Version is not serializable, if you look at it's properties on MSDN, you'll see they have no setters...so the serializer won't store them. However, this approach still works. That article (old but still works) provides a Version class that is serializable, can you switch to that and get going?
Edit by tomfanning
I have fished the code from the dead site out of archive.org, reproduced below.
using System;
using System.Globalization;
namespace CubicOrange.Version
{
/// <summary>
/// Serializable version of the System.Version class.
/// </summary>
[Serializable]
public class ModuleVersion : ICloneable, IComparable
{
private int major;
private int minor;
private int build;
private int revision;
/// <summary>
/// Gets the major.
/// </summary>
/// <value></value>
public int Major
{
get
{
return major;
}
set
{
major = value;
}
}
/// <summary>
/// Gets the minor.
/// </summary>
/// <value></value>
public int Minor
{
get
{
return minor;
}
set
{
minor = value;
}
}
/// <summary>
/// Gets the build.
/// </summary>
/// <value></value>
public int Build
{
get
{
return build;
}
set
{
build = value;
}
}
/// <summary>
/// Gets the revision.
/// </summary>
/// <value></value>
public int Revision
{
get
{
return revision;
}
set
{
revision = value;
}
}
/// <summary>
/// Creates a new <see cref="ModuleVersion"/> instance.
/// </summary>
public ModuleVersion()
{
this.build = -1;
this.revision = -1;
this.major = 0;
this.minor = 0;
}
/// <summary>
/// Creates a new <see cref="ModuleVersion"/> instance.
/// </summary>
/// <param name="version">Version.</param>
public ModuleVersion(string version)
{
this.build = -1;
this.revision = -1;
if (version == null)
{
throw new ArgumentNullException("version");
}
char[] chArray1 = new char[1] { '.' };
string[] textArray1 = version.Split(chArray1);
int num1 = textArray1.Length;
if ((num1 < 2) || (num1 > 4))
{
throw new ArgumentException("Arg_VersionString");
}
this.major = int.Parse(textArray1[0], CultureInfo.InvariantCulture);
if (this.major < 0)
{
throw new ArgumentOutOfRangeException("version", "ArgumentOutOfRange_Version");
}
this.minor = int.Parse(textArray1[1], CultureInfo.InvariantCulture);
if (this.minor < 0)
{
throw new ArgumentOutOfRangeException("version", "ArgumentOutOfRange_Version");
}
num1 -= 2;
if (num1 > 0)
{
this.build = int.Parse(textArray1[2], CultureInfo.InvariantCulture);
if (this.build < 0)
{
throw new ArgumentOutOfRangeException("build", "ArgumentOutOfRange_Version");
}
num1--;
if (num1 > 0)
{
this.revision = int.Parse(textArray1[3], CultureInfo.InvariantCulture);
if (this.revision < 0)
{
throw new ArgumentOutOfRangeException("revision", "ArgumentOutOfRange_Version");
}
}
}
}
/// <summary>
/// Creates a new <see cref="ModuleVersion"/> instance.
/// </summary>
/// <param name="major">Major.</param>
/// <param name="minor">Minor.</param>
public ModuleVersion(int major, int minor)
{
this.build = -1;
this.revision = -1;
if (major < 0)
{
throw new ArgumentOutOfRangeException("major", "ArgumentOutOfRange_Version");
}
if (minor < 0)
{
throw new ArgumentOutOfRangeException("minor", "ArgumentOutOfRange_Version");
}
this.major = major;
this.minor = minor;
this.major = major;
}
/// <summary>
/// Creates a new <see cref="ModuleVersion"/> instance.
/// </summary>
/// <param name="major">Major.</param>
/// <param name="minor">Minor.</param>
/// <param name="build">Build.</param>
public ModuleVersion(int major, int minor, int build)
{
this.build = -1;
this.revision = -1;
if (major < 0)
{
throw new ArgumentOutOfRangeException("major", "ArgumentOutOfRange_Version");
}
if (minor < 0)
{
throw new ArgumentOutOfRangeException("minor", "ArgumentOutOfRange_Version");
}
if (build < 0)
{
throw new ArgumentOutOfRangeException("build", "ArgumentOutOfRange_Version");
}
this.major = major;
this.minor = minor;
this.build = build;
}
/// <summary>
/// Creates a new <see cref="ModuleVersion"/> instance.
/// </summary>
/// <param name="major">Major.</param>
/// <param name="minor">Minor.</param>
/// <param name="build">Build.</param>
/// <param name="revision">Revision.</param>
public ModuleVersion(int major, int minor, int build, int revision)
{
this.build = -1;
this.revision = -1;
if (major < 0)
{
throw new ArgumentOutOfRangeException("major", "ArgumentOutOfRange_Version");
}
if (minor < 0)
{
throw new ArgumentOutOfRangeException("minor", "ArgumentOutOfRange_Version");
}
if (build < 0)
{
throw new ArgumentOutOfRangeException("build", "ArgumentOutOfRange_Version");
}
if (revision < 0)
{
throw new ArgumentOutOfRangeException("revision", "ArgumentOutOfRange_Version");
}
this.major = major;
this.minor = minor;
this.build = build;
this.revision = revision;
}
#region ICloneable Members
/// <summary>
/// Clones this instance.
/// </summary>
/// <returns></returns>
public object Clone()
{
ModuleVersion version1 = new ModuleVersion();
version1.major = this.major;
version1.minor = this.minor;
version1.build = this.build;
version1.revision = this.revision;
return version1;
}
#endregion
#region IComparable Members
/// <summary>
/// Compares to.
/// </summary>
/// <param name="obj">Obj.</param>
/// <returns></returns>
public int CompareTo(object version)
{
if (version == null)
{
return 1;
}
if (!(version is ModuleVersion))
{
throw new ArgumentException("Arg_MustBeVersion");
}
ModuleVersion version1 = (ModuleVersion)version;
if (this.major != version1.Major)
{
if (this.major > version1.Major)
{
return 1;
}
return -1;
}
if (this.minor != version1.Minor)
{
if (this.minor > version1.Minor)
{
return 1;
}
return -1;
}
if (this.build != version1.Build)
{
if (this.build > version1.Build)
{
return 1;
}
return -1;
}
if (this.revision == version1.Revision)
{
return 0;
}
if (this.revision > version1.Revision)
{
return 1;
}
return -1;
}
#endregion
/// <summary>
/// Equalss the specified obj.
/// </summary>
/// <param name="obj">Obj.</param>
/// <returns></returns>
public override bool Equals(object obj)
{
if ((obj == null) || !(obj is ModuleVersion))
{
return false;
}
ModuleVersion version1 = (ModuleVersion)obj;
if (((this.major == version1.Major) && (this.minor == version1.Minor)) && (this.build == version1.Build) && (this.revision == version1.Revision))
{
return true;
}
return false;
}
/// <summary>
/// Gets the hash code.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
int num1 = 0;
num1 |= ((this.major & 15) << 0x1c);
num1 |= ((this.minor & 0xff) << 20);
num1 |= ((this.build & 0xff) << 12);
return (num1 | this.revision & 0xfff);
}
/// <summary>
/// Operator ==s the specified v1.
/// </summary>
/// <param name="v1">V1.</param>
/// <param name="v2">V2.</param>
/// <returns></returns>
public static bool operator ==(ModuleVersion v1, ModuleVersion v2)
{
return v1.Equals(v2);
}
/// <summary>
/// Operator >s the specified v1.
/// </summary>
/// <param name="v1">V1.</param>
/// <param name="v2">V2.</param>
/// <returns></returns>
public static bool operator >(ModuleVersion v1, ModuleVersion v2)
{
return (v2 < v1);
}
/// <summary>
/// Operator >=s the specified v1.
/// </summary>
/// <param name="v1">V1.</param>
/// <param name="v2">V2.</param>
/// <returns></returns>
public static bool operator >=(ModuleVersion v1, ModuleVersion v2)
{
return (v2 <= v1);
}
/// <summary>
/// Operator !=s the specified v1.
/// </summary>
/// <param name="v1">V1.</param>
/// <param name="v2">V2.</param>
/// <returns></returns>
public static bool operator !=(ModuleVersion v1, ModuleVersion v2)
{
return (v1 != v2);
}
/// <summary>
/// Operator <s the specified v1.
/// </summary>
/// <param name="v1">V1.</param>
/// <param name="v2">V2.</param>
/// <returns></returns>
public static bool operator <(ModuleVersion v1, ModuleVersion v2)
{
if (v1 == null)
{
throw new ArgumentNullException("v1");
}
return (v1.CompareTo(v2) < 0);
}
/// <summary>
/// Operator <=s the specified v1.
/// </summary>
/// <param name="v1">V1.</param>
/// <param name="v2">V2.</param>
/// <returns></returns>
public static bool operator <=(ModuleVersion v1, ModuleVersion v2)
{
if (v1 == null)
{
throw new ArgumentNullException("v1");
}
return (v1.CompareTo(v2) <= 0);
}
/// <summary>
/// Toes the string.
/// </summary>
/// <returns></returns>
public override string ToString()
{
if (this.build == -1)
{
return this.ToString(2);
}
if (this.revision == -1)
{
return this.ToString(3);
}
return this.ToString(4);
}
/// <summary>
/// Toes the string.
/// </summary>
/// <param name="fieldCount">Field count.</param>
/// <returns></returns>
public string ToString(int fieldCount)
{
object[] objArray1;
switch (fieldCount)
{
case 0:
{
return string.Empty;
}
case 1:
{
return (this.major.ToString());
}
case 2:
{
return (this.major.ToString() + "." + this.minor.ToString());
}
}
if (this.build == -1)
{
throw new ArgumentException(string.Format("ArgumentOutOfRange_Bounds_Lower_Upper {0},{1}", "0", "2"), "fieldCount");
}
if (fieldCount == 3)
{
objArray1 = new object[5] { this.major, ".", this.minor, ".", this.build };
return string.Concat(objArray1);
}
if (this.revision == -1)
{
throw new ArgumentException(string.Format("ArgumentOutOfRange_Bounds_Lower_Upper {0},{1}", "0", "3"), "fieldCount");
}
if (fieldCount == 4)
{
objArray1 = new object[7] { this.major, ".", this.minor, ".", this.build, ".", this.revision };
return string.Concat(objArray1);
}
throw new ArgumentException(string.Format("ArgumentOutOfRange_Bounds_Lower_Upper {0},{1}", "0", "4"), "fieldCount");
}
}
}
I prefer to use approach below, so I have only one property with Type VersionXml in my class. Implicit operators are really useful here.
[Serializable]
[XmlType("Version")]
public class VersionXml
{
public VersionXml()
{
this.Version = null;
}
public VersionXml(Version Version)
{
this.Version = Version;
}
[XmlIgnore]
public Version Version { get; set; }
[XmlText]
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
public string Value
{
get { return this.Version == null ? string.Empty : this.Version.ToString(); }
set
{
Version temp;
Version.TryParse(value, out temp);
this.Version = temp;
}
}
public static implicit operator Version(VersionXml VersionXml)
{
return VersionXml.Version;
}
public static implicit operator VersionXml(Version Version)
{
return new VersionXml(Version);
}
public override string ToString()
{
return this.Value;
}
}
You can use string proxy property:
[XmlIgnore]
public System.Version Version { get; set; }
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
public string version
{
get
{
if (this.Version == null)
return string.Empty;
else
return this.Version.ToString();
}
set
{
if(!String.IsNullOrEmpty(value))
this.Version = new Version(value);
}
}
You need to define your get and set accessors, as:
public class Version
{
public int Build { get; set; }
public int Major { get; set; }
public int MajorRevision { get; set; }
public int Minor { get; set; }
public int MinorRevision { get; set; }
public int Revision { get; set; }
}
// ...
new XmlSerializer(typeof (Version))
.Serialize(Console.Out,
new Version()
{
Build = 111,
Major = 1,
MajorRevision = 0,
Minor = 1,
MinorRevision = 10,
Revision = 10
}
);
I got this output:
<?xml version="1.0" encoding="ibm850"?>
<Version xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Build>111</Build>
<Major>1</Major>
<MajorRevision>0</MajorRevision>
<Minor>1</Minor>
<MinorRevision>10</MinorRevision>
<Revision>10</Revision>
</Version>
More precisely, System.Version is not XML-serializable. It is serializable with a BinaryFormatter, for example:
Version version = new Version(1, 2, 3, 4);
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, version);
stream.Position = 0;
Version deserialized = (Version)formatter.Deserialize(stream);
}
I have cleaned up Nick Craver's implementation to be much more concise and readable, added XML Serialization support (with attributes instead of separate elements), implemented a string TypeConverter as well as fixed a couple comparison issues that had the possibility for NullReferenceExceptions or infinite loops (calling the overloaded operators from within themselves).
I also used some C# 6 features as well.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Xml.Serialization;
[Serializable]
[TypeConverter(typeof(VersionCodeConverter))]
public class VersionCode : ICloneable, IComparable<VersionCode>, IEquatable<VersionCode>
{
[XmlAttribute] public int Major { get; private set; }
[XmlAttribute] public int Minor { get; private set; }
[XmlAttribute] public int Build { get; private set; } = -1;
[XmlAttribute] public int Revision { get; private set; } = -1;
public VersionCode() { }
public VersionCode(int major, int minor, int build = 0, int revision = 0)
{
if (major < 0)
throw new ArgumentOutOfRangeException(nameof(major), $"{nameof(major)} cannot be less than 0");
if (minor < 0)
throw new ArgumentOutOfRangeException(nameof(minor), $"{nameof(minor)} cannot be less than 0");
if (build < 0)
throw new ArgumentOutOfRangeException(nameof(build), $"{nameof(build)} cannot be less than 0");
if (revision < 0)
throw new ArgumentOutOfRangeException(nameof(revision), $"{nameof(revision)} cannot be less than 0");
Major = major;
Minor = minor;
Build = build;
Revision = revision;
}
public VersionCode(string version)
{
if (version == null)
throw new ArgumentNullException(nameof(version));
var components = new Stack<string>(version.Split('.'));
if (components.Count < 2 || components.Count > 4)
throw new ArgumentException(nameof(version));
Major = int.Parse(components.Pop(), CultureInfo.InvariantCulture);
if (Major < 0)
throw new ArgumentOutOfRangeException(nameof(version), $"{nameof(Major)} cannot be less than 0");
Minor = int.Parse(components.Pop(), CultureInfo.InvariantCulture);
if (Minor < 0)
throw new ArgumentOutOfRangeException(nameof(version), $"{nameof(Minor)} cannot be less than 0");
if (!components.Any())
return;
Build = int.Parse(components.Pop(), CultureInfo.InvariantCulture);
if (Build < 0)
throw new ArgumentOutOfRangeException(nameof(version), $"{nameof(Build)} cannot be less than 0");
if (!components.Any())
return;
Revision = int.Parse(components.Pop(), CultureInfo.InvariantCulture);
if (Revision < 0)
throw new ArgumentOutOfRangeException(nameof(version), $"{nameof(Revision)} cannot be less than 0");
}
public object Clone()
=> new VersionCode(Major, Minor, Build, Revision);
public int CompareTo(VersionCode version)
=> Major != version.Major ? Major.CompareTo(version.Major)
: Minor != version.Minor ? Minor.CompareTo(version.Minor)
: Build != version.Build ? Build.CompareTo(version.Build)
: Revision.CompareTo(version.Revision);
public override bool Equals(object obj)
=> obj is VersionCode && Equals((VersionCode)obj);
public bool Equals(VersionCode version)
=> Major == version.Major
&& Minor == version.Minor
&& Build == version.Build
&& Revision == version.Revision;
public override int GetHashCode()
{
var hash = 0;
hash |= (Major & 15) << 0x1c;
hash |= (Minor & 0xff) << 20;
hash |= (Build & 0xff) << 12;
hash |= (Revision & 0xfff);
return hash;
}
public static bool operator ==(VersionCode v1, VersionCode v2)
=> ReferenceEquals(v1, null) ? ReferenceEquals(v2, null) : v1.Equals(v2);
public static bool operator !=(VersionCode v1, VersionCode v2)
=> !(v1 == v2);
public static bool operator >(VersionCode v1, VersionCode v2)
=> v2 < v1;
public static bool operator >=(VersionCode v1, VersionCode v2)
=> v2 <= v1;
public static bool operator <(VersionCode v1, VersionCode v2)
=> !ReferenceEquals(v1, null) && v1.CompareTo(v2) < 0;
public static bool operator <=(VersionCode v1, VersionCode v2)
=> !ReferenceEquals(v1, null) && v1.CompareTo(v2) <= 0;
public override string ToString()
=> Build < 0 ? $"{Major}.{Minor}"
: Revision < 0 ? $"{Major}.{Minor}.{Build}"
: $"{Major}.{Minor}.{Build}.{Revision}";
}
public class VersionCodeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
=> sourceType == typeof(string)
|| base.CanConvertFrom(context, sourceType);
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var version = value as string;
return version != null
? new VersionCode(version)
: base.ConvertFrom(context, culture, value);
}
}

Categories

Resources