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.
#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 Add
Cloning method in your ASP.<TBuilder>(IModule Builder <TBuilder>, Action <Object Cloning Options>) NET Core Blazor/Win Forms application builder. - In .NET Framework projects, you can invoke the Module Designer and drag the Clone Object module from the Toolbox to the Required
Modules 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.
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.
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 Phone 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:
Implement the same interface in both classes. For example, the following code snippets create
Note
andAppointment
classes that both implement theIText
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; } }
Add the following controller to the application’s Module project. This controller allows you to clone an
Appointment
object to either anAppointment
or aNote
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 anApppointment
object, XAF copies theSubject
andDescription
property values.