Custom Field Definition Element and Control

Posted by Community Admin on 04-Aug-2018 06:47

Custom Field Definition Element and Control

All Replies

Posted by Community Admin on 17-Jan-2012 00:00

I am trying to implement a custom field control that is similar to the FieldImage control. However, I would like to bind to multiple images. I decided to create a custom control wrapping a FieldImage in a RadListView. I have already added a OneToMany relationship using the MetadataManager on my custom content to store the images. I am having trouble getting a handle to the DataItem that should be bound to my custom field control. Any help would be greatly appreciated. I have posted my code below:

[FieldDefinitionElement(typeof(ImageListFieldDefinition)), RequiresDataItem]
    public class ImageListField : FieldControl, IRequiresDataItem
    
        protected override System.Web.UI.WebControls.WebControl DescriptionControl
        
            get return this.Container.GetControl<Label>("description", false);
        
 
        protected override System.Web.UI.WebControls.WebControl ExampleControl
        
            get return this.Container.GetControl<Label>("example", false);
        
 
        protected override System.Web.UI.WebControls.WebControl TitleControl
        
            get return this.Container.GetControl<Label>("title", false);
        
 
        
        void ListView_ItemDataBound(object sender, RadListViewItemEventArgs e)
        
            RadListViewItem radDataItem = e.Item;
            Telerik.Sitefinity.Web.UI.Fields.ImageField image = radDataItem.FindControl("imageField") as Telerik.Sitefinity.Web.UI.Fields.ImageField;
            if (image != null)
            
                Definition.Title = "";
                image.Configure(Definition);
            
        
 
        protected override void InitializeControls(Telerik.Sitefinity.Web.UI.GenericContainer container)
        
            DataItem = container.GetDataItemContainer().DataItem as IDataItem;   
            if (DataItem != null)
            
                App.Prepare().SetContentProvider(Definition.ImageProviderName);
                Telerik.Sitefinity.GenericContent.Model.Content data = App.WorkWith().AnyContentItem(Definition.DataFieldType, DataItem.Id).Get();
 
                if (data != null)
                
                    Label label = TitleControl as Label;
                    label.Text = Title;
 
                    IQueryable<ContentLink> imageLinks = data.GetValue(DataFieldName) as IQueryable<ContentLink>;
                    if (imageLinks != null)
                    
                        ListView.DataSource = imageLinks.Take(2).ToList();
                        ListView.ItemDataBound += new EventHandler<RadListViewItemEventArgs>(ListView_ItemDataBound);
                    
                
            
        
 
        void ImageListField_DataBinding(object sender, EventArgs e)
        
            Console.WriteLine(Value.ToString());
        
 
        public override void Configure(Telerik.Sitefinity.Web.UI.Fields.Contracts.IFieldDefinition definition)
        
            base.Configure(definition);
            ImageListFieldDefinition definition2 = definition as ImageListFieldDefinition;
            Definition = definition2;
            DataFieldName = definition2.DataFieldName;
             
            
            Title = definition2.Title;  
            WrapperTag = definition2.WrapperTag;
            CssClass = definition2.CssClass;
            ResourceClassId = definition2.ResourceClassId;
        
 
        private ImageListFieldDefinition Definition
        
            get;
            set;
        
 
        private int MaxNumberImages
        
            get;
            set;
        
 
        protected override string LayoutTemplateName
        
            get return layoutTemplateName;
        
 
        private const string layoutTemplateName = "Pintek.Modules.Businesses.Web.Controls.ImageListField.ascx";
 
        private RadListView ListView
        
            get return this.Container.GetControl<RadListView>("listView", true);
        
 
        private IDataItem dataItem;
        public IDataItem DataItem
        
            get return dataItem;
            set dataItem = value;
        
    

I thought having the RequiresDataItem attribute and Implementing IRequireDataItem would give my class access to the DataItem being bound to my control. However, this does not seem to be the case. Can you help me figure out what I am doing wrong?

Thanks,
Bobby

 

Posted by Community Admin on 17-Jan-2012 00:00

Hi Bobby,

You could use some logic like this:

protected override void OnDataBinding(EventArgs e)
    base.OnDataBinding(e);
    var container = this.GetDataItemContainer();
    object dataItem = null;
 
    if (container != null)
    dataItem = container.DataItem;
 
    if (dataItem != null)
    
    var propDescr = TypeDescriptor.GetProperties(dataItem)[this.DataFieldName];
 
    // Catch the exception in the front end, propagate the exception in the backend
    if (this.IsDesignMode())
    
        this.Value = propDescr.GetValue(dataItem);
    
    else
    
        try
        
            this.Value = propDescr.GetValue(dataItem);
        
        catch
    
    this.DataItem = dataItem as IDataItem;
    this.BoundOnServer = true;
    


Regards,
Lubomir Velkov
the Telerik team
Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items

Posted by Community Admin on 29-Jan-2012 00:00

Hi,

I have tried your suggestion and I have not been able to get it to work. So, I am going to take a step back and explain my goal and ask you the best way to accomplish my goal. I want to associate one or more images with an item. For example, for a User object can have several Images associated with it. I have setup my relationship using OpenAccess to mimic the following sudo code.

public Class User
    public List<Image> Images;

I want to display this images for a user and allows them to add, edit or delete their associated images. I see that you guys have a MultiImageField control but as I understand it, that control is only valid for Products for ecommerce. I want to mimic this behavior for any object that wants to have associated images. With everything that I have tried, I am unable to get the DataItem for the field control. The DataItem in this example would be the User object. As shown in my example below, I have marked by field control to require the DataItem and it also implements the IRequiredDataItem interface. So, there must be something that I don't understand and I was hoping you could help me find a solution. Am I going about this the right way and if so, could you help me understand where I am going wrong?


Thanks,
Bobby

Posted by Community Admin on 01-Feb-2012 00:00

Hi Bobby,

Here is a sample implementation of how to get a reference to the DataItem during the OnDataBinding event -

protected override void OnDataBinding(EventArgs e)
    base.OnDataBinding(e);
    var container = this.GetDataItemContainer();
    object dataItem = null;
 
    if (container != null)
    dataItem = container.DataItem;
 
    if (dataItem != null)
    
    var propDescr = TypeDescriptor.GetProperties(dataItem)[this.DataFieldName];
 
            // Catch the exception in the front end, propagate the exception in the backend
            if (this.IsDesignMode())
            
                this.Value = propDescr.GetValue(dataItem);
            
            else
            
                try
                
                    this.Value = propDescr.GetValue(dataItem);
                
                catch
            
    this.DataItem = dataItem as IDataItem;
    this.BoundOnServer = true;
    

Kind regards,
Lubomir Velkov
the Telerik team
Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items

Posted by Community Admin on 04-Feb-2012 00:00

Hi again,

I am using the code but I am not having any luck getting the DataItem. The OnDataBinding function never gets called. I am sure there is something that I am missing on how to get the DataItem. So, I guess my question is, when and how does the DataBinding function get called. Since, this is an override, I would expect it be be called automatically. Is there something that I need to do when creating the FieldControlDefinitionElement that alerts my control of the DataItem? Below is my latest code. I have attached my field control code below along with your suggestion, unless I am missing something, this code does not give me the DataItem. Can you please help. Does my custom code below work for you? Any help would be appreciated.

Thanks,
Bobby

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Telerik.Sitefinity.Web.UI.Fields;
using Telerik.Sitefinity.Web.UI.Fields.Enums;
using System.Web.UI.WebControls;
using Telerik.Sitefinity.Data;
using Telerik.Sitefinity.Model;
using Telerik.Web.UI;
using Telerik.Sitefinity;
using Telerik.Sitefinity.Model.ContentLinks;
using System.Web.UI;
using Telerik.Sitefinity.Web.UI.Fields.Definitions;
using Telerik.Sitefinity.Web.UI.Fields.Contracts;
using Telerik.Sitefinity.Web;
using Telerik.Sitefinity.Web.UI.Fields.Config;
using System.ComponentModel;
using Telerik.Sitefinity.Web.UI;
using Telerik.Sitefinity.Utilities.TypeConverters;
using Telerik.Sitefinity.Web.Configuration;
using Telerik.Sitefinity.Configuration;
using Telerik.Sitefinity.Localization;
using Telerik.Sitefinity.Modules.Libraries;
using Telerik.Sitefinity.Web.Services;
using Telerik.Sitefinity.Modules.Libraries.Images;
using System.Web.UI.HtmlControls;
using System.Web;
 
namespace Pintek.Modules.Businesses.Web.Controls
    [FieldDefinitionElement(typeof(ImageListFieldElement)), RequiresDataItem]
    public class ImageListField : FieldControl, IRequiresDataItem
    
        private const string ajaxUploadScript = "Telerik.Sitefinity.Resources.Scripts.ajaxupload.js";
        private bool? boundOnServer;
        private Type dataFieldType;
        private IDataItem dataItem;
        private bool displayEmptyImage;
        private const string fieldDisplayModeScript = "Telerik.Sitefinity.Web.UI.Fields.Scripts.FieldDisplayMode.js";
        private const string imageFieldModeScript = "Telerik.Sitefinity.Web.UI.Fields.Scripts.ImageFieldUploadMode.js";
        private const string imageFieldScript = "Telerik.Sitefinity.Web.UI.Fields.Scripts.ImageField.js";
        private const string jqueryUIScript = "Telerik.Sitefinity.Resources.Scripts.jquery-ui-1.8.8.custom.min.js";
 
        private const string reqDataItemScriptFileName = "Telerik.Sitefinity.Web.UI.Fields.Scripts.IRequiresDataItem.js";
        private ImageFieldUploadMode uploadMode;
        private const string uploadServiceUrl = "~/Telerik.Sitefinity.AsyncImageUploadHandler.ashx";
        private static readonly string layoutTemplateName;
 
        private const string imageListFieldScript = "Pintek.Modules.Businesses.Web.Controls.ImageListField.js";
 
        static ImageListField()
        
            layoutTemplateName = "Pintek.Modules.Businesses.Web.Controls.ImageListField.ascx";
        
 
        public ImageListField()
        
            this.displayEmptyImage = true;
        
 
        protected override void OnDataBinding(EventArgs e)
        
            base.OnDataBinding(e);
            var container = this.GetDataItemContainer();
            object dataItem = null;
 
            if (container != null)
                dataItem = container.DataItem;
 
            if (dataItem != null)
            
                var propDescr = TypeDescriptor.GetProperties(dataItem)[this.DataFieldName];
 
                // Catch the exception in the front end, propagate the exception in the backend
                if (this.IsDesignMode())
                
                    this.Value = propDescr.GetValue(dataItem);
                
                else
                
                    try
                    
                        this.Value = propDescr.GetValue(dataItem);
                    
                    catch
                
                this.DataItem = dataItem as IDataItem;
                this.boundOnServer = true;
            
        
 
        public override void Configure(IFieldDefinition definition)
        
            base.Configure(definition);
            IMultiImageFieldDefinition definition2 = definition as IMultiImageFieldDefinition;
            if (definition2 != null)
            
                this.ImageProviderName = definition2.ImageProviderName;
                this.ProviderNameForDefaultImage = definition2.ProviderNameForDefaultImage;
                this.DefaultImageId = definition2.DefaultImageId;
                this.MaxWidth = definition2.MaxWidth;
                this.MaxHeight = definition2.MaxHeight;
                this.DataFieldType = definition2.DataFieldType;
                this.DefaultSrc = definition2.DefaultSrc;
                this.DisplayEmptyImage = definition2.DisplayEmptyImage;
            
        
 
        public override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
        
            ScriptControlDescriptor descriptor = base.GetScriptDescriptors().Last<ScriptDescriptor>() as ScriptControlDescriptor;
            return new ScriptControlDescriptor[] descriptor ;
        
 
        public override IEnumerable<ScriptReference> GetScriptReferences()
        
            string fullName = typeof(ImageListField).Assembly.FullName;
            string name = Config.Get<ControlsConfig>().ResourcesAssemblyInfo.Assembly.GetName().Name;
 
            List<ScriptReference> list = new List<ScriptReference>(base.GetScriptReferences())
                new ScriptReference("Telerik.Sitefinity.Web.UI.Fields.Scripts.IRequiresDataItem.js", name)
            ;
 
            ScriptReference item = new ScriptReference("Telerik.Sitefinity.Resources.Scripts.jquery-ui-1.8.8.custom.min.js", name);
            list.Add(item);
            ScriptReference reference2 = new ScriptReference("Telerik.Sitefinity.Resources.Scripts.ajaxupload.js", name);
            list.Add(reference2);
            ScriptReference reference3 = new ScriptReference(imageListFieldScript, fullName);
            list.Add(reference3);
            ScriptReference reference4 = new ScriptReference("Telerik.Sitefinity.Web.UI.Fields.Scripts.FieldDisplayMode.js", fullName);
            list.Add(reference4);
            return list;
        
 
        protected override string LayoutTemplateName
        
            get return layoutTemplateName;
        
 
        [TypeConverter(typeof(StringTypeConverter))]
        public Type DataFieldType
        
            get
            
                return this.dataFieldType;
            
            set
            
                this.dataFieldType = value;
            
        
 
        public IDataItem DataItem
        
            get
            
                return this.dataItem;
            
            set
            
                this.dataItem = value;
            
        
 
        public string DefaultAlt get; set;
        public Guid DefaultImageId get; set;
        public string DefaultSrc get; set;
 
        protected override WebControl DescriptionControl
        
            get
            
                return null;
            
        
 
        public bool DisplayEmptyImage
        
            get
            
                return this.displayEmptyImage;
            
            set
            
                this.displayEmptyImage = value;
            
        
 
        protected override WebControl ExampleControl
        
            get
            
                return null;
            
        
 
        public string ImageProviderName get; set;
 
        public int? MaxHeight get; set;
        public int? MaxWidth get; set;
        public string ProviderNameForDefaultImage get; set;
 
        protected override void InitializeControls(GenericContainer container)
        
             
        
    
 

Posted by Community Admin on 07-Feb-2012 00:00

Here's what I have gathered from trying to do something similar...
The databinding doesn't happen on the server side in the backend tools. That is taken care of on the client side by the prototype scripts. As far as I can see, the only time the OnDatabinding event will fire is when the control is in read mode on a read only page. I am still working on a solution to this problem. I'm still a little frustrated that something that would've been 6 lines of code on any other site is at 250+ lines right now... and I still haven't gotten to the final solution.

Posted by Community Admin on 09-Feb-2012 00:00

Hello Bobby,

Have you tried overriding OnPreRender or InitializeControls and there use this code

var container = this.GetDataItemContainer();
object dataItem = null;
 
if (container != null)
dataItem = container.DataItem;

The GetDataItemContainer method is an extension method in the Telerik.Sitefinity.Web namespace. It should initialize the dataItem properly.

All the best,
Lubomir Velkov
the Telerik team
Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items

This thread is closed