in
Forums
Blogs
Files
Devexpress.Com
ClientCenter
Support Center
DevExpress Channel

eXpress App Framework Team

May 2006 - Posts

  • Using your own value types

    When we plan our work on the whiteboard, we write down work estimations in the form of hours, days or weeks of work: "1w", "2.5d", "4h". Now I have to write an application that supports this kind of estimation. So I thought it would be good to have a special value type called Effort:

        [TestFixture]
        public class EffortTests {
            [Test]
            public void TestParse() {
                Assert.AreEqual(5 * 8, new Effort("1w").Hours);
                Assert.AreEqual(4, new Effort("0.5d").Hours);
                Assert.AreEqual(5 * 8 + 1, new Effort("1w1h").Hours);
                Assert.AreEqual(0, new Effort("1.2.3h").Hours);
            }
            [Test]
            public void TestToString() {
                Assert.AreEqual("1w", new Effort(5 * 8).ToString());
                Assert.AreEqual("2d", new Effort(16).ToString());
                Assert.AreEqual("1w1h", new Effort(5 * 8 + 1).ToString()); 
            }
        }
    


    Here is the code to make these tests work:

        public struct Effort {
            private decimal hours;
    
            public Effort(string value) {
                this.hours = Parse(value);
            }
            public Effort(int hours) {
                this.hours = hours;
            }
            public decimal Hours {
                get { return hours; }
                set { hours = value; }
            }
            private static decimal workDayHours = 8.0m;
            public static decimal WorkDayHours {
                get { return workDayHours; }
                set { workDayHours = value; }
            }
            private static decimal workWeekHours = 40.0m;
            public static decimal WorkWeekHours {
                get { return workWeekHours; }
                set { workWeekHours = value; }
            }
    
            private static Regex regex = new Regex(@"((?'weeks'\d+(\.\d+)?)w)?((?'days'\d+(\.\d+)?)d)?((?'hours'\d+(\.\d+)?)h)?");
            private static decimal Parse(string value) {
                decimal result = 0;
                Match m = regex.Match(value);
                if (m.Groups["weeks"].Value != String.Empty) {
                    result += decimal.Parse(m.Groups["weeks"].Value) * WorkWeekHours; 
                }
                if (m.Groups["days"].Value != String.Empty) {
                    result += decimal.Parse(m.Groups["days"].Value) * WorkDayHours;
                }
                if (m.Groups["hours"].Value != String.Empty) {
                    result += decimal.Parse(m.Groups["hours"].Value);
                }
                return result;
            }
            public override string ToString() {
                string result = "";
                int w = (int)Math.Floor(hours / WorkWeekHours);
                if (w > 0) {
                    result += w.ToString("d") + "w";
                }
                int d = (int)Math.Floor((hours - w * WorkWeekHours) / WorkDayHours);
                if (d > 0) {
                    result += d.ToString("d") + "d";
                }
                decimal h = hours - w * WorkWeekHours - d * WorkDayHours;
                if (h > 0) {
                    result += h.ToString("n0") + "h";
                }
                return result;
            }
        }
    

    While it is normally a bad idea to make value types mutable, here we need a setter for the Hours property for XPO to be able to restore it from the database.

    To use a value type in the persistent XPO class, you have to use the Persistent attribute:

        public class Task : BaseObject {
            ...
            private Effort estimatedWork;
            [Persistent]
            public Effort EstimatedWork {
                get { return estimatedWork; }
                set {
                    estimatedWork = value;
                    OnChanged();
                }
            }
            ...
        }
    

    Now you can persist Effort type values in the database, but EAF still has no idea what kind of UI control to create for such types.
    I will create a Web property editor for the Effort type:

    ...
    using DevExpress.ExpressApp;
    using DevExpress.ExpressApp.Editors;
    using DevExpress.ExpressApp.Web.Editors;
    
    
    ...
        [PropertyEditor(typeof(Effort))]
        public class WebEffortEditor : WebStringPropertyEditor {
            public WebEffortEditor(Type objectType, DictionaryNode info) : base(objectType, info) {
            }
            protected override object GetControlValue() {
                return new Effort(Editor.Text);
            }
        }

    This editor will be discovered and used automatically by EAF as long as the module is loaded.

    Posted May 03 2006, 01:38 AM by Roman Eremin with no comments
    Filed under:
Copyright © 1998-2008 Developer Express Inc.
ALL RIGHTS RESERVED