Skip to main content

DevExpress v25.1 Update — Your Feedback Matters

Our What's New in v25.1 webpage includes product-specific surveys. Your response to our survey questions will help us measure product satisfaction for features released in this major update and help us refine our plans for our next major release.

Take the survey Not interested

Clone Object (Copy Data Records)

  • 7 minutes to read

The Clone Object module allows users to clone existing objects in a single click. This functionality can be useful when a user wants to create similar data items without the need to fill all the new data item’s fields.

Clone Object Module Winforms Blazor

#Add the Clone Object Module to an App

The Clone Object module consists of a single assembly:

  • DevExpress.ExpressApp.CloneObject.v25.1.dll

You can find it in the DevExpress.ExpressApp.CloneObject NuGet package.

To enable the Clone Object module functionality in Windows Forms, ASP.NET Web Forms, and ASP.NET Core Blazor applications, add CloneObjectModule to the XafApplication.Modules or ModuleBase.RequiredModuleTypes list:

File: MySolution\CS\MySolution.Module\MySolutionModule.cs

using DevExpress.ExpressApp.CloneObject;
// …
namespace MySolution.Module {
    public sealed partial class MySolutionModule : ModuleBase {
        public MySolutionModule() {
            // …
            RequiredModuleTypes.Add(typeof(CloneObjectModule));
        }
    }
}

Tip

  • In .NET applications, you can call the AddCloning<TBuilder>(IModuleBuilder<TBuilder>, Action<ObjectCloningOptions>) method in your ASP.NET Core Blazor/WinForms application builder.

  • In .NET Framework projects, you can invoke the Module Designer and drag the Clone Object module from the Toolbox to the RequiredModules panel. To add this module to an application project, invoke the Application Designer, drag the CloneObject module from the Toolbox‘s XAF Modules page to the Modules panel on the Designer, and rebuild your solution.
  • You can add modules to your application when you use the Template Kit to create a new XAF solution. Select modules in the Additional Modules section.

#How It Works

The Clone Object module supplies the CloneObjectViewController. This Controller contains the CloneObject Single Choice Action that clones the selected object.

The Action’s ChoiceActionBase.Items collection includes the current object type and types inherited from the object’s base class.

XAF CloneObject Action Items Collection, DevExpress

Tip

You can also clone incompatible types if they share the same interface. For more information, refer to the following section: Clone Incompatible or Non-Assignable Object Types That Implement a Common Interface.

#Specify Object Types and Properties to Be Cloned

The Clone Object module extends the Application Model‘s BOModel | <Class> nodes with the IModelClassCloneable.IsCloneable property. Set it to True to enable the action for objects of the selected type.

XAF IsClonable Property in Model Editor, DevExpress

Alternatively, decorate your business class with the following attribute in code: [ModelDefault("IsCloneable", "True")].

The CloneObject Action is disabled if the current object has unsaved changes because the clone process works in a separate Object Space.

To prevent an individual field or property from being cloned, decorate it with NonCloneableAttribute.

#Cloned Properties Behavior

Property type Cloning behavior XAF Blazor Main Demo example
Value (String, Boolean, Numeric, and so on) Fully equivalent to the source object. Clone an Employee object. All value type properties should be the same in the cloned object.
Reference XAF clones the value of the reference property, but does not clone the referenced object. Clone an Employee object. The Position property of both objects references the same Position object.
Aggregated reference XAF clones the property value and the referenced object. Clone an Employee object. The referenced Address object is also cloned.
Non-Aggregated Collection The cloned collection is empty, if it is a part of an non-aggregated One-to-Many or Many-to-Many relationship or a weak/non-associated collection. Clone a Department object that has a populated Employee collection. The cloned object’s Employee collection is empty.
Aggregated collection When you clone an object that has a populated aggregated collection, XAF also clones objects that populate this collection. Clone an Employee object. Objects from its PhoneNumbers collection are cloned too.

#Clone Object Module Customization

To implement custom behavior for the CloneObjectViewController or CloneObject Action, use standard XAF techniques described in the following topics:

For example, you can use the following customization techniques:

  • Create a custom View Controller and override its OnActivated method to access the target Controller and its Action.
  • Inherit from CloneObjectViewController and override its own virtual methods.

#Modify Default Cloner Logic or Implement a Fully Custom Algorithm

Handle the CloneObjectViewController.CustomCloneObject event to clone objects with custom logic.

using DevExpress.ExpressApp;
using DevExpress.ExpressApp.CloneObject;
using DevExpress.Persistent.BaseImpl.EF;
using Microsoft.EntityFrameworkCore.Metadata;

namespace YourApplicationName.Module.Controllers;

public class CustomizeCloneObjectController: ObjectViewController {
    protected override void OnActivated() {
        base.OnActivated();
        var cloneObjectController = Frame.GetController<CloneObjectViewController>();
        if(cloneObjectController != null) {
            cloneObjectController.CustomCloneObject += cloneObjectController_CustomCloneObject;
        }
    }
    void cloneObjectController_CustomCloneObject(object sender, CustomCloneObjectEventArgs e) {
        e.TargetObjectSpace = e.CreateDefaultTargetObjectSpace();
        var cloner = new MyCloner(e.TargetObjectSpace);
        object objectFromTargetObjectSpace = e.TargetObjectSpace.GetObject(e.SourceObject);
        e.ClonedObject = cloner.CloneTo(objectFromTargetObjectSpace, e.TargetType);
    }
}

public class MyCloner: Cloner {
    public MyCloner(IObjectSpace objectSpace) : base(objectSpace) {
    }
    public override void CopyPropertyValue(
        IPropertyBase property, object sourceObject, object targetObject) {
        if(!(property is IReadOnlyNavigationBase)) {
            base.CopyPropertyValue(property, sourceObject, targetObject);
        }
    }
}

#Customize Display of a Cloned Object’s Detail View

The following code snippet adds the cloned object to the current List View instead of displaying it in the Detail View. Note that the code only works when the current View is an editable List View. The default behavior persists when the current View is a Detail View or non-editable List View.

using DevExpress.ExpressApp;
using DevExpress.ExpressApp.CloneObject;
using DevExpress.Persistent.BaseImpl.EF;
using Microsoft.EntityFrameworkCore.Metadata;

namespace YourApplicationName.Module.Controllers;

public class MyCloneObjectController : CloneObjectViewController  {
    // ...
    protected override void OnActivated() {
        base.OnActivated();
        this.CustomShowClonedObject +=
            new EventHandler<CustomShowClonedObjectEventArgs>(
                MyCloneObjectController_CustomShowClonedObject);
    }
    private void MyCloneObjectController_CustomShowClonedObject(
        object sender, CustomShowClonedObjectEventArgs e) {
        if ((View is ListView) && (View.AllowEdit.ResultValue)) {
            e.TargetObjectSpace.CommitChanges();
            ((ListView)View).CollectionSource.Add(ObjectSpace.GetObject(e.ClonedObject));
            e.Handled = true;
        }
    }
}

#Prevent Cloning Descendants or Assignable Object Types

The following code deactivates the sibling cloning functionality. Descendant types of the current object’s ancestor do not appear in the Action’s ChoiceActionBase.Items collection:

using DevExpress.ExpressApp.CloneObject;
//...

namespace YourApplicationName.Module.Controllers;

public partial class MyCloneObjectController : ViewController {
    //...
    protected override void OnActivated() {
        base.OnActivated();
        CloneObjectViewController cloneObjectController =
            Frame.GetController<CloneObjectViewController>();
        if (cloneObjectController != null) {
            cloneObjectController.CloneObjectAction.Items.Clear();
            ChoiceActionItem myItem =
                new ChoiceActionItem(View.ObjectTypeInfo.Name, View.ObjectTypeInfo.Type);
            myItem.ImageName =
                Application.Model.BOModel.GetNode<IModelClass>(View.ObjectTypeInfo.FullName).ImageName;
            cloneObjectController.CloneObjectAction.Items.Add(myItem);
        }
    }
}

If you implement this Controller, the CloneObject Action clones the selected object to the object of the same type because the Action’s Item will be the current object type. To manually fill the Items of the CloneObjectAction, handle the CloneObjectViewController.CustomGetCloneActionTargetTypes event. For more information, refer to the event’s description.

#Clone Incompatible or Non-Assignable Object Types That Implement a Common Interface

To clone an object into an object of a different type, both types must be compatible: either they inherit one another or both inherit from the same type. If you want to clone incompatible types, you have to follow these steps:

  1. Implement the same interface in both classes. For example, the following code snippets create Note and Appointment classes that both implement the IText interface:

    C#
    using System.ComponentModel;
    using DevExpress.ExpressApp.DC;
    using DevExpress.Persistent.Base;
    using DevExpress.Persistent.BaseImpl.EF;
    
    namespace YourApplicationName.Module.BusinessObjects;
    
    public interface IText {
        string Subject { get; set; }
        string Description { get; set; }
    }
    
    C#
    using System.ComponentModel;
    using DevExpress.ExpressApp.DC;
    using DevExpress.Persistent.Base;
    using DevExpress.Persistent.BaseImpl.EF;
    
    namespace YourApplicationName.Module.BusinessObjects;
    
    public class Note : BaseObject, IText {
        public virtual String Author { get; set; }
        public virtual string Subject { get; set; }
        public virtual string Description { get; set; }
    }
    
    C#
    using System.ComponentModel;
    using DevExpress.ExpressApp.DC;
    using DevExpress.Persistent.Base;
    using DevExpress.Persistent.BaseImpl.EF;
    
    namespace YourApplicationName.Module.BusinessObjects;
    
    public class Appointment : BaseObject, IText {
        public Appointment() { }
        public virtual String Organizer { get; set; }
        public virtual string Subject { get; set; }
        public virtual string Description { get; set; }
    }
    
  2. Add the following controller to the application’s Module project. This controller allows you to clone an Appointment object to either an Appointment or a Note object:

    C#
    using DevExpress.ExpressApp.CloneObject;
    using DevExpress.XtraScheduler;
    using YourApplicationName.Module.BusinessObjects;
    
    namespace YourApplicationName.Module.Controllers;
    
    public class MyCloneObjectViewController : CloneObjectViewController {
        private void CloneObjectViewController_CustomGetCloneActionTargetTypes(object sender,
            CustomGetCloneActionTargetTypesEventArgs e) {
            if ((e.SourceType.Type == typeof(Note)) || (e.SourceType.Type == typeof(Appointment))) {
                e.TargetTypes.Add(Application.Model.BOModel[typeof(Note).FullName], typeof(Note));
                e.TargetTypes.Add(Application.Model.BOModel[typeof(Appointment).FullName], typeof(Appointment));
                e.Handled = true; }
        }
        public MyCloneObjectViewController() : base() {
                CustomGetCloneActionTargetTypes += CloneObjectViewController_CustomGetCloneActionTargetTypes; }
    }
    

    Note

    When you clone a Note object into an Apppointment object, XAF copies the Subject and Description property values.

    XAF ASP.NET Core Blazor Clone Object to a Different Type, DevExpress

See Also