Click here to Skip to main content
15,393,863 members
Articles / Programming Languages / C# 4.0
Article
Posted 26 Jul 2011

Tagged as

Stats

73K views
1.6K downloads
15 bookmarked

Using Domain Components (DC) in XAF (DevExpress): Part I: The Basics

Rate me:
Please Sign up or sign in to vote.
5.00/5 (10 votes)
26 Jul 2011CPOL15 min read
Developing a small generic re-usable model using the highly flexible DC technology resulting in a fully functional mini-application

This article appears in the Third Party Products and Tools section. Articles in this section are for the members only and must not be used to promote or advertise products in any way, shape or form. Please report any spam or advertising.

Image 1

Introduction

The ExpressApp Framework (XAF) is a commercial framework produced by DevExpress. The new technology called Domain Components (DC) is a very new way to create business logic within XAF. In this article, I want to explain the benefits of using this new technology, which is based on another framework called XPO (an ORM layer also from DevExpress).

In this article, I will show you how you can create business logic in a very intuitive and functional (and really fast) way and from there generating the database and UI automatically resulting in a fully functional application.

Before you want to build the attached sources, please download the trial version of the DevExpress Universal subscription first. The solution provided in the attachment is based on Visual Studio 2010. You can download a trial version here. Installing this version (11.1.6) will install all the Devexpress components and the XAF and XPO frameworks together with DC. (Note that installing the software can take a while.)

Background

Domain Components technology is designed to simplify data management and reusability by providing a much more flexible and abstracted approach to business object design. You can compose an application from reusable blocks, abstracted away from a particular persistence layer. With Domain Components, you define interfaces instead of regular business objects inherited from XPO classes. These interfaces will declare required properties or data fields. The way this data is to be processed (Domain Logic) is then defined by creating special classes that determine how interface members behave when an object is constructed, when properties are changed, etc. Actual business classes are automatically implemented by XAF at runtime based on the provided logic and interfaces. You can package interfaces and domain logic in an assembly and use it as a domain library. If you then create a new XAF application, you can reference the domain library and reuse domain components. Since interfaces support multiple-inheritance, required business objects can be combined into new domain components. With interfaces, you can make your domain model independent of implementation. (See DC basics for more details). Note that the DC technology is in the CTP end-phase and will be released in the near future officially.

Let's Start

First let us focus on the real world problem. Suppose we want to develop a simple customer maintenance application where we can maintain customer and address information. In addition, we want to create a basic, quite generic and re-usable functional layer first that we can re-use to build our showcase end-user application.

The Basic Functional Layer

If you look at database applications and analyze them, you can extract some commonly used patterns. These patterns (building blocks) can be easily described using DC. Here is a list of the basis patterns we'll describe:

  • Active: Is the object active or not?
  • CodeDescription: Does the object have a code and description?
  • DisplayName: The displayname of the object in the UI
  • Memos: Can the object contain memos?

Certainly, there are a lot more of these patterns (FriendlyKeys, Default object, etc.) but for the sake of this article, I'll keep it as simple as possible.

Creating the Basic Layer

For this, you need to install the (trial) DevExpress Universal subscription (latest version) first. After this, start VS and create a new project. Choose for the Applications solution template. This template will create a windows and an ASP.NET application in one solution.

Image 2

Just for convenience, we'll also use this solution to create our basic layer as well positioned in a separate project (Modules in XAF terms). After the template has generated our projects, we can add our new module representing the basic layer (tip: built the code after generation). Right click on the solution and choose Add\New project.

Image 3

In the Common.Module, create a new folder "BOModel". This is the place for our new business objects. Right mouse on the folder and choose Add\New item. Choose the Domain Component template and name it to "IActive" to create a new one.

Image 4

Our focus here is to create our domain model first using code (code first). After that, we can generate the persistent store (database) and default UI automatically (don't worry about that at the moment).

Now, VS has created the IActive.cs with a IActive interface decorated with the [DomainComponent] attribute and some code which is commented out. The solution will look like this:

Sample Image - maximum width is 600 pixels

Within this interface, we can define properties and methods just like a normal C# interface. Let's enter a read-only property IsActive as boolean (remove the comments in the interface).

C#
[DomainComponent]
public interface IActive
{
    [Custom("AllowEdit", "False")]
    bool IsActive { get; set; }
}

So this property will hold the actual active status. Now let's add some simple logic to manipulate this property. How can we do that? As we know, interfaces cannot contain logic. Therefore a trick is used to map logic to a particular interface. DC is using the class attribute [DomainLogic] for this purpose. By applying this attribute on a class, we can map a class to an interface like this:

C#
[DomainLogic(typeof(IActive))]
public class IActiveLogic
{

}

You can see that the DomainLogic attributes points to the IActive interface. By convention, we can define some pre-defined (static) methods here (see the comments) controlling the interface and its properties. In addition, we can map method signatures defined in the interface with actual methods in the class. When the IActive interface gets instantiated, the AfterConstruction method will be called automatically. A nice place to assign default values. The IsActive property has a read-only UI hint "AllowEdit". This will make the property disabled in the UI. By default, the IsActive property must be set to true.

C#
[DomainComponent]
public interface IActive
{
    [Custom("AllowEdit", "False")]
    bool IsActive { get; set; }
    void Activate();
    void DeActivate();
}
[DomainLogic(typeof(IActive))]
public class IActiveLogic
{
    public static void AfterConstruction(IActive active)
    {
        active.IsActive = true;
    }
    public static void Activate(IActive instance)
    {
        instance.IsActive = true;
    }
    public static void DeActivate(IActive instance)
    {
        instance.IsActive = false;
    }
}

In order to call these interface methods, we need to use so-called controllers. XAF is using the MVC concept to decouple the model from the presentation layer. The controllers have access to both layers and can contain a collection of actions. These actions are eventually displayed somewhere in the UI (f.e. on the toolbar). So we need to create two actions which are defined in a controller specific for the IActive interface. To do this, create a new folder called "Controllers" and add a new ViewController via the ViewController template. Rename the controller to lvActiveViewController.

Image 6

Open the designer and add two simple actions from the toolbox. Rename the first action to "actActivate" (name and id) and set SelectionDependencyType to "RequireSingleObject" and Category to "View". Set the caption to "Activate". Do the same for the second action but then for deactivate. Double click the two actions to generate the events methods in code. In XAF, controllers will be activated if the controller meets specific criteria. In our case, the controller must become active if the IActive instance is in focus and a listview (grid) has been requested. If this is the case, the activate and deactivate actions must be displayed. We can specify criteria in the constructor of the controller like this:

C#
public partial class lvActiveViewcontroller : ViewController
{
    public lvActiveViewcontroller()
    {
        InitializeComponent();
        RegisterActions(components);
        TargetViewType = ViewType.ListView;
        TargetObjectType = typeof(IActive);
    }
    ...

The controller keeps track of the currentobject so we can cast the current object to our IActive interface. We can do this because the controller becomes active only in the context of IActive (TargetObjectType = typeof(IActive)). We can add the following code in our activate event:

C#
private void actActivate_Execute(object sender, SimpleActionExecuteEventArgs e)
{
   var obj = this.View.CurrentObject as IActive;
    if (obj != null)
    {
        obj.Activate();
    }
}

The same can be done for the deactivate event (call obj.Deactivate). One more thing here. If the user selects an object in the list which is active, the Activate action must be disabled and vice versa. To accomplish this, we need to know when the currentobject is changed. We can use the CurrentObjectChanged event for that which is defined in the ObjectSpace of the ListView. (More information about controllers) Normally the place to register such events is the OnActivated override of the controller.

C#
protected override void OnActivated()
{
    base.OnActivated();
    ((ListView)View).CurrentObjectChanged += 
    new EventHandler(lvActiveViewcontroller_CurrentObjectChanged);
}
....
 void lvActiveViewcontroller_CurrentObjectChanged(object sender, EventArgs e)
{
    enableActions();
}
...
private void enableActions()
{
    var obj = this.View.CurrentObject as IActive;
    if (obj != null)
    {
        actActivate.Active["active"] = !obj.IsActive;
        actDeactivate.Active["active"] = obj.IsActive;
    }
}

Note that we've to call the enableActions method also if we have activated or deactivate the object.

We've finished our first building block. Let's create the other ones. First the CodeDescription pattern. Create a new DC (using template) and rename it to ICodeDescription. As I described earlier, this pattern consists of two properties:

C#
[DomainComponent]
public interface ICodeDescription
{
    string Code { get; set; }
    string Description { get; set; }
}

For this interface, we want the following:

  • The code and description are required fields.
  • The code must be unique.
  • Initially, if the code is entered and the description is empty, the code must be assigned to the description.

DC components (and the underlying XPO framework) rely on attributes heavily. It uses attributes for validation, UI hints and some simple data retrieval. To make a property required, we can use the RuleRequiredField attribute. For uniqueness, the RuleUniqueValue attribute and for the last requirement we need to add some code in the corresponding logic class. The result is this:

C#
[DomainComponent]
public interface ICodeDescription
{
    [RuleRequiredField("", DefaultContexts.Save)]
    [RuleUniqueValue("", DefaultContexts.Save)]
    string Code { get; set; }
    [RuleRequiredField("", DefaultContexts.Save)]
    string Description { get; set; }
}

[DomainLogic(typeof(ICodeDescription))]
public class ICodeDescriptionLogic
{
    public static void AfterChange_Code(ICodeDescription instance)
    {
        if (String.IsNullOrEmpty(instance.Description))
            instance.Description = instance.Code;
    }
}

Quite simple. You can see the AfterChange method convention. This method is automatically called by the DC system when the value of the Code property changes. An instance of the interface is always provided in the parameter list as the optional ObjectSpace (kind of a session object to get access to the database). The "CodeDescription" pattern can be widely used for different kind of entities like language, countries, currency but also products where a code and a description field can be used. Wouldn't it be handy to activate and deactivate those entities? The only thing I need to do here is implementing the IActive interface for ICodeDescription. That's it. It'll be even more fun if we use multiple-inheritance.....

C#
[DomainComponent]
public interface ICodeDescription : IActive
{
   ...
}

The displayname pattern is a special one. In DC, read-write properties defined in the interface are automatically persisted to the datastore. In our case, a displayname is a read-only thing which we don't want to persist. It must be calculated on the fly and it must be flexible to define. For this scenario, we also want to use an interface but not a DC! It will be a regular C# interface which we can implement for the appropriate DC interfaces. Create a new class and rename it to IDisplayName. Add a string property "DisplayName" to it.

C#
[XafDefaultProperty("DisplayName")]
public interface IDisplayName
{
    [VisibleInDetailView(false)]
    string DisplayName { get; }
}

The XafDefaultProperty points to the default property used to display objects in the XAF userinterface. The VisibleInDetailView(false) tells XAF that this property must be hidden in detailviews (normal entry forms). We can use this interface to define a nice displayname for the ICodeDescription interface by implementing the IDisplayName like this:

C#
[DomainComponent]
public interface ICodeDescription : IActive, IDisplayName
{
    [RuleRequiredField("", DefaultContexts.Save)]
    [RuleUniqueValue("", DefaultContexts.Save)]
    string Code { get; set; }
    [RuleRequiredField("", DefaultContexts.Save)]
    string Description { get; set; }
}

[DomainLogic(typeof(ICodeDescription))]
public class ICodeDescriptionLogic
{
    public static void AfterChange_Code(ICodeDescription instance)
    {
        if (String.IsNullOrEmpty(instance.Description))
            instance.Description = instance.Code;
    }
    public static string Get_DisplayName(ICodeDescription instance)
    {
        return instance.Code != instance.Description ? String.Format("{0} - {1}", 
        instance.Code, instance.Description) : instance.Code;
    }
}

As you can see, DC will pick up the "Get_DisplayName" seamlessly because the DisplayName property is now just a derived member in the ICodeDescription interface.

It's always quite convenient to register memos for particular entities in your application. We can define a memo pattern for this. Obviously, this will be a DC again. Create a new DC and rename it to IMemo. This interface is simple.

C#
[DomainComponent]
public interface IMemo
{
    [RuleRequiredField("", DefaultContexts.Save)]
    [Size(SizeAttribute.Unlimited)]
    [Custom("RowCount", "10")]
    [VisibleInListView(false)]
    string MemoText { get; set; }
    [Custom("AllowEdit", "False")]
    [RuleRequiredField("", DefaultContexts.Save)]
    DateTime EnterDate { get; set; }
    [Custom("AllowEdit", "False")]
    [RuleRequiredField("", DefaultContexts.Save)]
    string User { get; set; }
}

We've three properties which are all required, the memotext and two read-only (but persisted) fields EnterDate and the user creating the memo. (Of course, this pattern can be extended with more properties.) To fill the two read-only properties automatically, we can use a method that will be called when the instance will be constructed. I'm referring to the AfterConstruction method.

C#
[DomainLogic(typeof(IMemo))]
public class IMemoLogic
{
    public static void AfterConstruction(IMemo instance)
    {
        instance.EnterDate = DateTime.Now;
        instance.User = SecuritySystem.CurrentUserName;
    }
}

Suppose we want to register memos for a customer, it would be nice if we can enter a subject for the memo as well. We create a new DC for this derived from IMemo. For simplicity, we define this DC in the same file as IMemo. We call it IMemoSubject and the subject is a required field.

C#
[DomainComponent]
public interface IMemoSubject : IMemo
{
    [RuleRequiredField("", DefaultContexts.Save)]
    string Subject { get; set; }
}

It would be functional if we can maintain a collection of memos which we can apply to other entities. Therefore, we need to create a new DC holding a collection of memos. Furthermore, we can define a read-only calculated convenience property "LastMemo" to display the last entered memo. Again, for the sake of simplicity, we define this DC in the same file as IMemo.

C#
[DomainComponent]
public interface IMemos
{
    IList<imemo /> Memos { get; }
    [Browsable(false)]
    IMemo LastMemo { get; }
    [Custom("RowCount", "5")]
    [VisibleInListView(false)]
    string LastMemoText { get; }
}

The LastMemo and LastMemoText are implicitly read-only because they have no setter. (Later, we'll see that they are disabled in the UI). The LastMemoText will display the memo text only. Even more convenience.... To calculate the lastmemo, we need to define additional logic in the corresponding logic class for IMemos. Something like this:

C#
[DomainLogic(typeof(IMemos))]
public class IMemosLogic
{
    public static IMemo Get_LastMemo(IMemos instance)
    {
        return instance.Memos.OrderByDescending(x => x.EnterDate).FirstOrDefault();
    }
    public static string Get_LastMemoText(IMemos instance)
    {
        return instance.LastMemo != null ? instance.LastMemo.MemoText : "";
    }
}

The solution will look like this now:

Sample Image - maximum width is 600 pixels

Okay, we now have created a simple basic functional layer containing some re-usable building blocks in a separate module.

The Customer Maintenance Application

This application is capable of creating and maintaining customers (companies) and their addresses and contact persons (another common pattern?) Additionally we can assign languages, currencies to the customers and countries to the addresses. We can create the BO model immediately since we already have our CustomerMaintenance solution. The only thing we need to do is to reference the Common.Module in the CustomerMaintenance.Module, where we create our new entities, in order to get access to the basic layer. again, create a new folder in the CustomerMaintenance.Module called BOModel. Add a new DC and rename it to ILanguage. Derive ILanguage from ICodeDescription and IActive and add a additional property to it.

C#
[DomainComponent]
[NavigationItem(true, GroupName = "Definitions")]
public interface ILanguage : ICodeDescription, IActive
{
    string CultureCode { get; set; }
}

The same for ICurrency and ICountry:

C#
[DomainComponent]
[NavigationItem(true, GroupName = "Definitions")]
public interface ICurrency : ICodeDescription, IActive
{
    string Symbol { get; set; }
    int NumOfDecimals { get; set; }
}
...
 [DomainComponent]
 [NavigationItem(true, GroupName = "Definitions")]
public interface ICountry : ICodeDescription, IActive
{
    ILanguage DefaultLanguage { get; set; }
    ICurrency DefaultCurrency { get; set; }
}

Take special note of the NavigationItem attribute. This attribute creates a menu item in the specified group for that particular interface. And the address and contact person interfaces. First the address:

C#
public enum AddressType
{
    Mail,
    Invoice,
    Deliver,
    POBox,
    Visit
}
[DomainComponent]
public interface IAddress: IDisplayName
{
    [RuleRequiredField("", DefaultContexts.Save)]
    string Street { get; set; }
    string StreetNo { get; set; }
    string ZipCode { get; set; }
    String City { get; set; }
    AddressType Type { get; set; }
    [RuleRequiredField("", DefaultContexts.Save)]
    ICountry Country { get; set; }
}
[DomainLogic(typeof(IAddress))]
public class IAddressLogic
{
    public static string Get_DisplayName(IAddress instance)
    {
        return String.Format("{0} {1} {2} {3} {4}", instance.Street,
        instance.StreetNo, instance.ZipCode, instance.City,
        instance.Country == null ? "": instance.Country.Description);
    }
}

Note that we define a pure address entity with no relations to other interfaces. Later on, we'll connect addresses with f.e. customers. The same counts for persons.

C#
public enum Gender
{
    None = 0,
    Male = 1,
    Female = 2
}
[DomainComponent]
public interface IPerson : IDisplayName
{
    [RuleRequiredField("", DefaultContexts.Save)]
    string LastName { get; set; }
    string FirstName { get; set; }
    string MiddleName { get; set; }
    DateTime? Birthday { get; set; }
    string Function { get; set; }
    Gender Gender { get; set; }
    string Initials { get; set; }
}
[DomainLogic(typeof(IPerson))]
public class IPersonLogic
{
    public static string Get_DisplayName(IPerson instance)
    {
        return String.Format("{0}{1}{2}",String.IsNullOrEmpty
        (instance.FirstName) ? instance.Initials : instance.FirstName,
        String.IsNullOrEmpty(instance.MiddleName) ? "" : " " +
        instance.MiddleName,  instance.LastName);
    }
}

Last but not least, we define our customer interface which, for the sake of this article, is an organisation. We also define a customer type using an enumeration (in XAF, by default, an enumeration will be automatically transferred to a dropdown control).

C#
public enum CustomerType
{
    Prospect,
    CustomerBronze,
    CustomerSilver,
    CustomerGold,
}

[DomainComponent]
[NavigationItem(true, GroupName = "CRM")]
public interface ICustomer : IDisplayName, IMemos, IActive
{
    [RuleRequiredField("", DefaultContexts.Save)]
    [RuleUniqueValue("", DefaultContexts.Save)]
    [Custom("DisplayFormat","000000")]
    int Id { get; set; }
    [RuleRequiredField("", DefaultContexts.Save)]
    [RuleUniqueValue("", DefaultContexts.Save)]
    string Name { get; set; }
    string Phone { get; set; }
    string Email { get; set; }
    string Website { get; set; }
    CustomerType CustomerType { get; set; }
    [DevExpress.ExpressApp.DC.Aggregated]
    IList<IAddress> Addresses { get;  }
    [DevExpress.ExpressApp.DC.Aggregated]
    IList<IPerson> ContactPersons { get;  }
    [RuleRequiredField("", DefaultContexts.Save)]
    ILanguage Language { get; set; }
    [RuleRequiredField("", DefaultContexts.Save)]
    ICurrency Currency { get; set; }
}
[DomainLogic(typeof(ICustomer))]
public class ICustomerLogic
{
    public static string Get_DisplayName(ICustomer instance)
    {
        return String.Format("{0} - {1}", instance.Id, instance.Name);
    }
}

The customer interface consists of unique id and name fields, a customer type, two collections representing addresses and contact persons and some other properties. The Aggregated attribute enables the user to update addresses and persons together with the customer in 1 save action instead of saving the customer first and then update the addresses and persons. Furthermore, the customer interface has support for memos (IMemos) and can be activated or deactivated (IActive). Now what if we need a customer reference for an address or a person? We can add a property f.e. Customer referring to ICustomer in respect to the IAddress and IPerson interfaces easily. But maybe we need these interfaces in different contexts not related with a customer at all. Therefore, the best practice is to make descendants of these interfaces containing a reference to the ICustomer interface like this. (Also, we can add additional functionality to these new DC which are meaningful in relation with a customer.)

C#
[DomainComponent]
public interface ICustomerAddress : IAddress
{
    [Browsable(false)]
    ICustomer Customer { get; set; }
}
public enum CustomerPersonType
{
    Employee,
    Contact
}

[DomainComponent]
public interface ICustomerPerson : IPerson
{
    [Browsable(false)]
    ICustomer Customer { get; set; }
    CustomerPersonType Type { get; set; }
}
...
[DomainComponent]
[NavigationItem(true, GroupName = "CRM")]
public interface ICustomer : IDisplayName, IMemos, IActive
{
    ...
    [DevExpress.ExpressApp.DC.Aggregated]
    IList<ICustomerAddress> Addresses { get;  }
    [DevExpress.ExpressApp.DC.Aggregated]
    IList<ICustomerPerson> ContactPersons { get;  }
}

The solution should look like this:

Image 8

Register the Domain Components

In order to let the XAF framework know we've DCs, we have to register them in XAF. Only the interfaces we want to use in our application need to be registered (conceptually, we call them entities). There is only one recommended place in our solution where we register the DCs. In the Module.cs file of the CustomerMaintenance.Module, we register all the entities for the CustomerMaintenance application.

C#
// To use a Domain Component in an XAF application,
// the Component should be registered.
// Override the ModuleBase.Setup method in the application's module and
// invoke the ITypesInfo.RegisterEntity method in it:
//
public override void Setup(XafApplication application)
{
    if (!XafTypesInfo.IsInitialized)
    {
     XafTypesInfo.Instance.RegisterEntity
       ("Language", typeof(ILanguage), typeof(BaseObject), false);
       XafTypesInfo.Instance.RegisterEntity
       ("Country", typeof(ICountry), typeof(BaseObject),false);
       XafTypesInfo.Instance.RegisterEntity
       ("Currency", typeof(ICurrency), typeof(BaseObject),false);
       XafTypesInfo.Instance.RegisterEntity
       ("Person", typeof(ICustomerPerson), typeof(BaseObject),false);
       XafTypesInfo.Instance.RegisterEntity
       ("Address", typeof(ICustomerAddress), typeof(BaseObject),false);
       XafTypesInfo.Instance.RegisterEntity
       ("Customer", typeof(ICustomer), typeof(BaseObject), false);
       XafTypesInfo.Instance.RegisterEntity
       ("Memo", typeof(IMemoSubject), typeof(BaseObject), false);
    }
    base.Setup(application);
}

The End Result

Ok, we're done. We now have a windows application and a web (ASP.NET) application based on the same domain model! Make sure the startup project points to the Customermaintenance.Win project. Now we need to adjust the app.config to specify a valid connectionstring. If you've a local Microsoft SQLExpress server with Integrated Security, then you don't have to do anything. XAF is creating the database automatically based on the DC domain model.

If the connectionstring is valid, press F5 to run the application. For Windows, the result is this:

Image 9

Above, the listview of the customer. See the Deactivate action in the toolbar. All entities derived from IActive have the same functionality now, not only for the business logic, but also for the UI logic.

Double click on the customer and you will get the corresponding detailview:

Image 10

The validation defined in the domain model triggers the errorproviders in the UI automatically.

Image 11

And of course, we've the same application on the web with the same look and feel based on the same logic!

Image 12

Image 13

The XAF framework has generated these layouts for 99% automatically. I've made a few adjustments in the application model where you can overrule the default layoutitems (see Model.DesignedDiffs.xaml in CustomerMaintenance.Module).

In the downloadable sample application, I've added some initial test data (see Updater.cs in CustomerMaintenance.Module). This test data will be added just after a schema has been modified. This becomes handy if you make changes to the domain model a lot and therefore have to drop the database once in a while. Starting the application will then re-generate the database and fill it with the same data over and over again. Obviously, this is only convenient and useful when developing and should not be used for production environments.

For more information about XAF and/or DC, please dive into the online help files here.

Conclusion

This article shows how easy it is to model your real world problems using the DC technology. It will let you focus on functional modeling by using interfaces in a very new and flexible way. DC and the support for multiple-inheritance enables you to build genuine re-usable building blocks in a way never done before. I'm not aware of any ORM, DAL, etc. that is using this concept in combination with relational persistent stores. The combination XAF and DC is very powerful if you want to develop end-user applications. XAF takes you away from the hassle of building and re-building user interfaces and their components. By scaffolding the default UI (which you can overrule at any time), you can concentrate on what is most important: the business logic!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Arjan van Dijk
Software Developer (Senior) Distel Software B.V.
Netherlands Netherlands
Senior Developer and Technical Director at Distel Software B.V.(www.distel.nl)

About 15 years of IT experience in several languages (c++, c#, vb.net) specializing in framework oriented development
(using DC, DO and XAF/XPO) based on C# where the focus is on functional modeling and creating re-usable building blocks.

Comments and Discussions

 
QuestionHow to change IMemos definition to reuse this in another DC ? Pin
JacekKosinski30-Jan-18 9:45
MemberJacekKosinski30-Jan-18 9:45 
AnswerRe: How to change IMemos definition to reuse this in another DC ? Pin
JacekKosinski31-Jan-18 10:27
MemberJacekKosinski31-Jan-18 10:27 
GeneralVery nice! Pin
Pieter Van Parys13-Jun-12 11:41
MemberPieter Van Parys13-Jun-12 11:41 
QuestionCan you provide an example of Default object patterns Pin
khoirom1-Dec-11 17:24
Memberkhoirom1-Dec-11 17:24 
AnswerRe: Can you provide an example of Default object patterns Pin
Arjan van Dijk22-Jan-12 8:08
MemberArjan van Dijk22-Jan-12 8:08 
QuestionUpgrade xaf project to 2011.2.5 error Pin
khoirom1-Dec-11 5:03
Memberkhoirom1-Dec-11 5:03 
AnswerRe: Upgrade xaf project to 2011.2.5 error Pin
Arjan van Dijk22-Jan-12 8:00
MemberArjan van Dijk22-Jan-12 8:00 
QuestionAdding fields to a form Pin
Jaap Britz13-Nov-11 23:46
MemberJaap Britz13-Nov-11 23:46 
AnswerRe: Adding fields to a form Pin
Arjan van Dijk22-Jan-12 8:03
MemberArjan van Dijk22-Jan-12 8:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.