creating fields programmatically in featureactivated - sharepoint

I need help creating a method that allows me to create a bunch of fields during featureactivated. C#. There are about 15 different fields, of varying types, and I'd like to be able to pass in all of the necessary attributes to create each field.
Anyone have any sample code or guidance on this?

Okay I found part of the answer...quite a bit to go though. I found the following utility method here:
public void AddCustomField(SPWeb web, string fieldType, string fieldName, bool isRequired, string defaultValue, string fieldGroup)
{
//Check if the field is there or not already
if (!web.Fields.ContainsField(fieldName))
{
//Initializing a SPField instance
SPField customField;
//Creating a new filed
customField = web.Fields.CreateNewField(fieldType, fieldName);
//Assigning a group
customField.Group = fieldGroup;
//Sets this field is required field
customField.Required = isRequired;
//Assigning a default value
customField.DefaultValue = defaultValue;
//Adding the newly created field to SPweb
web.Fields.Add(customField);
}
}
However, I'm not sure how to call this method, could someone give me an example?

Related

Check if ListBoxFor selectedValues is null before display in view?

I have a number of ListBoxFor elements on a form in edit mode. If there was data recorded in the field then the previously selected items are displaying correctly when the form opens. If the field is empty though an error is thrown as the items parameter cannot be null. Is there a way to check in the view and if there is data to use the ListBoxFor with the four parameters but if there isn't to only use three parameters, leaving out the selected items?
This is how I'm declaring the ListBoxFor:
#Html.ListBoxFor(model => model.IfQualityPoor, new MultiSelectList(ViewBag.IfPoor, "Value", "Text", ViewBag.IfQualityPoorSelected), new { #class = "chosen", multiple = "multiple" })
I'm using the ViewBag to pass the ICollection which holds the selected items as the controller then joins or splits the strings for binding to the model field. The MultiSelectLists always prove problematic for me.
Your question isn't entirely clear, but you're making it way harder on yourself than it needs to be using ListBoxFor. All you need for either DropDownListFor or ListBoxFor is an IEnumerable<SelectListItem>. Razor will take care of selecting any appropriate values based on the ModelState.
So, assuming ViewBag.IfPoor is IEnumerable<SelectListItem>, all you need in your view is:
#Html.ListBoxFor(m => m.IfQualityPoor, (IEnumerable<SelectListItem>)ViewBag.IfPoor, new { #class = "chosen" })
The correct options will be marked as selected based on the value of IfQualityPoor on your model, as they should be. Also, it's unnecessary to pass multiple = "multiple" in in your htmlAttributes param, as you get that just by using ListBoxFor rather than DropDownListFor.
It's even better if you use a view model and then add your options as a property. Then, you don't have to worry about casting in the view, which is always a good way to introduce runtime exceptions. For example:
public class FooViewModel
{
...
public IEnumerable<SelectListItem> IfQualityPoorOptions { get; set; }
}
Then, you set this in your action, before returning the view (instead of setting ViewBag). Finally, in your view:
#Html.ListBoxFor(m => m.IfQualityPoor, Model.IfQualityPoorOptions, new { #class = "chosen" })
Much simpler, and you'll never have any issues doing it that way.
UPDATE (based on comment)
The best way to handle flattening a list into a string for database storage is to use a special property for that, and then custom getter and setter to map to/from. For example:
public string IfQualityPoor
{
get { return IfQualityPoorList != null ? String.Join(",", IfQualityPoorList) : null; }
set { IfQualityPoorList = !String.IsNullOrWhiteSpace(value) ? value.Split(',').ToList() : null; }
}
[NotMapped]
public List<string> IfQualityPoorList { get; set; }
Then, you post to/interact with IfQualityPoorList, and the correct string will be set in the database automatically when you save.

Using "Content" view as EditorFor template in an "Embedded Resource" view

I have an "Embedded Resource" view. In this view I am using the following model
public class TestModel
{
public TestModel()
{
CustomModel1 = new CustomModel1 ();
CustomModel2 = new CustomModel2();
}
public CustomModel1 CustomModel1 { get; set; }
public CustomModel2 CustomModel2{ get; set; }
}
In this view I have a form and inside it I am using #Html.EditorFor instead of #Html.Partial, because when I use #Html.Partial the CustomModel1 passed to the action (when the form is submitted) is empty.
#Html.EditorFor(m => m.CustomModel1, Constants.CustomEmbeddedView1)
However when I use #Html.EditorFor and pass as a template a "Content" view
#Html.EditorFor(m => m.CustomModel1, "~/Views/Common/_CustomPartialView.cshtml")
I get the following error:
The model item passed into the dictionary is null, but this dictionary requires a non-null model item of type 'System.Int32'.
If I set the "Content" view to be an "Embedded Resource" everything works fine.
Is there any way to resolve this problem? Perhaps there is another solution to the model binding problem instead of using #Html.EditorFor.
I found a solution to my problem. I still do not know why the error is thrown, but at least I fixed the model binding.
The problem with the model binding, is that when you call #Html.Partial
#Html.Partial("~/Views/Common/_CustomPartialView.cshtml", Model.CustomModel1)
The elements that are dispayed (I use #Html.EditorFor(m => m.Name) for example in the partial view) have an id="Name". So the model binding tries to find a "Name" property inside the TestModel, but the name property is inside the CustomModel1 property. This is why the model binding does not work, and the Name property is an empty string when the form is submitted.
The fix is to set the HtmlFieldPrefix.
var dataDictCustomModel1 = new ViewDataDictionary { TemplateInfo = { HtmlFieldPrefix = "CustomModel1" } };
#Html.Partial("~/Views/Common/_CustomPartialView.cshtml", Model.CustomModel1, dataDictCustomModel1 )
This way the id of the Name property becomes id="CustomModel1_Name", thus allowing the model binder to properly set the value of the Name property.
There may be a better solution for this, but so far this is the best, that I have come up with.

Convert ExpandoObject to anonymous type

Is it possible to convert ExpandoObject to anonymously typed object?
Currently I have HtmlHelper extension that can take HTML attributes as a parameter. The problem is that my extension also needs to add some HTML attributes so I've use ExpandoObject to merge my attributes and attributes that user passes to the function using htmlAttributes parameter. Now I need to pass merged HTML attributes to original HtmlHelper function, and when I send ExpandoObject, nothing happens. So I guess that I need to convert ExpandoObject to anonymously typed object or something similar - any suggestions are welcome.
I don't think that you need to deal with expandos to achieve your goal:
public static class HtmlExtensions
{
public static IHtmlString MyHelper(this HtmlHelper htmlHelper, object htmlAttributes)
{
var builder = new TagBuilder("div");
// define the custom attributes. Of course this dictionary
// could be dynamically built at runtime instead of statically
// initialized as in my example:
builder.MergeAttribute("data-myattribute1", "value1");
builder.MergeAttribute("data-myattribute2", "value2");
// now merge them with the user attributes
// (pass "true" if you want to overwrite existing attributes):
builder.MergeAttributes(new RouteValueDictionary(htmlAttributes), false);
builder.SetInnerText("hello world");
return new HtmlString(builder.ToString());
}
}
and if you wanted to call some of the existing helpers, then a simple foreach loop could do the job:
public static class HtmlExtensions
{
public static IHtmlString MyHelper(this HtmlHelper htmlHelper, object htmlAttributes)
{
// define the custom attributes. Of course this dictionary
// could be dynamically built at runtime instead of statically
// initialized as in my example:
var myAttributes = new Dictionary<string, object>
{
{ "data-myattribute1", "value1" },
{ "data-myattribute2", "value2" }
};
var attributes = new RouteValueDictionary(htmlAttributes);
// now merge them with the user attributes
foreach (var item in attributes)
{
// remove this test if you want to overwrite existing keys
if (!myAttributes.ContainsKey(item.Key))
{
myAttributes[item.Key] = item.Value;
}
}
return htmlHelper.ActionLink("click me", "someaction", null, myAttributes);
}
}
Is it possible to convert ExpandoObject to anonymously typed object?
Only if you generate the anonymous type yourself at execution time.
Anonymous types are normally created by the compiler, at compile-time, and baked into your assembly like any other type. They're not dynamic in any sense. So, you'd have to use CodeDOM or something similar to generate the same kind of code that's used for anonymous type... that's not going to be fun.
I think it's rather more likely that someone else will have created some MVC helper classes which know about ExpandoObject (or can just work with IDictionary<string, object>).

Getting DefaultView through client object model

I want to load fields of the default view for Sharepoint list through client object model (I am using Silverlight). Here are some relevant things I've found (on msdn here):
class List has property DefaultViewUrl [of type string]
class List has method GetView(Guid)
class List has property Views [of type ViewCollection]
class ViewCollection has method GetById(Guid)
class ViewCollection has method GetByTitle(string)
class View has property DefaultView [of type bool]
That's everything I was able to find. As you can see there is no direct way of getting DefaultView (there is missing DefaultViewId property on List or GetByUrl(string) method on ViewCollection).
Seems to me as the only solution is to iterate through List.Views collection and check DefaultView property on each View. Which is kind of...well, inefficient...
Did I miss something? Anyone see some straigh solition?
Thanks for ideas.
Try LoadQuery using a LINQ statement
For Example:
private IEnumerable<View> viewQuery = null;
public void LoadDefaultView()
{
using (ClientContext ctx = ClientContext.Current)
{
list = ctx.Web.Lists.GetByTitle("YourList");
viewQuery = ctx.LoadQuery(list.Views
.Include(v => v.Title) // include more lamda statements here to populate View Properties
.Where(v => v.DefaultView == true));
ctx.ExecuteQueryAsync(LoadDefaultViewSuccess, LoadDefaultViewFailure);
}
}
private void LoadDefaultViewSuccess(object sender, ClientRequestSucceededEventArgs args)
{
// should only be one View in views
View defaultView = viewQuery.FirstOrDefault();
// use default.Title here
}
private void LoadDefaultViewFailure(object sender, ClientRequestFailedEventArgs args)
{
// handle failure here
}
MSDN SharePoint 2010 Silverlight COM article here
http://msdn.microsoft.com/en-us/library/ee538971.aspx
What about SPList.DefaultView? The SPList DefaultView member is an SPView object (not bool)

AllowMultiple does not work with Property Attributes?

I'm tying to collect all Custom Attributes placed over a Property. There are more than one Attributes of the same type assigned to the Property, but when collecting them , the resulting collection only contains the first Attribute of the specific type:
The Attribute class
[AttributeUsage(System.AttributeTargets.Property,
AllowMultiple = true)]
public class ConditionAttribute : Attribute{...}
Usage:
[ConditionAttribute("Test1")]
[ConditionAttribute("Test2")]
[ConditionAttribute("Test3")]
public Color BackColor{get; set;}
Now when looping through all Props of the object 'value' whose class contains the Prop "BackColor":
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value))
{
foreach (Attribute attribute in property.Attributes)
{ ... }
....
}
the collection property.Attributes only contains ONE Attribute of type "ConditionAttribute" : The one with "Test1". The others are ignored;-(
So does AllowMultiple not work for Property Attributes ?
Thanks in advance
henrik
According to a post on MSDN, this is by design as part of the PropertyDescriptor class.
However, you can actually solve the problem by overriding TypeId in your custom attribute (Thanks to Ivan from Mindscape for pointing this out):
public override object TypeId
{
get
{
return this;
}
}
Yes, it does work. Not sure why it does not work via PropertyDescriptors.
You can always do: Attribute.GetCustomAttributes(methodInfo, typeof(ConditionAttribute))
Another way to tweak this,
[ConditionAttribute("Test1,Test2,Test3")]
public Color BackColor{get; set;}
and in your validation code,
Dim lstProperties() As String = _ChkColors.Split(",")
For each strProp as string in lstPropertyes
' your validation
' return
Next

Resources