eXpress App Framework Team

This Blog

August 2011 - Posts

  • XAF- XPO Ask the Team Webinar–August

    This is a reminder post for today’s webinar. We are going to cover 3 main areas. Firstly we are going to demo how to extend our Workflow module in order to provide support for scheduled workflows. Then we are going to see how nested properties are handled by our enhanced FilterEditor. Finally we are going to explore a very early preview of a Middle Tier XAF application. In this preview only the security is implemented as a service, however the point of the demo is to show the general direction when working with an application server.

    The team and myself are really looking forward to speak to you this evening, so pop along and register now!

  • XAF Workflow persistence storage

    The .NET Framework 4 ships with the SQL Workflow Instance Store which allows workflows to persist state information about workflow instances in a SQL Server 2005 or SQL Server 2008 database. However its implementation is based on stored procedures which can be a bit scary if you are not familiar with them. In addition our customers may follow different standards (Oracle, Firebird, VistaDB, MySQL, PostgreSQL etc.) and it’s unrealistic to hire more people to support SQL Server infrastructure.

    Usually when dealing with these issues the first thing we do is to carry out a Google search for storage solutions. Surprisingly there is no such solution out there! Moreover there is very little useful code or samples available. Luckily XAF provides us with an easier route!

    XAF is the perfect workflow modeling environment. It provides a ready made solution for creating and deploying a server that will execute workflows as described here.  In order to start modeling workflows we can use VS design time along with our re-hosted runtime Workflow designer and custom WF4 activities. XAF also gives us increased control over our workflows for example through the ability to manually start workflows. Finally since XAF uses XPO to access data we can easily support 16 different database systems simply by providing a connection string!

    XPO Data Store Adapter XPO Data Store Adapter's Assembly Name Database Provider Assembly
    AccessConnectionProvider DevExpress.Xpo.vXXX System.Data.dll
    AdvantageConnectionProvider DevExpress.Xpo.vXXX.Providers Advantage.Data.Provider.dll 9.10.2.0
    AsaConnectionProvider DevExpress.Xpo.vXXX.Providers iAnywhere.Data.SQLAnywhere.dll 11.0.0.12642
    AseConnectionProvider DevExpress.Xpo.vXXX.Providers Sybase.Data.AseClient.dll 1.15.50.0
    DB2ConnectionProvider DevExpress.Xpo.vXXX.Providers IBM.Data.DB2.dll 9.5.2.2
    FirebirdConnectionProvider DevExpress.Xpo.vXXX.Providers FirebirdSql.Data.Firebird.dll 1.7.1.0
    FirebirdSql.Data.FirebirdClient.dll 2.5.1.0
    MSSqlConnectionProvider DevExpress.Xpo.vXXX System.Data.dll
    MSSqlCEConnectionProvider DevExpress.Xpo.vXXX.Providers System.Data.SqlServerCe.dll 3.5.0
    System.Data.SqlServerCe.dll 4.0.8482.1
    MySqlConnectionProvider DevExpress.Xpo.vXXX.Providers MySql.Data.dll 5.2.5.0
    OracleConnectionProvider DevExpress.Xpo.vXXX.Providers System.Data.OracleClient.dll 2.0.0.0
    Oracle.DataAccess.dll 9.2.0.700
    ODPConnectionProvider DevExpress.Xpo.vXXX.Providers Oracle.DataAccess.dll 10.1.0.200
    PervasiveSqlConnectionProvider DevExpress.Xpo.vXXX.Providers Pervasive.Data.SqlClient.dll 2.10.0.15
    PostgreSqlConnectionProvider DevExpress.Xpo.vXXX.Providers Npgsql.dll 2.0.11.0
    SQLiteConnectionProvider DevExpress.Xpo.vXXX.Providers System.Data.SQLite.dll 1.0.61.0
    VistaDBConnectionProvider DevExpress.Xpo.vXXX.Providers VistaDB.4.dll 4.0.0.0

    More info @ Database Systems Supported by XPO

    This is only one example of what XAF can do for us. XAF provides a comprehensive set of solutions that allow you to outsource all of the mundane programming tasks leaving you to focus purely on your business needs. For more info consult our docs, blogs, code central and support center.

    DevExpress Workflow Instance Store

    In version 11.1.7 our team now provides workflow instance support outside XAF borders! With a few lines of code, it is now possible to store our workflows in any of the 14 database systems described.

    Durability is a key benefit of the Workflow Foundation and it is based on the ability to store a running workflow instance on the fly at almost any time.
    To this end Microsoft workflow team implemented the SqlWokflowInstanceStore class. Using this class and a few lines is possible to store workflow instances in SQL Server as shown,

    // Define SqlWorkflowInstanceStoreBehavior:

    // Set interval to renew instance lock to 5 seconds.

    // Set interval to check for runnable instances to 2 seconds.

    // Instance Store does not keep instances after it is completed.

    // Select exponential back-off algorithm when retrying to load a locked instance.

    // Instance state information is compressed using the GZip compressing algorithm.

    SqlWorkflowInstanceStoreBehavior instanceStoreBehavior = new SqlWorkflowInstanceStoreBehavior(connectionString);

    instanceStoreBehavior.HostLockRenewalPeriod = new TimeSpan(0, 0, 5);

    instanceStoreBehavior.RunnableInstancesDetectionPeriod = new TimeSpan(0, 0, 2);

    instanceStoreBehavior.InstanceCompletionAction = InstanceCompletionAction.DeleteAll;

    instanceStoreBehavior.InstanceLockedExceptionAction = InstanceLockedExceptionAction.AggressiveRetry;

    instanceStoreBehavior.InstanceEncodingOption = InstanceEncodingOption.GZip;

    host.Description.Behaviors.Add(instanceStoreBehavior);

    The above code was copied from the "BuiltInConfiguration" demo, "InstanceStore1" project. This demo is described at "Built-in Configuration"
    (
    http://msdn.microsoft.com/en-us/library/ee622978.aspx). You can download full sources of this demo and many others at "WCF and WF Samples for .NET Framework 4"
    (
    http://www.microsoft.com/download/en/details.aspx?id=21459).

    Following the same architecture our team implemented the DX WorkFlow Instance Store. eXpress Persistent Objects (XPO) is used for common objects storage and is fully capable of working transparently with 14 different database systems. For example to provide support for an Oracle database we could write,

    //We create or connect to a database by setting the connectionstring

    //This code will create 2 tables (XpoWorkflowInstance, XpoInstanceKeyc) in the database

    using (var session = new Session()) {

    session.ConnectionString = "Data Source=DevExpressInstanceStore;User Id=myUsername;Password=myPassword";

    session.UpdateSchema(typeof(XpoWorkflowInstance), typeof(XpoInstanceKey));

    session.CreateObjectTypeRecords(typeof(XpoWorkflowInstance), typeof(XpoInstanceKey));

    }

    // Define WorkflowInstanceStoreBehavior:

    var dxInstanceStoreBehavior = new WorkflowInstanceStoreBehavior(

    typeof(XpoWorkflowInstance), typeof(XpoInstanceKey), DevExpressConnectionString);

    host.Description.Behaviors.Add(dxInstanceStoreBehavior);

    dxInstanceStoreBehavior.RunnableInstancesDetectionPeriod = new TimeSpan(0, 0, 2);

    dxInstanceStoreBehavior.InstanceCompletionAction = InstanceCompletionAction.DeleteAll;

    You can download a modified version of the “BuiltInConfiguration” solution here. The console application starts a long running workflow that implements a counting service. Once the service’s start method is invoked, the service counts from 0 to 59. The counter is incremented every 2 seconds. After each count the workflow persists so you can close the application at any time and when you start it next time it will continue. A new one will be started from '0' value in addition to the loaded instances. The second project “InstanceStore2” in the solution provides the same functionality, however it is configured using the app.config file as shown,

    <system.serviceModel>

    <extensions>

    <behaviorExtensions>

    <add name="DevExpressWorkflowInstanceStore" type="DevExpress.Workflow.Store.WorkflowInstanceStoreElement, DevExpress.Workflow.Activities.v11.1"/>

    </behaviorExtensions>

    </extensions>

    <services>

    <service name="CountingWorkflow" behaviorConfiguration="">

    </service>

    </services>

    <behaviors>

    <serviceBehaviors>

    <behavior name="">

    <!--<sqlWorkflowInstanceStore

    connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=InstanceStore;Integrated Security=True;Asynchronous Processing=True"

    hostLockRenewalPeriod="00:00:05" runnableInstancesDetectionPeriod="00:00:02" instanceCompletionAction="DeleteAll"

    instanceLockedExceptionAction="AggressiveRetry" instanceEncodingOption="GZip"

    />-->

    <DevExpressWorkflowInstanceStore

    connectionString="Data Source=DevExpressInstanceStore;User Id=myUsername;Password=myPassword"

    runnableInstancesDetectionPeriod="00:00:02" instanceCompletionAction="DeleteAll"/>

    </behavior>

    </serviceBehaviors>

    </behaviors>

    </system.serviceModel>

    Note; All we need to do to use these code snippets in our code is to reference 'DevExpress.ExpressApp.v11.1.dll' and 'DevExpress.Workflow.Activities.v11.1.dl assemblies. Even though these assemblies are part of our eXpressApp framework and need a special license they can also be used to support any other type of .NET application!

    We are waiting to read your feedback about this. Remember that your questions are the best candidates for future posts.

    Related Links
    Blog posts
    Online documentation
    Videos

  • Questions, ideas for 30-Aug-2011 - “Ask the XAF and XPO Teams” webinar

    We would like to make our "Ask Team" webinars more interactive and interesting. In order to do this, we want to try to collect questions and ideas from our users and address them during the webinar, rather than just waiting for questions "at runtime" or demo-ing something we planned ourselves. The next "Ask DevExpress - XAF and XPO" webinar is on 30-Aug-2011 (click here for free registration), and we would like to make the first experiment for it.

    What do you think about this idea in general?

    If you support it, let's make suggestions for the upcoming webinar, either in comments to this blog, the thread in the XAF forum or even in corresponding groups in social networks. For instance, the next question asked in the forums seems to be a good example:
    "Why use Domain Components in AsAlias mode instead of pure persistent objects?", and it can be addressed in the webinar.

    Thank you for your feedback in advance!

  • Case Study: Paid Access Control Systems built with DevExpress WPF controls & XAF

    Check out a case study from Roman Shelomanov, a developer working for a Russian company DevPark that created Access Control software system. This system consists of an administrative module, built utilizing the DevExpress eXpressApp Framework (XAF) technology, and client (cashier) applications, built using DevExpress WPF Controls.

    Be sure to learn more about Roman’s experience with XAF and how it has helped Roman accelerate the development of LOB applications at the end of the case study. We apologize for the inconvenience the absence of screenshots with English captions may cause some of our readers. If you wish to see more screenshots of a typical UI produced by XAF out-of-the-box, please find them here.

    Wish to share your story about using our products in your projects and have it appear  on our web site?
    Get a case study template and write to us at clientservices@devexpress.com!

    Happy XAFingWinking smile

  • EasyTest Improvements (Coming in v2011 vol 1.7)

    In this post, I would like to announce improvements to the EasyTest framework, which will be available in the next minor update.


    CheckTable Command


    Those XAFers who use EasyTest for testing their XAF applications, know the CheckTable command well. This command checks data in a List View. If an expected row(s) is not found, test execution fails. In certain scenarios, checking the exact content of a List View is not a requirement; it is simply enough to check the row count. For instance, you may want to test a Detail View Controller with an Action that influences the count of records in a nested List View. To cover this usage, we have introduced a new RowCount parameter. Note that this change does not break your existing tests, it just extends the CheckTable command syntax. Here is an example.

    *CheckTable Contacts
     RowCount = 5

    The test above fails if the contacts count is not 5. Of course, the new RowCount parameter can be mixed with other parameters.

    *CheckTable Contacts
     Columns = Name
     Row = Sam
     Row = John
     RowCount = 2


    If you need to check to see if a table is empty, there is no need in the RowCount parameter, just use the CheckTable command without secondary parameters.

    *CheckTable Contacts

    As the CheckTable command does not allow the "!" prefix, the new RowCount parameter with the "!=" operator is required to ensure that a table is not empty.

    *CheckTable Contacts
     RowCount != 0

    I believe you will find this minor improvement helpful when EasyTesting your applications.


    ReopenApplication Command

    The ReopenApplication command restarts an application during the test flow. But, in certain scenarios, testing multiple applications with a common data set in a single EasyTest script is required. For instance, you may want to start a Windows Forms application, make some changes, and then see how these changes affect the ASP.NET application. Now, the ReopenApplication command takes the optional parameter that specifies the name of the application to be started after closing the current one.

    #DropDB MySolution
    #Application MySolutionWin

    ; ...

    *ReopenApplication MySolutionWeb

    ; ...

     

    CompareScreenshot and SetActiveWindowSize Commands


    The CompareScreenshot command takes the screenshot of the active window (or browser's content) and compares it with the template PNG image specified by the primary parameter. When the script is executed for the first time and there are no templates, this command simply creates templates (the Images subfolder of the configuration file location is used for this purpose). The actual testing is performed on subsequent test runs, and the command fails if the current and template screenshots are not identical. You can use this command to ensure that your carefully crafted layout is not changed accidentally, or to check that the markup of your custom web template is not corrupted. The SetActiveWindowSize command, as its name suggests, resizes an active window. You can use it to guarantee that window sizes on the current and etalon screenshots are the same. Below is an example of using these commands together.

    *SetActiveWindowSize 640x480
    *CompareScreenshot ContactsDetailViewWin.png

    Another possible usage of these commands is the automatic generation of illustrations for end-user documentation. Keeping screenshots up-to-date for each new version takes a lot of effort. You can simplify this task by implementing an EasyTest script, and running it each time when there are noticeable changes in a UI. The only thing left to do is to delete obsolete images from the folder with templates before running a script.


    Certainly, I will include information from this post in the next update of XAF documentation.

  • Creating a State Machine module for eXpandFramework – Part 1

    Let me describe for a moment how we at DevExpress work. We build and sell software which means that we only sell and provide support for products that have been built and tested by us! However I am here as a framework evangelist and huge XAF fan. This makes it my duty to spread the word as much as I can and make XAF even bigger. To this end through collaboration within the XAF community, we have been building and supporting eXpand. This framework follows XAF to the letter and takes things even further. eXpand gets its inspiration from real life situations and bases itself on examples from DevExpress Support Center. eXpand is the first open source project based on the DevExpress eXpressApp Framework (XAF). More info is available at www.expandframework.com and our very existence relies on your efforts! Anyone is welcome to contribute and enjoy the rewards. It is not necessary to be a XAF guru, we can all manage to create a behavior taken from DevExpress code central. Let’s work together to enhance our beloved XAF!

    Today we are going to deal with creating a reusable module for eXpandFramework. This new module will host State Machine logic therefore we are going to name it Xpand.ExpressApp.StateMachine. A Security logic similar to this is going to be the first resident however we are going to use permissions rather than manually writing the custom function criteria operator to the TargetObjectCriteria. This module can be used as a standalone without the need of eXpandFramwork for the moment. However keeping it under the eXpand umbrella will help us to share features/processes within the framework in the future.

    First we need to create a new VS project that shares characteristics with the rest of the eXpand modules. Currently eXpand only provides a new Solution VS template. This means that we need to do things the old fashioned way, i.e. copying and pasting an existing module. Our behavior is platform independent thus it’s a good idea to choose a module that is also platform independent such as Xpand.ExpressApp.ViewVariants.

    The next step is to open the cloned project, rename it and set its references to those shown in the pic below. It is important to leave all Xpand core references as they are.

    image

    It is advisable to register the original StateMachine module to avoid having to register it later.

    image

    XpandSystemModule is already registered since we used the Xpand.ExpressApp.ViewVarians project as a template.

    We want (with the help of the existing XAF Security permissions system) to be able to assign special types of permissions to a role. We can then use these permissions to control the transition to certain states. The State Machine module uses XpoStateMachine and XpoState persistent classes. These classes can be linked to a permission by name. As a result a permission having 2 properties StateMachineName and StateName would be sufficient.

    [NonPersistent]

    public class StateMachineTransitionPermission : PermissionBase {

        public override IPermission Copy() {

            return new StateMachineTransitionPermission(Modifier, StateCaption, StateMachineName);

        }

     

        public StateMachineTransitionPermission() {

        }

        public override SecurityElement ToXml() {

            SecurityElement result = base.ToXml();

            result.AddAttribute("modifier", Modifier.ToString());

            result.AddAttribute("stateMachineName", StateMachineName);

            result.AddAttribute("stateCaption", StateCaption);

            return result;

        }

        public override void FromXml(SecurityElement e) {

            base.FromXml(e);

            Modifier =

                (StateMachineTransitionModifier)

                Enum.Parse(typeof(StateMachineTransitionModifier), e.Attributes["modifier"].ToString());

            StateCaption = e.Attributes["stateCaption"].ToString();

            StateMachineName = e.Attributes["stateMachineName"].ToString();

     

        }

        public StateMachineTransitionPermission(StateMachineTransitionModifier modifier, string stateCaption, string stateMachineName) {

            Modifier = modifier;

            StateCaption = stateCaption;

            StateMachineName = stateMachineName;

        }

        public override bool IsSubsetOf(IPermission target) {

            var isSubsetOf = base.IsSubsetOf(target);

            if (isSubsetOf) {

                var stateMachineTransitionPermission = ((StateMachineTransitionPermission)target);

                return stateMachineTransitionPermission.StateCaption == StateCaption &&

                       stateMachineTransitionPermission.StateMachineName == StateMachineName;

            }

            return false;

        }

     

        public StateMachineTransitionModifier Modifier { get; set; }

     

        public string StateMachineName { get; set; }

     

        public string StateCaption { get; set; }

    }

     

    A  Modifier property can be used to disable our permission, moreover the SecuritySystem is going to grant the permission by calling the IsSubsetOf method.

    Finally, we are going to create a controller and check if a permission with the same state and statemachine name has been granted to our system. If not we are going to throw an

    exception

     

    public class StatePermissionController : ViewController, IModelExtender {

        void IModelExtender.ExtendModelInterfaces(ModelInterfaceExtenders extenders) {

            extenders.Add<IModelOptions, IModelOptionsStateMachine>();

        }

     

        protected override void OnActivated() {

            base.OnActivated();

            var stateMachineController = Frame.GetController<StateMachineController>();

            stateMachineController.TransitionExecuting += OnTransitionExecuting;

        }

     

        void OnTransitionExecuting(object sender, ExecuteTransitionEventArgs executeTransitionEventArgs) {

            var states = executeTransitionEventArgs.StateMachine.States.OfType<XpoState>();

            foreach (var state in states) {

                if (IsNotGranted(state))

                    throw new UserFriendlyException("Permissions are not granted for transitioning to the " + state.Caption);

            }

        }

     

        bool IsNotGranted(XpoState state) {

            return IsNotGranted(new StateMachineTransitionPermission(StateMachineTransitionModifier.Deny, state.Caption, state.StateMachine.Name));

        }

     

        static bool IsNotGranted(IPermission permission) {

            var securityComplex = ((SecurityBase)SecuritySystem.Instance);

            bool isGrantedForNonExistentPermission = securityComplex.IsGrantedForNonExistentPermission;

            securityComplex.IsGrantedForNonExistentPermission = true;

            bool granted = SecuritySystem.IsGranted(permission);

            securityComplex.IsGrantedForNonExistentPermission = isGrantedForNonExistentPermission;

            return granted;

        }

    }

    During the writing of this post M. Brekhof asked if it is possible to hide the transition in the UI if there are no permissions. This is a certainly a useful feature to include in the new module. To implement it we can subscribe to the ItemsChanged event of the ChangeStateAction and use the IsGranted method there.

    image

    Using the model to control the above allows us more flexibility as we can choose whether to include these improvements or not. In order to do so we need to define and register a model extender.

    image

    It should be clear that this is a rapid development! In a few short minutes we have created a module which can be used as often as necessary to enhance the functionality of our applications. This is another fine example of getting the job done the XAF way!

    So far we have not discussed how to create lookups for our StateMachineTransitionPermission StateMachineName,StateName properties. Don’t worry all of this will be featured in part 2. In the meantime if any of you need any other information please let me know so that I can cover it too.

    Happy eXpanding!

  • Manually starting workflows – the best choice

    Prerequisites
    Manually starting workflows

    In the first post of the series we saw how to start a workflow using a WCF service. Furthermore we also discussed doing this using the module’s out of the box mechanisms. So far however, we haven’t had time to analyze the reasons for choosing one method or the other. This post aims to clarify the situation.

    WCF Solution

    As Dennis likes to say, “There is no need to light a cigar with a nuclear reactor”. This is certainly true when it comes to the WCF solution as it can be somewhat unwieldy. In addition we have to come to terms with more technology as it requires configuration for even the smallest task. For example, the client always needs to know the WCF service address. It is also very difficult to work with WF instances because in order to accomplish common tasks (view, track status, generate reports, measure client service reply time) we need to introduce a system of persistent classes that will help to simulate the out of the box behavior. A third issue arises when modifying the solution because there is often a need for changes to occur in many places at the same time. For example when introducing additional properties we have to change client code and Receive contracts.

    This option is only recommended when we have a large system (>10000 WF instances). Only then we will notice a gain in performance. This is because one WCF request can start a workflow which costs nothing compared to multiple Sql Queries. In all other cases we are fortunate that XAF allows us to take the easy road as usual!

    Out of the box

    Take our usual product ordering task; a client simply creates a new OrderRequest business object and fills the necessary members. A workflow with  an AutoStartWhenObjectIsCreated condition will start when the object is saved. After that we can access all the information in the workflow algorithm. This method allows us to,

    Note that a similar process is described in Working with CRUD activities – Short Transactions

    We would appreciate your feedback on this post. Has it been useful to you? Feel free to contact us  with any further questions

    Related Links
    Online documentation
    Blog posts

  • How to use an eXpand module with an existing XAF application

    Let me describe for a moment how we at DevExpress work. We build and sell software which means that we only sell and provide support for products that have been built and tested by us! However I am here as a framework evangelist and huge XAF fan. This makes it my duty to spread the word as much as I can and make XAF even bigger. To this end through collaboration within the XAF community, we have been building and supporting eXpand. This framework follows XAF to the letter and takes things even further. eXpand gets its inspiration from real life situations and bases itself on examples from DevExpress Support Center. eXpand is the first open source project based on the DevExpress eXpressApp Framework (XAF). More info is available at www.expandframework.com and our very existence relies on your efforts! Anyone is welcome to contribute and enjoy the rewards. It is not necessary to be a XAF guru, we can all manage to create a behavior taken from DevExpress code central. Let’s work together to enhance our beloved XAF!

    Using an eXpand module with an existing XAF application is an issue that is being raised a lot recently and I am sure it deserves a post and a place in Xpand Wiki. The following information applies to any Xpand module. Note that installing an Xpand module is almost as easy as using a XAF one.

    20/08/12 (Modules can be used without the core functionality http://goo.gl/JU5hi)

    Introduction

    Firstly, as a base type for our application components it is imperative to use XpandWinApplication or XpandWebApplication. Therefore we need to reference Xpand.ExpressApp.Win or Xpand.ExpressApp.Web assemblies from our application projects and replace their components’ base types accordingly,

    image

    Additionally we need to reference Xpand.Persistent.BaseImpl and set its Copy Local attribute to true. Having similar architecture with XAF, eXpand modules use interfaces and their implementations are hosted in the BaseImpl assembly. Xpand modules know how to locate the Business Objects they require so this is all we need to do at this point.

    Note; BaseImpl assembly has no references to DX BaseImpl and it is possible to use a replace it with a custom one .

    Registering in Toolbox (Obsolete after v11.1.8.13 - Toolbox is autopopulated now)

    Now, unless we are a XAF guru, we have to register the assemblies in the VS toolbox. I suggest creating the toolbox items within a new Xpand Tab as shown. At this point we don’t have the resources to create our own ToolBoxCreator utility. If any VS IDE guru wants to contribute such a tool, we owe him one!

    image image

    Registration

    Registering and using a module is as easy as dragging and dropping it into the Application Designer. The designer will then add all required references and modules for us.

    image

    Give us your feedback

    Was this post useful to you? Do you want to learn more about Xpand framework? We would love to hear your feedback!

    Happy eXpanding!

    Updated: 19/12/2012

    Under folder Demos/Modules there is a set of solutions that can guide you how to install each module seperately

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