Tuesday, February 2, 2010

PropertyGrid Control and Complex Type Object

For one of my last projects I decided to use the PropertyGrid Control, I assume it would be nice to use it as an editor for my complex type object.

Cheerful as I was it turn to be not such an easy mission, apparently the PropertyGrid Control can handle privitive types, collections and enums, but when it comes to complex type objects it need to be told what will be the way to edit them.

Here is the PropertyGrid as it was displayed originaly:








As you can see, the complex type “Manager” is not editable !!!

In order to make it editable it needs to be define with Editor Attribute, as such:

[Editor(typeof(MyUITypeEditor),typeof(UITypeEditor))]

In order to use this custom UITypeEditor, we’ll need to create two thins:
1. MyUITypeEditor - Based on the UITypeEditor.
2. UIMyClassEditorForm – The form that will be displayed whenever we’ll click the complex type for editing.

I really wanted this to be generic as I could so my UIMyClassEditorForm contains a PropertyGrid Control for editing the object pass through.

So now, when clicking the Manager property we should be abe to edit it and the screen will look like this:


Here is the source code:

Person.cs
public class Person
{
public enum City
{
NewYork,
TelAviv,
Rome
}

public Person() { }
public Person(string firstName, string lastName,int age, City myCity)
{
this.FirstName = firstName;
this.LastName = lastName;
this.Age = age;
this.MyCity = myCity;
this.Manager = new Person();
}

List children = new List();
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public City MyCity { get; set; }
public List Children
{
get { return children; }
}

[Editor(typeof(MyUITypeEditor),typeof(UITypeEditor))]
public Person Manager{ get; set; }

public override string ToString()
{
return string.Format("{0},{1}", FirstName, LastName);
}

}
MyProperyGridSampleForm.cs
public partial class MyProperyGridSampleForm : Form
{


public MyProperyGridSampleForm()
{
InitializeComponent();
}

private void EditPersonButton_Click(object sender, EventArgs e)
{
Person myTestEntity = new Person();
myTestEntity.FirstName = "John";
myTestEntity.LastName = "Smith";
myTestEntity.Age = 29;
myTestEntity.MyCity = Person.City.NewYork;
myTestEntity.Manager = new Person("Liron", "Leybovich", 33, Person.City.TelAviv);

propertyGrid1.SelectedObject = myTestEntity;
}
}
UIMyClassEditorForm.cs
public partial class UIMyClassEditorForm : Form
{
public object MyClass;
public IWindowsFormsEditorService _wfes;


public UIMyClassEditorForm()
{
InitializeComponent();
TopLevel = false;
}

private void UIMyClassEditorForm_Load(object sender, EventArgs e)
{
this.propertyGrid1.SelectedObject = MyClass;
}

protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
_wfes.CloseDropDown();
}

MyUITypeEditor.cs
public class MyUITypeEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(
ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}


#region Edit Value
public override object EditValue(ITypeDescriptorContext context,
IServiceProvider provider,
object value)
{
IWindowsFormsEditorService wfes = provider.GetService(
typeof(IWindowsFormsEditorService)) as
IWindowsFormsEditorService;

if (wfes == null) { return value; }

UIMyClassEditorForm form = new UIMyClassEditorForm();

if (value != null)
{
form.MyClass = value;
}
else
{
form.MyClass = new object();
}

form._wfes = wfes;

wfes.DropDownControl(form);

return value;
}
#endregion

}

All the best….
Liron

Tuesday, January 19, 2010

How to update ShowInFileDlg programmatically


At some point in your SharePoint development life time you will probably need to update the ShowInFileDlg attribute of a Site Column.

If you will try to do that by a feature and update the XML with new Field's attributes values, the changes will only take effect on new instances, and will not effect on exsisting ContentTypes or Lists that use these Site Columns.

Programmatically updating the Site Column and the Content Type, the changes will be deployed to the children as well.

Here is a generic method for updating any attribute of the Field element and forcing the changes to the Content Type and its children:

public void UpdateFieldAttribute(string siteCollectionURL, string siteColumnName, string contentTypeName, string fieldAttributeName, string fieldAttributeValue)
{
SPSite site = null;
SPWeb web = null;
try
{
using (site = new SPSite(siteCollectionURL))
{
using (web = site.OpenWeb())
{

SPField siteColumn = site.RootWeb.Fields.GetField(siteColumnName);
XmlDocument fieldSchemaXml = new XmlDocument();
fieldSchemaXml.LoadXml(siteColumn.SchemaXml);
XmlNode fieldXmlNode = fieldSchemaXml.SelectSingleNode("Field");
XmlAttribute attr = fieldXmlNode.Attributes[fieldAttributeName];
attr.InnerText = fieldAttributeValue; ;

siteColumn.SchemaXml = fieldXmlNode.OuterXml;
siteColumn.Update(true);

SPContentType contentType = site.RootWeb.ContentTypes[contentTypeName];

contentType.Update(true, false); //Updating the content type is important or the changes will not be reflected
}
}
}
finally
{
if (web != null)
web.Dispose();
if (site != null)
site.Dispose();
}
}

Thursday, January 14, 2010

SharePoint Search Services Error HRESULT: 0x80040D1B

You might facing an issue when clicking the Search Settings in your SSP:
Exception from HRESULT: 0x80040D1B at Microsoft.SharePoint.Portal.Search.Admin.Pages.SearchAdminPageBase.ErrorHandler(Object sender, EventArgs e) at Microsoft.SharePoint.Portal.Search.Admin.Pages.SearchSSPAdminPageBase.OnError(EventArgs e) at System.Web.UI.Page.HandleError(Exception e) at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) at System.Web.UI.Page.ProcessRequest() at System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context) at System.Web.UI.Page.ProcessRequest(HttpContext context) at ASP._layouts_searchsspsettings_aspx.ProcessRequest(HttpContext context) at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

In order to solve this issue you will need to do the following:

  1. Make sure to add the service account to the local SharePoint backup operators group on all SharePoint servers.

  2. Set your service name principle account for your SSP on your SharePoint application server. In order to complete this operation you must install Window support tools. Run these commands and all SharePoint web servers:
    setspn.exe -A HTTP/sharepointsite domain\service account
    setspn.exe -A HTTP/sharepoint.domainname site domain\service account

    (Make sure you have the right privileges to run the setspn.exe, usually you will need the domain admin)

  3. On Central administration - Operations - Services on Server
    Stop Office SharePoint Server Search.

  4. Then go to Central administration - Operations - Service accounts.
    Click on web application pool.
    Under web service choose Windows SharePoint Service Web application.
    Under application pool, choose your Shared Service Provider application pool account.
    Re-enter your password and click ok.

  5. Do iisrest in all SharePoint servers.

  6. In Central administration - Operations - Services on Server
    Restart your Office Server Search.

  7. In Central administration - Application Management - Create or configure Farm's Shared Services.
    In the default SSP and click edit properties.
    In the SSP service credentials enter username and password.
    In the index server category select your index server and then click ok.
    Make sure you wait about 5 minutes. (You need to do this for all SSPs)

Good luck...

Liron