Aaron Robertson-Hodder

Hi,

I have created a custom field type for SharePoint that represents hierachical data from and XML file (stored in SharePoint) in a TreeView.

I have created properties on the field to store the location of the source XML file and whether to render the TreeView as a multi-select, ie. with checkboxes.

I created the properties in the _fldtypes xml definition using the property schema elements as they are only simple textboxes and checkboxes. My problem is that these properties work ok to add the form and save, but when you try and edit the field, the values in the properties reset to <field description> value. ie. they are not what was set and they are not even the default!

This is supposed to be out of the box stuff according to the SDK, but does not seem to work. The SDK is pretty thin when it comes to this stuff. Does anyone have any ideas, or have this working

Thanks.

Aaron.




Re: SharePoint - Development and Programming Custom Field Type Properties

Antons Mislevics

Hi, Aaron,

I was investigating the same issue for some time, and found a workaround, using custom control, to render field properties. I*ll try to describe my solution step-by-step.

1. Create your field definition in FLDTYPES_xxx.XML file, and add FieldEditorUserControl property to your custom field definition. Define fields for all custom properties of your custom field definition in PropertySchema section. If you don*t define some property here, you are not able to set it*s value using field.SetCustomProperty(※fieldname§) later. Looks like WSS just ignores it. In addition, you should set Hidden attribute of all Field elements in PropertySchema section to True. Otherwise, fields will be rendered in user interface.

For example, my custom property definition looks like:

<FieldType>

<Field Name="TypeName">MyCustomField</Field>

<Field Name="TypeDisplayName">My Custom Field</Field>

<Field Name="ParentType">Text</Field>

<Field Name="FieldTypeClass">MyFields.MyCustomField, MyFields, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f3bf8ec1b42660c3</Field>

<Field Name="Sortable">TRUE</Field>

<Field Name="Filterable">TRUE</Field>

<Field Name="FieldEditorUserControl">/_controltemplates/MyCustomFieldControl.ascx</Field>

<PropertySchema>

<Fields>

<Field Name="MyCustomProperty" DisplayName="My Custom Property"

Type="Text" Hidden="TRUE">

</Field>

</Fields>

</PropertySchema>

...

<!--RenderPatterns -->

</FieldType>

2. The next step is to create .ascx control file in TEMPLATE\CONTROLTEMPLATES directory. In my sample it*s MyCustomFieldControl.ascx. My control is very simple:

<%@ Control Language="C#" Inherits="MyFields.SelectMyCustomProperty,MyFields, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f3bf8ec1b42660c3" AutoEventWireup="false" compilationMode="Always" %>

<%@ Register TagPrefix="wssuc" TagName="InputFormControl" src="~/_controltemplates/InputFormControl.ascx" %>

<%@ Register TagPrefix="wssuc" TagName="InputFormSection" src="~/_controltemplates/InputFormSection.ascx" %>

<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Import Namespace="Microsoft.SharePoint" %>

<wssuc:InputFormSection runat="server" id="MySections" Title="My Custom Section">

<Template_InputFormControls>

<wssuc:InputFormControl runat="server"

LabelText="My Custom Property">

<Template_Control>

<asp:DropDownList id="ctlMyCustomProperty" runat="server">

</asp:DropDownList>

</Template_Control>

</wssuc:InputFormControl>

</Template_InputFormControls>

</wssuc:InputFormSection>

As you can see, it has only one drop-down control. The idea is, that user can select some value from drop-down, and it*ll be stored in custom property of our custom field.

3. Now you should create .NET classes for your custom field type and user control. For custom field type:

public class MyCustomField : SPFieldText

{

public MyCustomField(SPFieldCollection fields, string fieldName)

: base(fields, fieldName)

{

this.Init();

}

public MyCustomField(SPFieldCollection fields, string typeName, string displayName)

: base(fields, typeName, displayName)

{

this.Init();

}

private void Init()

{

this.MyCustomProperty = this.GetCustomProperty("MyCustomProperty")+"";

}

private string myCustomProperty;

public string MyCustomProperty

{

get

{

return this.myCustomProperty;

}

set

{

this.myCustomProperty = value;

}

}

public override void Update()

{

this.SetCustomProperty("MyCustomProperty", this.MyCustomProperty);

base.Update();

}

}

As you can see, I*ve created string attribute and property, for my field type*s CustomProperty. In Init method, I populate it with stored custom property value. Finally, I*ve overridden Update method, to save modified custom property values to the field. I*ll describe what*s happening in WSS during field update and why all of these steps are necessary a bit later.

The code for my user control:

public class SelectMyCustomProperty : UserControl, IFieldEditor

{

protected DropDownList ctlMyCustomProperty;

private string value = "";

public void InitializeWithField(SPField field)

{

MyCustomField myField = field as MyCustomField;

if(myField != null)

this.value = myField.MyCustomProperty+"";

}

public void OnSaveChange(SPField field, bool isNew)

{

string value = this.ctlMyCustomProperty.SelectedValue;

MyCustomField myField = field as MyCustomField;

myField.MyCustomProperty = value;

}

public bool DisplayAsNewSection

{

get

{

return true;

}

}

protected override void CreateChildControls()

{

base.CreateChildControls();

// load values

ctlMyCustomProperty.Items.Add("");

ctlMyCustomProperty.Items.Add("aaa");

ctlMyCustomProperty.Items.Add("bbb");

ctlMyCustomProperty.Items.Add("ccc");

if(!this.IsPostBack)

{

ListItem item = ctlMyCustomProperty.Items.FindByText(this.value);

if(item != null)

item.Selected = true;

}

}

}

As described in SDK user control class should implement IFieldEditor interface. It has 2 methods and 1 property:

InitializeWithField 每 is called, when control is initialized;

OnSaveChanged 每 called, when user clicks OK button in field properties window;

DisplayAsNewSection 每 true if control should be displayed as new section in field properties form.

In CreateChildControls we just populate our drop-down list with some values.

The crucial point here is, not to try setting values directly to custom properties in OnSaveChange (like, myField.SetCustomProperty(※MyCustomProperty§, value)). Even if you call myField.Update() afterwards, the value won*t be saved! More precisely, it will be saved, but then it will be overwritten by current custom property value. Looks like SharePoint calls OnSaveChange in our control, then continues to update values of custom properties from other sources (and our Hidden field in PropertySchema section is one of them), and finally calls field*s Update method. That*s why we need to override Update method in our custom field class and set custom property values there 每 at that point SharePoint has done his job, and won*t change any property values.

Hope this helps!

Best regards,

Anton






Re: SharePoint - Development and Programming Custom Field Type Properties

Aaron Robertson-Hodder

That all sounds pretty good to me!

I had a go with onSaveChanges and came to a similar conclusion to you about the update firing after and nuking the props!

I will try this method out as soon as I can!

Thanks again!






Re: SharePoint - Development and Programming Custom Field Type Properties

Aaron Robertson-Hodder

Anton,

I have tried your code and found that it allows me to edit and re-save the properties ok, but that it does not set the property when the field is first created. The overridden Update method does not get called when adding a new field.

Can you confirm that this behaviour is the same for you in case I have managed to break something in my copying of it

Thanks!






Re: SharePoint - Development and Programming Custom Field Type Properties

Smidje

Aaron,

I have tried this and can confirm the behaviour you're describing. The overriden Update only get called when editing a field and not when it's created.
/Johan Smidje





Re: SharePoint - Development and Programming Custom Field Type Properties

Anton Myslevich

Hi, Aaron

 

You are right 每 custom property value is not set, when the new field is created. I*ve missed this point somehow while developing my workaround. Sorry for that.

 

I*ve investigated it, and came to the following conclusions:

1)     Update method is not called, when new custom field item is created. Only OnAdded method is firing.

2)      We can not solve this problem, by just including something like:

      public override void OnAdded(SPAddFieldOptions op)

      {

            base.OnAdded(op);

            Update();

}

in our custom field type class. SharePoint calls OnSaveChange(SPField field, bool isNew) in our custom control, providing field object, but before saving it creates the new one! That*s why custom property values will be lost.

 

So we need to save our custom property values between object instances. This may be achieved in 2 ways: storing value in session or in some static member. I*ve chosen the second approach, because we should take in mind, that user may change custom properties of our field not only using web interface, but from API as well. In this case, I think, Session object will not be available. In addition, we should take in mind that multiple users (or multiple connections for one user) may try to modify fields of the same type across the site simultaneously. That*s why we can*t just store our custom property value in static member, but we should create static Dictionary of values, and use some Identity for each connection. I*ve chosen to use current context object hash code as identity: SPContext.Current.GetHashCode().

In addition, I think, we should avoid always storing value in static member, cause it just gives us additional head ache 每 how to determine when the value is not used any more, and remove it from our static member If we won*t do it, we will create a memory leak.

 

So we need to modify our code like:

 

public class MyCustomField : SPFieldText

{

      public MyCustomField(SPFieldCollection fields, string fieldName)

            : base(fields, fieldName)

      {

            this.Init();

      }

      public MyCustomField(SPFieldCollection fields, string typeName, string displayName)

            : base(fields, typeName, displayName)

      {

            this.Init();

      }

      private void Init()

      {

            this.MyCustomProperty = this.GetCustomProperty("MyCustomProperty")+"";

      }

      private static Dictionary<int, string> updatedMyCustomProperty = new Dictionary<int, string>();

      private string myCustomProperty;

      public string MyCustomProperty

      {

            get

            {

                  return updatedMyCustomProperty.ContainsKey(ContextId) updatedMyCustomProperty[ContextId] : myCustomProperty;

            }

            set

            {

                  this.myCustomProperty = value;

            }

      }

      public void UpdateMyCustomProperty(string value)

      {

            updatedMyCustomProperty[ContextId] = value;

      }

      public int ContextId

      {

            get

            {

                  return SPContext.Current.GetHashCode();

            }

      }

      public override void Update()

      {

            this.SetCustomProperty("MyCustomProperty", this.MyCustomProperty);

            base.Update();

            if(updatedMyCustomProperty.ContainsKey(ContextId))

                  updatedMyCustomProperty.Remove(ContextId);

      }

      public override void OnAdded(SPAddFieldOptions op)

      {

            base.OnAdded(op);

            Update();

      }

}

 

public class SelectMyCustomProperty : UserControl, IFieldEditor

{

      /* ... */

     

      public void OnSaveChange(SPField field, bool isNew)

      {

            string value = this.ctlMyCustomProperty.SelectedValue;

            MyCustomField myField = field as MyCustomField;

            if(isNew)

                  myField.UpdateMyCustomProperty(value);

            else

                  myField.MyCustomProperty = value;

      }

     

      /* ... */

}

 

 

Hope this helps!

 

Anton






Re: SharePoint - Development and Programming Custom Field Type Properties

AndersR

Hi Anton,

very helpful! You might what to do a blog post about it, since i have seen several others asking about this (myself included:-)

have you reported this to MS as a bug It does seem like alot of hazzle for something that ought to work OOTB

Aron/Anton

i have also created a treeview custom field type (gets populated with the Site provider the user selects when creating field). However i ran into some weird limitations: even though i inherit directly from SPField, i cannot select items that add up to more than 255 chars. This has something to do with the way i store the selected nodes (custom ToString method that seperates selections with ;# just like SPFieldMultiChoice and SPFieldMultiColumn).

I tried all sorts of workarounds like selecting Note as display field in xml file, but this only stops wss from throwing an exception and then cuts the output at 255 chars. I know this can be done as other fields can display alot more than 255 chars (like SOFieldMultiChoice that displays +7000 chars as limited in UI).

Have anyone got a workaround for this

My post about this issue

thx

AndersR






Re: SharePoint - Development and Programming Custom Field Type Properties

Smruti

Hi Antons,

I am using this article to create a custom dropdown field which will populate values from the database. I should be able to select any value from this dropdown custom field and depending on selection another set of values should come from the database which will be again another custom dropdown Field.

When i am following this article none of the values are populated in my custom dropdown field.even it is not diplaying values that i add manually.

Can you please suggest me some workaround for this

As it is urgent and inevitable for my sharepoint portal





Re: SharePoint - Development and Programming Custom Field Type Properties

Anton Myslevich

Hi Smruti,

As I understand from your post, this solution is very similar, to the one, described in my sample. The only difference is that there will be two drop-downs in your custom field control, and values will be filled in from database. So, I think, you*ve just missed some part of my implementation. Please, review your code carefully and compare it to mine 每 may be you will find the missing bit.

If it still doesn*t work, feel free to post some simplified code sample of your solution here. It will help to understand this issue.

Best regards,

Anton






Re: SharePoint - Development and Programming Custom Field Type Properties

Smruti

Hi Anton

Thanks for your response.Actually i managed to create a custom field which has a dropdown list ,t hat is fetching data from a database in SQL server.

Now i need to implement another custom field having a dropdown list which is dependent on the value selected in the first custom field's dropdown.

Please suggest me how to proceed .Got really stuck up in this .

Regards

Smruti





Re: SharePoint - Development and Programming Custom Field Type Properties

Rail Sabirov

I don't understand, why we can not do like this:

internal string ValueColumn
{
get { return GetCustomProperty("valueColumn").ToString();
set { SetCustomProperty("valueColumn", value); }
}





Re: SharePoint - Development and Programming Custom Field Type Properties

Smruti

Hi Anton

Thanks for your response.Actually i managed to create a custom field which has a dropdown list ,t hat is fetching data from a database in SQL server.

Now i need to implement another custom field having a dropdown list which is dependent on the value selected in the first custom field's dropdown.

Please suggest me how to proceed .Got really stuck up in this .

Regards

Smruti





Re: SharePoint - Development and Programming Custom Field Type Properties

Mark3838

I just built this exact solution. Thanks to Anton's code for much help!

My solution has a dropdown available when the user creates a column (which reads values for names describing sql queries).

Example: If user chooses "Departments" then at runtime the dropdown will be populated by a sql query which retrieve department names. If the user chooses "products" then at runtime a sql query will run displaying the list of products etc....

Nothing else tricky except in CreateChildControls you retrieve the value from the hidden property defined in the XML file and use that value to populate the dropdown

protected override void CreateChildControls()

{

if (Field == null) return;

base.CreateChildControls();

if (ControlMode == Microsoft.SharePoint.WebControls.SPControlMode.Display)

return;

cboData = (DropDownList)TemplateContainer.FindControl("cboData");

if (cboData == null)

{

throw new ArgumentException("cboData is null. Corrupted SQLDropdownFieldControl.ascx file.");

}

if (!Page.IsPostBack)

{

try

{

string ddtype = Field.GetCustomProperty("DropdownType").ToString();

Common.FillCombobox(cboData, ddtype);

}

catch (Exception ex)

{

//we need to do something here, I am not sure what the best pratice is though.

}

}

}

}





Re: SharePoint - Development and Programming Custom Field Type Properties

Smruti

Dear Mark , Anton

Thanks for the solution.

I was able to create dependent listboxes in my custom fieldtypes.

I have now created around 15 custom fieldtypes.

So when i go to a list or document library and say Create column

It is showing me 15 custom fieldtypes apart from ( default ones from Sharepoint such as Single line of text, choice,multiple lines of text ..etc )

So now my problem is i want to group all my custom fieldtypes under one fieldtype and lets say name it as My Custom Field type which should appear in create column page and when i select it , it should populate all my 15 custom field types in form of radio buttons..from which i should select and create my column in a list or a document library.





Re: SharePoint - Development and Programming Custom Field Type Properties

Hardeep Singh

I'm trying to create a field type which will have the link to another page by default and I need to get the current Item context and pass the item ID as query string. I can get the list ID by this.ParentList.id but how do I get the List Item Id Please help.

Thanks!