Working with the data form

Auto-generating DataFormItems for the data field

By default, the DataFormItems will be generated based on the property type. For example, the DataFormNumericItem will be created for the int type property. 

The DataFormItem generation depends on the type and attribute defined for the property. The following table lists the several types of DataFormItem and its constraints for auto generation:

Generated DataFormItem Type Editor Data Type / Attribute

DataFormTextItem

Text Default DataFormItem generated for the String type and the properties with [DataType(DataType.Text)], [DataType(DataType.MultilineText)] and [DataType(DataType.Password)] attributes.

DataFormNumericItem

Numeric Generated for Int, Double, Float, Decimal, Long types and also its nullable property with [DataType(DataType.Currency)] and [DataType("Percent")] attributes.

DataFormDateItem

Date Generated for DateTime type and properties with [DataType(DataType.Date)] and [DataType(DataType.DateTime)] attributes.

DataFormTimeItem

Time Generated for the property with [DataType(DataType.Time)] attribute.

DataFormPickerItem

Picker Generated for Enum type property and the property with [EnumDataTypeAttribute] attribute.

DataFormItem

CheckBox Bool type

You can customize the property settings or cancel the generation of DataFormItem by handling the AutoGeneratingDataFormItem event.

Customize auto generated fields

You can customize or cancel the generated DataFormItem by handling the AutoGeneratingDataFormItem event. This event occurs when the field is auto-generated for public and non-static property of the data object.

dataForm.AutoGeneratingDataFormItem += DataForm_AutoGeneratingDataFormItem;

private void DataForm_AutoGeneratingDataFormItem(object sender, AutoGeneratingDataFormItemEventArgs e)
{

}

AutoGeneratingDataFormItemEventArgs provides the information about the auto-generated.  AutoGeneratingDataFormItemEventArgs.DataFormItem  property returns the newly created DataFormItem.

Cancel DataFormItem generation of the data field

You can cancel the specific DataFormItem adding to the data form by handling the AutoGeneratingDataFormItem event or by defining display attribute to avoid the particular data field being displayed.

Using attributes

You can use the Bindable attribute or set AutoGenerateField to false for canceling the DataFormItem generation.

private int id;
[Display(AutoGenerateField = false)]
public int ID
{
    get
    {
        return id;
    }
    set
    {
        id = value;
        RaisePropertyChanged("ID");
    }
}
private string middleName;
[Bindable(false)]
public string MiddleName
{
    get { return this.middleName; }
    set
    {
        this.middleName = value;
    }
}

Using event

In the following code, the DataFormItem generation for the MiddleName property is canceled by setting the Cancel property to true.

dataForm.AutoGeneratingDataFormItem += DataForm_AutoGeneratingDataFormItem;

private void DataForm_AutoGeneratingDataFormItem(object sender, AutoGeneratingDataFormItemEventArgs e)
{
    if (e.DataFormItem.Name == "MiddleName")
        e.Cancel = true;
}

Changing editor type

You can change the editor of the DataFormItem in the AutoGeneratingDataFormItem event.

In the following code, the editor is changed for IsAvailable field from Bool to Switch.

dataForm.AutoGeneratingDataFormItem += DataForm_AutoGeneratingDataFormItem;

private void DataForm_AutoGeneratingDataFormItem(object sender, AutoGeneratingDataFormItemEventArgs e)
{
    if (e.DataFormItem.Name == "IsAvailable")
        e.DataFormItem.Editor = "Switch";
}

Changing property settings

You can change the property of DataFormItem in the AutoGeneratingDataFormItem event.

Here, Salary data field is restricted from being edited in the data form.

dataForm.AutoGeneratingDataFormItem += DataForm_AutoGeneratingDataFormItem;

private void DataForm_AutoGeneratingDataFormItem(object sender, AutoGeneratingDataFormItemEventArgs e)
{
    if (e.DataFormItem != null)
    {
        if (e.DataFormItem.Name == "Salary")
            e.DataFormItem.IsReadOnly = true;
    }
}

Setting watermark

You can display the watermark in the editor by defining the display attribute or using theAutoGeneratingDataFormItem event.

Using attribute

You can show the watermark in the editor by setting the Prompt in display attribute.

private string middleName;
[Display(Prompt = "Enter middle name")]
public string MiddleName
{
    get { return this.middleName; }
    set
    {
        this.middleName = value;
    }
}

Using event

You can show the watermark in the editor by using the PlaceHolderText property in DataFormItem.

dataForm.AutoGeneratingDataFormItem += DataForm_AutoGeneratingDataFormItem;

private void DataForm_AutoGeneratingDataFormItem(object sender, AutoGeneratingDataFormItemEventArgs e)
{
    if (e.DataFormItem != null)
    {
        if (e.DataFormItem.Name == "Description")
            e.DataFormItem.PlaceHolderText = "Enter description";
    }
}

Changing DataFormItem

You can change the created DataFormItem and assign new DataFormItem.

Loading DataFormTextItem with number keyboard

Here, DataFormTextItem with number keyboard is loaded for numeric value instead of DataFormNumericItem.

dataForm.AutoGeneratingDataFormItem += DataForm_AutoGeneratingDataFormItem;

private void DataForm_AutoGeneratingDataFormItem(object sender, AutoGeneratingDataFormItemEventArgs e)
{
    if (e.DataFormItem != null)
    {
        if (e.DataFormItem.Name == "ID")
            e.DataFormItem = new DataFormTextItem() { Name = "ID", Editor = "Text", InputType = Android.Text.InputTypes.ClassNumber };
    }
}

Adding or removing the data field displayed in the dataForm at runtime

If you want to remove or add data fields item at runtime, you can use the RefreshLayout method which auto-generates the DataFormItem where you can skip certain item from display. By default, it will generate the canceled items initially. If you want to regenerate all the items, you should pass argument as true.

In the following code snippet, items are auto generated based on refreshLayout flag where you can change flag at runtime and call RefreshLayout method to add or remove items being displayed in the data form at runtime.

var linearLayout = new LinearLayout(this);
dataForm  = new SfDataForm(this);
dataForm.DataObject = new ContactsInfo();
dataForm.AutoGeneratingDataFormItem += DataForm_AutoGeneratingDataFormItem;
linearLayout.AddView(dataForm);
linearLayout.Orientation = Orientation.Vertical;
var button = new Button(this);
button.Text = "More Fields";
button.Click += Button_Click;
linearLayout.AddView(button);

private void DataForm_AutoGeneratingDataFormItem(object sender, AutoGeneratingDataFormItemEventArgs e)
{
    if (e.DataFormItem != null)
    {
        if (!refreshLayout)
        {
            if (e.DataFormItem.Name.Equals("MiddleName") || e.DataFormItem.Name.Equals("LastName"))
                e.Cancel = true;
        }
        else
        {
            if (e.DataFormItem.Name == "GroupName")
                e.Cancel = true;
        }
    }
}

Initial rendering of data form items in Xamarin.Android DataForm

If you want to generate the MiddleName and LastName fields at runtime, you should set refreshLayout flag to true and call the RefreshLayout method which triggers AutoGeneratingDataFormItem event again and generates the items based on refreshLayout flag.

private void Button_Click(object sender, System.EventArgs e)
{
    refreshLayout = true;
    dataForm.RefreshLayout();
}

Here, the MiddleName and LastName fields are generated at runtime after clicking the more field button.

Adding data form fields at run time in Xamarin.Android DataForm

TheGroupName field is displayed initially in the data form. If you want to remove it at runtime, you should set refreshLayout flag to true and pass the argument as true inRefreshLayout method. It triggers AutoGeneratingDataFormItem event for all the fields where you can cancel `GroupName’ field item generation.

private void Button_Click(object sender, System.EventArgs e)
{
    refreshLayout = true;
    dataForm.RefreshLayout(true);
}

Here, the GroupName field is removed at runtime.

Removing data form fields at run time in Xamarin.Android DataForm

You can download the sample from here.

DataFormItemManager

The DataFormItemManager creates DataFormItems collection and handles value reflection and validation. It also overrides to handle the get and set property values from and to the data object.

Manually generate DataFormItems for DataObject

By default, DataFormItems will be generated based on DataObject. If you need to generate DataFormItems manually, override the DataFormItemManager class and set it to SfDataForm.ItemManager.

To create DataFormItems, override the GenerateDataFormItems method.

public class DataFormItemManagerExt : DataFormItemManager
{       
    public DataFormItemManagerExt(SfDataForm dataForm) : base(dataForm)
    {
            
    }
    protected override List<DataFormItemBase> GenerateDataFormItems(PropertyDescriptorCollection itemProperties, List<DataFormItemBase> dataFormItems)
    {
        var items = new List<DataFormItemBase>();
        foreach (PropertyDescriptor propertyInfo in itemProperties)
        {
            DataFormItem dataFormItem;
            if (propertyInfo.Name == "ContactNumber")
                dataFormItem = new DataFormTextItem() { Name = propertyInfo.Name, Editor = "Text", InputType = Android.Text.InputTypes.ClassNumber };
            else if (propertyInfo.Name == "FirstName")
                dataFormItem = new DataFormTextItem() { Name = propertyInfo.Name, Editor = "Text" };
            else
                dataFormItem = new DataFormTextItem() { Name = propertyInfo.Name, Editor = "Text" };
            items.Add(dataFormItem);
        }

        return items;
    }
}

dataForm.DataObject = new ContactsInfo();
dataForm.ItemManager = new DataFormItemManagerExt(dataForm);

You can download the source code of this demo from here GenerateDataFormItemsForDataObject

Manually generate DataFormItems for data dictionary

You can load the dataform with custom dictionary by generating DataFormItems manually. To create DataFormItems from dictionary, override the GenerateDataFormItems method.

public class DataFormItemManagerExt : DataFormItemManager
{
    Dictionary<string, object> dataFormDictionary;
    public DataFormItemManagerExt(SfDataForm dataForm, Dictionary<string, object> dictionary) : base(dataForm)
    {
        dataFormDictionary = dictionary;
    }


    protected override List<DataFormItemBase> GenerateDataFormItems(PropertyDescriptorCollection itemProperties, List<DataFormItemBase> dataFormItems)
    {
        var items = new List<DataFormItemBase>();
        foreach (var key in dataFormDictionary.Keys)
        {
            DataFormItem dataFormItem;
            if (key == "ID")
                dataFormItem = new DataFormNumericItem() { Name = key, Editor = "Numeric", MaximumNumberDecimalDigits = 0 };
            else if (key == "Name")
                dataFormItem = new DataFormTextItem() { Name = key, Editor = "Text" };
            else
                dataFormItem = new DataFormTextItem() { Name = key, Editor = "Text" };

            items.Add(dataFormItem);
        }
         
        return items;
    }
}

var dictionary = new Dictionary<string, object>();
dictionary.Add("ID", 1);
dictionary.Add("Name", "John");
dataForm.ItemManager = new DataFormItemManagerExt(dataForm, dictionary);

Handling reading and writing values to and from the dictionary

By default, the dictionary value will be shown in corresponding editor and value changes in editor will be committed again in dictionary value by using the GetValue and SetValue override methods in DataFormItemManager.

Here, the value is read and written from/to dictionary instead of the data object.

public class DataFormItemManagerExt : DataFormItemManager
{
    Dictionary<string, object> dataFormDictionary;
    public DataFormItemManagerExt(SfDataForm dataForm, Dictionary<string, object> dictionary) : base(dataForm)
    {
        dataFormDictionary = dictionary;
    }

    public override object GetValue(DataFormItem dataFormItem)
    {
        var value = dataFormDictionary[dataFormItem.Name];
        return value;
    }

    public override void SetValue(DataFormItem dataFormItem, object value)
    {
        dataFormDictionary[dataFormItem.Name] = value;
    }

}

Loading dataform with dictionary in Xamarin.Android DataForm

Here, the dataform is loaded with field from dictionary.

You can download the source code of this demo from GenerateDataFormItemsForDictionary

Binding with dynamic data object

You can also load the dynamic object in SfDataForm DataObject property and by default text editor will be generated for each dynamic object property. You can change the editor of DataFormItem for dynamic object property data type (default string) by using the AutoGeneratingDataFormItem event. You can find details about this here

dataForm.DataObject =new DynamicDataModel().ExpandoObject;
dataForm.AutoGeneratingDataFormItem += DataForm_AutoGeneratingDataFormItem;

private void DataForm_AutoGeneratingDataFormItem(object sender, AutoGeneratingDataFormItemEventArgs e)
{
    if (e.DataFormItem != null && e.DataFormItem.Name == "DateOfBirth")
        e.DataFormItem.Editor = "Date";
}

public class DynamicDataModel
{
    public dynamic ExpandoObject;

    public dynamic DynamicObject;
    public DynamicDataModel()
    {
        ExpandoObject = new ExpandoObject();
        ExpandoObject.FirstName = "John";
        ExpandoObject.LastName = "";
        ExpandoObject.DateOfBirth = DateTime.Now.Date;
        ExpandoObject.Email = "";

        DynamicObject = new Data();
        DynamicObject.FirstName = "John";
        DynamicObject.LastName = "";
        DynamicObject.DateOfBirth = DateTime.Now.Date;
        DynamicObject.Email = "";
    }
}

public class Data : DynamicObject, IDictionary<string, object>
{
    Dictionary<string, object> list = new Dictionary<string, object>();

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        list[binder.Name] = value;
        return true;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        return list.TryGetValue(binder.Name, out result);
    }

    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return list.Keys;
    }

    public void Add(string key, object value)
    {
        this.list.Add(key, value);
    }

    public bool ContainsKey(string key)
    {
        return this.list.ContainsKey(key);
    }

    public ICollection<string> Keys
    {
        get { return this.list.Keys; }
    }

    public bool Remove(string key)
    {
        return this.list.Remove(key);
    }

    public bool TryGetValue(string key, out object value)
    {
        return this.list.TryGetValue(key, out value);
    }

    public ICollection<object> Values
    {
        get { return this.list.Values; }
    }

    object IDictionary<string, object>.this[string key]
    {
        get { return this.list[key]; }
        set { this.list[key] = value; }
    }

    public void Add(KeyValuePair<string, object> item)
    {
        this.list.Add(item.Key, item.Value);
    }

    public void Clear()
    {
        this.list.Clear();
    }

    public bool Contains(KeyValuePair<string, object> item)
    {
        return this.list.Contains(item);
    }

    public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
    {

    }

    public int Count
    {
        get { return this.list.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(KeyValuePair<string, object> item)
    {
        return this.list.Remove(item.Key);
    }

    public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
    {
        return this.list.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.list.GetEnumerator();
    }
}

Binding with dynamic data object in Xamarin.Android DataForm

You can download the sample from here