Here's a sample test class that we'll use to illustrate this problem:
public class TestClass
{
private SmartDate _mySmartDate = new SmartDate();
public SmartDate MySmartDate
{
get { return _mySmartDate; }
set { _mySmartDate = value; }
}
}
Here's a test script that illustrates the problem:
// This works great!
SmartDate d = new SmartDate();
d.Date = DateTime.Now;
// This does't work at all.
TestClass
// compile error for above line is this:
//Cannot modify the return value of 'TestClass.MySmartDate' because it is not a variable
// This does work.
TestClass tc = new TestClass();
tc.MySmartDate = new SmartDate(DateTime.Now);
It appears that the set logic in the property definition trumps your ability to modify properties of the property object.
Is this intended? Is there a work-around?
If that type of trick has to be done, wouldn't it be better to expose it as a string?
That way, an empty string would represent null. Otherwise, the DateTime class will expose null dates as some day in the 1700's, won't it?
There is also a Nullable(of T) generics class that you can use like
Private mNullableDate As Nullable(Of Date) = New Nullable(Of Date)
This allows you to represent the date as null. You need to subclass the DateTimePicker to allow binding to this type of object.
Honestly the whole point of SmartDate is for internal use so you can expose the value as a string. SmartDate understands the idea of an "empty" date, and that an empty string equates to an empty date.
If you don't need the concept of a date being empty, then you really don't need SmartDate in most cases. If a date is required, you can just use DateTime. If it can be null you can use Nullable<DateTime>.
But if you need the concept of "empty" (which is quite different from null), then SmartDate is your friend.
Also, if you want to expose a date value in a simple textbox so the user can type dates free-form, use parsing shortcuts like "Monday" or "Today" or "t" then again, SmartDate is your friend, because it handles all the string-to-and-from-date translation.
RockfordLhotka:Honestly the whole point of SmartDate is for internal use so you can expose the value as a string. SmartDate understands the idea of an "empty" date, and that an empty string equates to an empty date.
If you don't need the concept of a date being empty, then you really don't need SmartDate in most cases. If a date is required, you can just use DateTime. If it can be null you can use Nullable<DateTime>.
But if you need the concept of "empty" (which is quite different from null), then SmartDate is your friend.
Also, if you want to expose a date value in a simple textbox so the user can type dates free-form, use parsing shortcuts like "Monday" or "Today" or "t" then again, SmartDate is your friend, because it handles all the string-to-and-from-date translation.
Ok, you sold me on the approach of exposing the property as a string.
However, once we bind the collection to a datagrid, we want to allow the user to sort it.
And, of course, the datagrid sorts our dates and numbers as strings.
Is there an easy workaround for this?
Brian's solution is a good one.
A quick and dirty one is to have two properties for the date - one as a String and the other as a Date.
Bind the grid to the string property but sort by the date property.
Joe
Brian,
I had seen your ObjectListView before, but hadn't gotten around to trying it.
Very nice!
Pretty intuitive, and it seems to me much easier to use than the SortedBindingList and FilteredBindingList.
Thanks for the good work, and for the tip!
========================================
FYI, the Sort and Filter help text need some examples of usage, such as:
Sort:
"PropertyOne asc, PropertyFive desc"
Filter:
"PropertyOne >= 10 and PropertyFive like 'M%'"
We're looking at implementing the IExtendSort in our RuleBusinessBase class, which is our development subclass of BusinessBase.
We use the standard naming convention of "PropertyName" for a property and "_propertyName" for the corresponding private variable. We're hoping that we can deduce the private variable name and use reflection to get the value to return.
Advantages (we hope!):
Disadvantages (we hope not!):
Have you tried this? If not, we'll post our progress.
Brian Criswell:Interesting solution. It should work fine. I would recommend caching the property descriptors and field descriptors per object type.
Good idea! My colleague, who's taking it to the next step, just left for the day, but he felt pretty confident he would get it working in the morning. I'll pass on your suggestion.
I'm really excited for it to work, because that would become one less thing I would have to worry about going wrong, and therefore one less thing to test. :)
We moved the code up to our version of BusinessBase and it worked great.
So, the only thing we have to do in our business objects is to claim they implement the interface.
Would you mind sharing some code so we can see how it was done?
Thanks.
JoeFallon1:Would you mind sharing some code so we can see how it was done?
Thanks.
Not at all!
But the current version is company-specific and company-proprietary. :(
I have to redevelop it at home. I'm testing a new DotNetNuke module version this week, so it probably won't happen until next week.
Ok, I got bored at lunch. :)
This would go into your subclass of BusinessBase.
/// <summary>
/// All internal variables that are SmartFields (SmartDate, SmartInt16, etc.), if exposed as properties,
/// are exposed as strings. This makes binding extremely easy, but it also means that the default sorting
/// behavior is to sort those properties as strings. This routine works in conjunction with the sorting
/// capability provided by <see cref="ObjectListView"/> so that the objects are sorted
/// by their underlying datatypes, not their public string values.
///
/// Note:
///
/// If the property is not a string, it is assumed that the property itself should be used to sort by.
///
/// This implementation is based upon correctly following a simple naming convention. If a property is named
/// PropertyNameHere, it's underlying private variable will be named _propertyNameHere. If such a variable
/// is found we return an object of the appropriate private variable type. Otherwise, we
/// just return the value we were given.
/// </summary>
/// <param name="property">Contains a PropertyDescriptor for the property being sorted.</param>
/// <param name="value">Contains an object of the appropriate property type, containing that property's value.</param>
/// <returns>An object of the type we want to sort by, containing the value to sort by.</returns>
// We assume the property name starts with an uppercase letter, and that the corresponding private variable
// starts with an underscore and a lowercase version of the starting letter.
int len = property.Name.Length - 1;
// privateVariableName = the deduced corresponding private variable name.
string privateVariableName = "_"
+ (property.Name.ToCharArray()[0].ToString().ToLower())
+ property.Name.Substring(1, len);
// Check each variable name for a match.
foreach (FieldInfo f in fi)
{
// We have the correct private variable if the names match.
if (f.Name.Equals(privateVariableName))
{
// Return the underlying variable value.
return f.GetValue(this);
}
}
return value;
}
}
Thanks for sharing the code.
I see what you mean now.
Thanks.
Joe
Here is a VB version which assumes private variables are named: mPropertyName.
Public Function GetSortValue(ByVal [property] As System.ComponentModel.PropertyDescriptor, ByVal value As Object) As ObjectDim f As FieldInfo
For Each f In fi
If f.Name.Equals(privateVariableName) Then
Return f.GetValue(Me)
End If
Next f
Return value
End Function
This routine is added to your Base class that Inherits BusinessBase and it works in conjunction with the sorting
capability provided by <see cref="ObjectListView"/> so that the objects are sorted by their underlying datatypes, not their public string values.A BO that wants to use this feature should simply:
Implement IExtendSort
The ObjectListView class will then call this code automatically if it needs to sort properties on that BO.
Copyright (c) Marimer LLC