eXpress App Framework Team

This Blog

March 2008 - Posts

  • Partially valid data - followup on the comments

    Thanks to everybody for your comments on my post Partially valid business objects, they are most appreciated.

    First I need that I was probably not very clear on my own intent in that first post. At this stage, while I was reviewing the suggestion for inclusion in our release plans, my primary intent is to find out whether we are actually missing some important use case, either around validation (as the suggestion hinted) or on its own. The result is no, we're not missing anything important - in other words, there's room for improvement as opposed to a glaring omission.

    Here's what I'm taking away from the discussion:

    1. There doesn't appear to be a use case that can't be solved by using flags in the object. In fact, in all the cases that were mentioned, this seems to be the easiest and most effective solution.
    2. It is of course possible to write a controller to perform some sort of check over data and inform the end user about certain "special states" that are important outside the "real" data validation process. It would be nice to simplify this process and that would be a feature that comes closest to the original suggestion. At the same time I stand by my original comments - in these cases a warning that appears when the user enters the data is just the tip of the iceberg. I'll consider adding this feature in the future.
    3. Many applications could benefit greatly from workflow support, since it seems to be the rule rather than the exception that processes are handled in multiple steps, often by more than one person. This is not news to us - not all of you have probably been around for long enough, but during the pre-release phase we had a release or two that included our Windows Workflow Foundation integration. Unfortunately we had to push that feature away because its completion would have taken too much time. WF integration is now planned only for 2008.3, since it depends on a few other things that won't be completed before then.
  • Partially valid business objects

    This suggestion made it into our user story planning for XAF 8.2:

    There are times when one wants to inform the user of some sort of problem with the business logic, but an error (preventing a save of the object) may be too strict. It would be beneficial to show the user a warning so that the user can have his/her attention drawn to it, but not be prevented from saving the object.

    I was thinking about this and right now I'm not sure if we're going to do it - probably not for 8.2, I guess, since it definitely needs further consideration. Anyway, I thought it would be good to blog about this, to explain my thoughts and perhaps get some additional feedback on it.

    First I was trying to come up with a scenario that fits the description given in the issue. The one thing that came to my mind was an ordering scenario: a customer gives the phone operator a few details about an order, but not all of them, since they want to complete the order at a later point, they are undecided or whatever. Now, when the Order object has already been created, but (for example) no OrderLines have been filled in, this might be regarded as a "partially valid" state, which satisfies the description above. This scenario is quite representative in some parts, but not quite so in some others... read on.

    My initial reaction to that problem is that if the "partially valid" situation is expected in the workflow of the application user, then I would solve it in a different way. I have two ideas about this:

    1. I could add an "Incomplete" flag to the order object
    2. I could store data for incomplete orders in a different type - "IncompleteOrder" comes to mind. For usability reasons, I would implement a few actions in XAF that allow the user to start writing an Order in the Order detail form, then change his mind to save it as an IncompleteOrder instead. Or the other way round of course - convert an existing IncompleteOrder into an Order object when the customer has made his mind.

    In spite of these arguments I pondered the idea of integrating with Validation further. If I was assuming that the "partially invalid" state was expected, what use would it be to show a warning to the user when saving? Most probably the user would already be aware of the state at that point. Displaying a warning would really make sense if the user was surprised by the fact that his data didn't satisfy all validity requirements. Or rather, not all of them - just those that are "not quite as important" as the "real" requirements. I tried to come up with a scenario that fits this new description, but I couldn't. If you can, please let me know - perhaps there's something entirely new to the whole story when given a real-world example.

    Now, assuming we implemented a variety of the Validation process that would allow to check for partially valid state. The developer would implement some checking function that would find whether the state is satisfactory or not. I guess this resemblance to the way Validation works is really why Validation was originally mentioned in the issue. Would that work? I'm not convinced - in my scenario of Orders and OrderLines, what if the user has actually entered some OrderLines for the Order, but the customer still said to hold off with the order another day? How would the automated checking code be made aware of that fact?

    Great, so say we have a state checker and we trigger it in contexts similar to Validation - would that be sufficient? No, I don't think so. We could find out on Save that there's a partially valid state and show a warning of arguable relevance. But what then? Surely the user would be interested to find those partially invalid objects among all his others later, right? So we'd have to implement another block of new functionality that runs checks all the time and displays visual feedback in list views. But of course there would be other functionality in the application that deals with the partially invalid objects - one prominent example would be reports. So we'd have to find a way to integrate with that other functionality, basically perform some filtering based on certain state, but since the information about that state is not available from the object itself, it would require a "sideways" extension of the whole data binding functionality. Hm...

    So this is my point of view right now: implementing this based on Validation is a major problem, because it leaves lots of open questions and a minefield of problem cases. I can see that there are theoretically two different types of situations that involve partial validity, one of them in expected cases and the other in unexpected ones. I couldn't come up with an example for the unexpected case, so that point might be moot. Under any circumstances, the solution that involves a flag or a separate class seems a good one - not only is it pretty clean in the expected case, it also doesn't have any of the problems with application functionality that the Validation based solution seems to have. And the "flag" solution could even be used in unexpected cases - standard Validation can find (partially) invalid objects and deliver a message to the end user saying basically "your data is invalid, either correct it or check the Incomplete flag".

  • Doctors, Patients and all those other people - revision 2

    Yesterday I blogged about a particular business class modelling problem pertaining to people who can have different non-exclusive roles. You can find that article here.

    In the forum, Robert pointed out that some things could be improved about the UI of the sample:

    1. It shouldn't be possible to delete the standard roles that are created during application initialization
    2. It would be nice if the link dialog that is used to assign new roles to a person would only show those roles that haven't been assigned to that person yet

    I started working on suggestion number 1, and I found that I had to make a few changes to my sample to achieve an easy solution. It is possible to prevent deletion of objects on several different levels, and I decided to do it through permissions. Our security system also offers a different approach to preventing creation of new instances (different from the internal constructors I was using previously), so I decided to combine the two.

    Configuring type specific permissions is only possible in conjunction with our "Complex Security System" module, so I had to configure my application to use this first.

    Assigning permissions in the complex security system works by way of roles (you guessed it!), and I had one immediate problem with this: my Role class clashed with our existing Role class for that security system, so I renamed my class to PersonRole - a more appropriate name anyway.

    The complex security system always creates a role called "Default", and since I didn't want to dilute my sample any more than necessary, I simply used the (standard in new solutions) Active Directory Authentication Strategy and the Default role for my permission settings. This is the code I added to my UpdateDatabaseAfterUpdateSchema method to establish the permissions:

      Role defaultRole = Session.FindObject(
    new BinaryOperator("Name", "Default"));
    if (defaultRole == null)
    defaultRole = new Role(Session) { Name = "Default" };
    while (defaultRole.PersistentPermissions.Count > 0)
    Session.Delete(defaultRole.PersistentPermissions[0]);
    defaultRole.AddPermission(new ObjectAccessPermission(
    typeof(object), ObjectAccess.AllAccess));
    defaultRole.AddPermission(new ObjectAccessPermission(
    typeof(PersonRole), ObjectAccess.Delete, ObjectAccessModifier.Deny));
    defaultRole.AddPermission(new ObjectAccessPermission(
    typeof(PersonRole), ObjectAccess.Create, ObjectAccessModifier.Deny));
    defaultRole.Save( );

    This code either finds an existing Default role or otherwise just creates it. Then it clears any existing permissions from the role and adds those necessary to allow access to all objects, with the exception of Delete and Create rights for the PersonRole type.

    The final changes I made at this point were largely cosmetic. I changed the names of all methods to reflect the name change of the Role class, and I configured all my classes to have public constructors again, and the CreatePersonRoleIfNeeded method to use the simpler overload of the Activator.CreateInstance method.

    That's part 1 done - it is no longer possible to either create or delete PersonRole instances or those of derived types at runtime. (I'm sure somebody will very soon find something I missed... :-))

    Moving on to part 2. When bringing up the dialog to assign roles, it should only list the roles that haven't been assigned yet. Luckily, XAF supports several attributes that allow to influence the content of lookup collections. In this case I decided to use the DataSourcePropertyAttribute to fetch the content for the lookup as an entire separate collection. The code to create that separate collection is this:

    new XPCollection(
    PersistentCriteriaEvaluationBehavior.InTransaction,
    Session,
    new NotOperator(new ContainsOperator("RolePersons",
    new BinaryOperator("This", this))));

    I think it's pretty much self-explaining - a query is defined that fetches all those PersonRole that don't have a reference to the current RolePerson in their RolePersons collection. I was able to find that our Knowledge Base article A2404 describes this approach in more detail, if you're curious about it.

    There's one special option being used in this code, which is the PersistentCriteriaEvaluationBehavior.InTransaction flag. This is one of the use cases for this flag - since I want to see data that has already been changed in the current (non-committed) unit of work, I need to use this flag. Otherwise my query would return the status of configured roles outside the current unit of work, which obviously doesn't help me.

    Here's how this collection is assigned to be used as the lookup source for the Roles property:

      [Association("RolePerson-Roles")]
    [DataSourceProperty("AvailableRoles")]
    public XPCollection Roles {
    get {
    return GetCollection("Roles");
    }
    }

    [Browsable(false)]
    public XPCollection AvailableRoles {
    get {
    return new XPCollection(
    PersistentCriteriaEvaluationBehavior.InTransaction,
    Session,
    new NotOperator(new ContainsOperator("RolePersons",
    new BinaryOperator("This", this))));
    }
    }

    When I tested this code, I found that our dialog calls into the AvailableRoles property several times. The article I linked before therefore uses a variable to store the collection away for later reuse, so that the collection is only fetched once. Of course this would probably not be a major problem in this particular case, since it can probably be assumed that the number of available roles is not going to be huge. I still decided to make the code more efficient by storing away the collection.

    Now, the problem with this is that the collection is still going to change every time the Roles collection changes. So I had to hook into an event for the Roles collection to detect when it changes and reset my temporary variable from there. Only when the temporary variable is in its initial state do I refetch the collection. The resulting code is less concise than that above, which is why I decided not to show it here - have a look at the complete sample to see how this works.

    Right, that's it. The download of the new version is here: XAFRolesRev2.zip

    Note: before you run this sample, you should probably drop the database XAFRoles to have everything recreated. I have not tested what's going to happen if you run this on the basis of the other database and I have not changed the connection string.

    Again, I'm going to post to the forum about this in a few minutes. I've decided to move this sample forward as much as necessary - so please feel free to request any other changes or additions you would like to see.

  • Doctors, Patients, Mechanics and Tax Evaders

    In business applications, there's a situation we encounter regularly: a class hierarchy alone apparently doesn't allow us to model the complex reality of everyday life. Of course there's a lot to be said about this basic issue, but the problem I'd like to talk about here is the fact that an entity can "be" several things at once, in other words, it can have multiple roles.

    Doctors and Patients

    In our forums, an example was recently given involving people: assuming "people" are divided into three groups "Doctors", "Patients" and "all the rest", Oliver might be a Doctor and Dan might be a Patient. There's a chance that Oliver injures his arm drinking lots of Guinness and so becomes a Patient as well. On the other hand, Dan might go to med school and become a Doctor. A good example (although of course I would never drink so much Guinness ), and there are lots more: Dan could be a Mechanic in addition to being a Patient, and anybody who's a Doctor, a Patient and/or a Mechanic could be a TaxEvader.

    Given this example, it seems clear that being a Doctor or a Patient has to be modelled in a different way than other properties, like for instance being a Man or a Woman. Deriving classes for Man and Woman from Person and distinguishing the two this way obviously makes sense, but using the same approach for Doctor, Patient, Mechanic or TaxEvader is equally obviously not going to do the trick, since these are not mutually exclusive and thus don't fit a simple hierarchical model.

    Flags...

    A very simple idea to solve the problem, which was also mentioned in the forum thread, is to use a common class for Doctors and Patients - perhaps the Person class - and to introduce flags (boolean fields) that say whether a certain instance of that class is considered to be a Doctor or a Patient or both. For simple cases this is probably worth a consideration, but this doesn't look like the best idea as soon as you'd need a few more than just two such flags.

    ... or Roles

    It's not too hard to come up with a better way to implement this: roles, just like they are used in many security systems (the one delivered with XAF does this as well). I even used the word "roles" before, since it really seems to be a very useful non-technical language term to describe what Doctor, Patient, Mechanic and TaxEvader really are. A Person (or a Man or Woman, since they remain derived from Person) can get a bunch of roles assigned, which makes any combination possible. Of course there might be restrictions in reality pertaining to the combinations of roles that can be used - this would require some business logic code, based on the validation system in XAF. Since a Person can have any number of Roles and a Role can be assigned to any number of Person objects, there's naturally a many-to-many relationship here - see below for reasons to choose a different approach. XAF supports many-to-many relationships out of the box, so that there shouldn't be too much work required to get good UI functionality supporting this structure.

    What's in a Role?

    A question that remains is what exactly is actually in the Role? So far, the fact that a certain Role is assigned to a Person instance is simply a marker, denoting the Person object in question to be one of a certain kind. At this point it seems alright to just have a class Role with a Title property, and create a few instances of it with the different Titles "Doctor", "Patient" and so on. This might be a workable scenario for many cases, but sometimes there will be business logic associated with being a Doctor, for example, and so it may be a good idea to derive specific classes for each of the Role types, which can then add the necessary business functionality. On the other hand, this approach doesn't allow for new Roles to be created at runtime - it really depends on the requirements which approach is preferable.

    A third possible approach would be one where the Roles are used for data storage, either in addition to business logic or without it. That would mean that instead of creating a set of Roles that don't change at runtime - the many-to-many scenario above - one could configure a one-to-many relationship instead and instantiate new Role instances (or instances of the derived classes, if this is combined with the business logic approach) as they are assigned to the Person instances.

    As you can see, there are a lot of possible variations on this. In the end, the result is the same: a class structure that allows the Person to be associated with a collection of Roles. There's no limit to the number of Roles any given Person can have, and depending on requirements Roles can be created in application initialization code or at runtime. It is also easy to query Person instances that are associated with a particular Role.

    Basics are Basics

    At this point I would like to stress that so far the whole concept has not related to the UI in any way. Yes, I made a reference at some point saying that since XAF supports many-to-many out of the box, that makes it a good choice. But in any case this is a structure that is based solely on analysis of the situation and the requirements. When I started writing the test program that accompanies this article, I was pretty sure that XAF would either be able to deal with the structure out of the box, or would otherwise be easy to extend to deal with it. In theory, if I had found something that was actually impossible, I would have added a user story to our XAF plans for urgent consideration. The target is simple: if your business class structure is sound and follows good practices, it should be supported by XAF. We believe we've done a good job implementing this support up to this point, but we're always willing to learn - show us a business class structure that fits the description and that's not supported, and we'll try our best to help you!

    Right, moving on to some examples. Based on the thoughts above, I implemented the following classes (and a few others for the other roles):

      public class RolePerson : Person {
    public RolePerson(Session session) : base(session) { }

    [Association("RolePerson-Roles")]
    public XPCollection Roles {
    get { return GetCollection("Roles"); }
    }
    }

    [DefaultClassOptions]
    public class Woman : RolePerson {
    public Woman(Session session) : base(session) { }
    }

    [DefaultClassOptions]
    public class Man : RolePerson {
    public Man(Session session) : base(session) { }
    }

    public class Role : BaseObject {
    internal Role(Session session) : base(session) { }

    private string title;
    public string Title {
    get { return title; }
    set { SetPropertyValue("Title", ref title, value); }
    }

    private string description;
    public string Description {
    get { return description; }
    set { SetPropertyValue("Description", ref description, value); }
    }

    [Association("RolePerson-Roles")]
    public XPCollection RolePersons {
    get { return GetCollection("RolePersons"); }
    }
    }

    public class DoctorRole : Role {
    internal DoctorRole(Session session) : base(session) { }

    public override void AfterConstruction( ) {
    base.AfterConstruction( );
    base.Title = "Doctor";
    }
    }

    Also, in my Updater class I implemented the following methods to create the initial data (I'm going for the "separate classes for different roles, but no data storage" approach):

      public override void UpdateDatabaseAfterUpdateSchema( ) {
    base.UpdateDatabaseAfterUpdateSchema( );

    EnsureRoleSetup(Session);
    }

    private void CreateRoleIfNeeded(Session session, Type roleType) {
    Role role = (Role) Session.FindObject(roleType, null);
    if (role == null) {
    if (typeof(Role).IsAssignableFrom(roleType)) {
    Role newRole = (Role) Activator.CreateInstance(roleType,
    BindingFlags.CreateInstance | BindingFlags.NonPublic | BindingFlags.Instance,
    null, new object[] {session}, null);
    newRole.Save( );
    }
    }
    }

    private void EnsureRoleSetup(Session session) {
    CreateRoleIfNeeded(session, typeof(DoctorRole));
    CreateRoleIfNeeded(session, typeof(PatientRole));
    CreateRoleIfNeeded(session, typeof(MechanicRole));
    CreateRoleIfNeeded(session, typeof(TaxEvaderRole));
    }

    And that's it, pretty much.

    Of course it would be possible to create further UI around this functionality, such as special actions that assign certain roles automatically. One could also customize the UI to be a bit more end user friendly - perhaps the term Role is not the best for all types of applications (I do find it quite intuitive when talking about people, but in other use cases it might not fit so well). It's also thinkable to create an entire custom dialog for the purpose of assigning Roles, but given the standard implementation I think that's a bit over the top. I gave some extra thought to the issue of the UI since this was mentioned in the forum thread that triggered this research, but I don't find the standard implementation to be particularly lacking.

    I'm going to post a forum thread pointing to this blog post in a little while. Please direct any discussion that requires answers to the forum instead of the comments on this post, as it's easier to deal with structured discussion there.

    Finally, here's the download with the demo project I created: XAFRoles.zip

  • Reusable Domain Models strikes back

    This post may be outdated. For the latest Domain Components concepts and examples refer to the current online documentation.

    In my first post about reusable domain models, I was looking for an elegant, intuitive and code-centric way in which to create domain models from existing parts. Since then we've done additional research. And now, I’m ready to share our results. I will describe the approach we're going to implement. Please note that some details might be changed. So, you cannot rely on this article as on a specification.

    The main conclusion drawn from our research is that there is no elegant way to construct a domain model from existing parts. By an elegant and code-centric way, I mean the ability to look at domain class code and understand what’s going on. The approach we have chosen is that you provide logic and interfaces, and the framework builds an implementation for you.

    Let’s consider one small and well-know domain model – Address. Address usually contains Country, City, State/Province, postcode, street address (or line1, line2). There can be one or several addresses. My requirements include:

    1. I want to be able to apply this model to any domain objects I want (Customer, Company, Contact, Location, Airport, Gas Station – anything).
    2. I want the business logic related to this model to be maintained by its developer. For bug fixes, he would give me only an updated assembly.
    3. I don’t want this model to depend on any ORM specifics.
    4. I don’t want to write lots of code to use this model.

    With interfaces, I can make my domain model independent from my implementation. Here are simplistic Address interface definitions (please note that this code is just an illustration):

        public interface IAddress {
            IAddressable Owner { get; set; }
            string City { get; set; }
            string Line1 { get; set; }
            bool IsPrimary { get; set; }
        }
    
        public interface IAddressable {
            IAddress PrimaryAddress { get; }
            IList Addresses { get; } 
        }
    

    Together with these interfaces, I should supply business logic. Since interfaces cannot contain method definitions, the only thing I can use is some kind of extension methods in the service class:

        [Service(typeof(IAddresable))]
        public static class AddressableService:EntityService {
            public IAddress Get_PrimaryAddress(IAddressable self) {
                if (self.Addresses.Count > 0) {
                    return self.Addresses.Where(address => address.IsPrimary );
                } else {
                    IAddress addr = SomeObjectFactory.CreateObject();
                    addr.Owner = self;
                    addr.IsPrimary = true;
                    self.Addresses.Add(addr);
                    return addr;
                }
            }
        }
        [Service(typeof(IAddress))]
        public class AddressService:EntityService {
            private IAddress self;
            private bool isPrimary;
            private bool lockPrimary = false;
    
            public AddressService(IAddress self) {
                this.self = self;
            }
    
            public bool IsPrimary {
                get { return isPrimary; }
                set {
                    if (lockPrimary) return;
    
                    try {
                        lockPrimary = true;
                        if (IsPrimary) {
                            foreach (Address addr in self.Owner.Addresses) {
                                if (Address != this) {
                                    addr.IsPrimary = false;
                                }
                            }
                        }
                    } finally {
                        lockPrimary = false;
                    }
                }
            }
        }
    

    As a domain model developer, I don’t need to specify anything else. This code assumes that the implementation of interfaces can persist its properties, service methods are called in the corresponding implementation, and that there is SomeObjectFactory that can create an implementation with a given interface. This service class will be wired into a generated interface implementation using naming conventions and/or attributes.

    Even as a developer of a final application (that uses this Address model), I still don’t want to know how to implement a persistent property in the particular ORM I’m using. Or, if I do want to – I want to write it in one place, not in every single property getter and setter. I want to provide something like this:

        public class XPOPersistPropertyImpl:PropertyImplBase {
            private OfType value;
    
            public PersistPropertyImpl(string name):base(name) {
                this.name = name;
            }
            public  OfType GetValue(XPObject owner) {
                return owner.GetPropertyValue(Name);
            }
            public void SetValue(XPObject owner, OfType newValue) {
                owner.SetPropertyValue(Name, newValue);
            }
        }
    

    So, I can imagine the entire application as a superposition of several domain models (one of them is an addition for the final application itself). Assume that we have a library of domain models (IOrganization, IAddress-IAddressable, IContact, ICustomer-ISale-ISaleItem-IProduct-IAgent, IEmployee-IEmployer):

        public interface ICar : ISaleItem {
            public ICarModel Model { get; set; } //Implements ISaleItem.Product
            public string Color { get; set; }
            public DateTime MadeOn { get; set; }
        }
    
        public interface ICarModel : IProduct {
            string Name;
        }
    
        public class MyObjectSpace {
            public static MyObjectSpace() {
                RegisterEntity("Company", IOrganization, ICustomer, IAddressable);
                RegisterEntity("Contact", IContact, ICustomer, IAddressable);
                RegisterEntity("Dealership", IOrganization, IEmployer, IAddressable);
                RegisterEntity("SalesManager", IEmployee, IAgent);
                RegisterEntity("CarModel", ICarModel);
                RegisterEntity("Car", ICar);
                RegisterEntity("Sale", ISale);
                RegisterEntity("Address", IAddress);
            }
        }
    

    Who is going to implement all these interfaces? These implementations will be generated at runtime (we will probably provide other options in the future) using domain service classes, and any additional orthogonal services (like the persistent XPO property’s setter class, mentioned earlier).

    An additional benefit of the described approach is that we automatically use a “programming against interfaces” approach, which can give us benefits of aspect-oriented extensions. We can add field-level security, and audit features. The interface can be implemented by some proxy class. So, we can even create an application server with remote objects from the same code. A multi-tiered application is something many of our customers have asked for.

    If you only have interfaces, it is very easy to write unit tests for service classes. There are mock frameworks that will automatically generate interface implementations.

    There is a well-known metaphor of the Dependency Injection (DI) container that can be used here to handle the model creation, and later the resolution of dependencies. We are going to build our own DI container that is tailored to our specific needs, and at the same time, is pretty generic to be used in other parts of XAF, or in your own use cases (OK, frankly – just to make Rinat Abdullin stop complaining that we don’t use IoC in XAF Smile).

    A big problem here is indirection. Since you don’t have direct control over the implementation of your interfaces, it might be hard to diagnose problems. We should make some extra efforts to minimize side effects and make it easier to understand what’s going on.

    This is what we will work on after 2008 vol.1 release. I doubt that this technology will be implemented in 2008 vol.2. Our target is 2008 vol. 3. We are likely to release some preview bits in interim.

  • Developer Express at Basta in Germany

    I was at Basta conference in Germany last week. In between everything else I had to do at that conference, I found time to show Developer Express products to a lot of people. I did a lot of demos of XAF, as well as our other products like CodeRush and Refactor! and our .NET components. This was the biggest Basta Spring conference ever, and the exhibition area was very well attended.

    The feedback was great! Especially, people really liked the final version of XAF, with the Visual Studio integration taken to new levels. If you haven't seen it yourself yet, I suggest you go to the XAF area on our website and have a good look.

    I'd like to thank everybody I met there for their feedback and for being interested in our technology. And I want to apologize to those who saw me fight the huge green monster machine I had to borrow from my friend John, when my own laptop suddenly died - some usability aspects of that thing (especially its keyboard, which had a loose y-key) were hard to get used to.

LIVE CHAT

Chat is one of the many ways you can contact members of the DevExpress Team.
We are available Monday-Friday between 7:30am and 4:30pm Pacific Time.

If you need additional product information, write to us at info@devexpress.com or call us at +1 (818) 844-3383

FOLLOW US

DevExpress engineers feature-complete Presentation Controls, IDE Productivity Tools, Business Application Frameworks, and Reporting Systems for Visual Studio, along with high-performance HTML JS Mobile Frameworks for developers targeting iOS, Android and Windows Phone. Whether using WPF, ASP.NET, WinForms, HTML5 or Windows 10, DevExpress tools help you build and deliver your best in the shortest time possible.

Copyright © 1998-2017 Developer Express Inc.
All trademarks or registered trademarks are property of their respective owners