I am trying to implement a Settings dialog for iOS using MonoTouch and the DialogViewController.
The class below contains some public properties, and a method to get a DialogViewController for it.
The problem is that when the view disappears, the string value in thisName.Value is null (I have of course filled in something in the text field).
Why?
public class Settings
{
public string Name { get; set; }
public int MagicNumber { get; set; }
public bool ThisIsEnabled{ get; set; }
public Settings ()
{
var defaults = NSUserDefaults.StandardUserDefaults;
Name = defaults.StringForKey ("name");
ThisIsEnabled = defaults.BoolForKey("thisisenabled");
MagicNumber = defaults.IntForKey ("123");
}
public UIViewController GetViewController ()
{
var thisBoolean = new BooleanElement ("This boolean", ThisIsEnabled);
var thisName = new EntryElement ("Name", "name", Name);
thisName.KeyboardType = UIKeyboardType.ASCIICapable;
var root = new RootElement ("Options"){
new Section (){thisBoolean,thisName}
};
var dv = new DialogViewController (root, true){Autorotate= true};
dv.ViewDissapearing += delegate {
ThisIsEnabled = thisBoolean.Value; // <== This works
Name = thisName.Value; // <== This is NULL
var defaults = NSUserDefaults.StandardUserDefaults;
defaults.SetBool (ThisIsEnabled, "thisisenabled");
defaults.SetString (Name, "name");
};
return dv;
}
}
}
the current release of MT.D will not "save" a field's value until the user navigates away from it. This may be what you are seeing.
This behavior has been fixed, but has not been released yet.
Related
I need some help to map an anonymous object using Automapper. The goal is combine Product and Unity in a ProductDto (in which unity is a product's property).
Autommaper CreateMissingTypeMaps configuration is set to true.
My Classes:
public class Product
{
public int Id { get; set; }
}
public class Unity
{
public int Id { get; set; }
}
public class ProductDto
{
public int Id { get; set; }
public UnityDto Unity{ get; set; }
}
public class UnityDto
{
public int Id { get; set; }
}
Test Code
Product p = new Product() { Id = 1 };
Unity u = new Unity() { Id = 999 };
var a = new { Product = p, Unity = u };
var t1 = Mapper.Map<ProductDto>(a.Product);
var t2 = Mapper.Map<UnityDto>(a.Unity);
var t3 = Mapper.Map<ProductDto>(a);
Console.WriteLine(string.Format("ProductId: {0}", t1.Id)); // Print 1
Console.WriteLine(string.Format("UnityId: {0}", t2.Id)); // Print 999
Console.WriteLine(string.Format("Anonymous ProductId: {0}", t3.Id)); // Print 0 <<< ERROR: It should be 1 >>>
Console.WriteLine(string.Format("Anonymous UnityId: {0}", t3.Unity.Id)); // Print 999
There are two maps added to the profile:
CreateMap<Product, ProductDto>();
CreateMap<Unity, UnityDto>();
The problem is how Automapper map anonymous objects. I haven't time to check out Automapper source code but I got the desired behaviour with minor changes on my anonymous object:
var a = new { Id = p.Id, Unity = u };
By doing this, I might even delete previous mappings because now it is using only CreateMissingTypeMaps.
Note: As matter of fact I'm not sure if it is really an issue or I it was just my unreal expectations.
Is it possible to create fields in SharePoint with CSOM, not using XML?
I've seen many examples using XML, but none with just setting properties for the field programmatically?
fields.Add(new **FieldCreationInformation** {
InternalName = "Test",
etc..
});
That's doable, in the following example is introduced a FieldCreationInformation class:
[XmlRoot("Field")]
public class FieldCreationInformation
{
[XmlAttribute("ID")]
public Guid Id { get; set; }
[XmlAttribute()]
public string DisplayName { get; set; }
[XmlAttribute("Name")]
public string InternalName { get; set; }
[XmlIgnore()]
public bool AddToDefaultView { get; set; }
//public IEnumerable<KeyValuePair<string, string>> AdditionalAttributes { get; set; }
[XmlAttribute("Type")]
public FieldType FieldType { get; set; }
[XmlAttribute()]
public string Group { get; set; }
[XmlAttribute()]
public bool Required { get; set; }
public string ToXml()
{
var serializer = new XmlSerializer(GetType());
var settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
var emptyNamepsaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
serializer.Serialize(writer, this, emptyNamepsaces);
return stream.ToString();
}
}
public FieldCreationInformation()
{
Id = Guid.NewGuid();
}
}
and then extension method for creating a new field:
public static class FieldCollectionExtensions
{
public static Field Add(this FieldCollection fields, FieldCreationInformation info)
{
var fieldSchema = info.ToXml();
return fields.AddFieldAsXml(fieldSchema, info.AddToDefaultView, AddFieldOptions.AddFieldToDefaultView);
}
}
Usage
var fieldInfo = new FieldCreationInformation();
fieldInfo.FieldType = FieldType.Geolocation;
fieldInfo.InternalName = "ContactsLocation";
fieldInfo.DisplayName = "Contacts Location";
ctx.Site.RootWeb.Fields.Add(fieldInfo);
ctx.ExecuteQuery();
When I add fields with CSOM/JSOM I use the method on the FieldCollection AddFieldAsXml. This requires you to build a string of xml with all of the properties for the desired field, but it works. I included an excerpt of the related cpde below:
Microsoft.SharePoint.Client.Web web = _context.Web;
FieldCollection fields = web.Fields;
_context.Load(fields);
_context.ExecuteQuery();
Field field = fields.FirstOrDefault(f => f.StaticName == _staticName);
if (field == null)
{
Field createdField = fields.AddFieldAsXml(xml, false, AddFieldOptions.AddToNoContentType);
_context.Load(createdField);
_context.ExecuteQuery();
}
Similar code is used if you would like to add a field directly to an existing list.
Using below code, I have two shape results:
public ActionResult CompareRevisions(List<String> Ids)
{
contentItemLeft = // code to get a ContentItem
contentItemRight = // code to get a ContentItem
dynamic modelLeft = Services.ContentManager.BuildDisplay(contentItemLeft);
dynamic modelRight = Services.ContentManager.BuildDisplay(contentItemRight);
var ctx = Services.WorkContext;
ctx.Layout.Metadata.Alternates.Add("Layout_Null");
var shapeResultLeft = new ShapeResult(this, modelLeft);
var shapeResultRight = new ShapeResult(this, modelRight);
return shapeResultLeft;
}
When I return any of one shape result such as return shapeResultLeft at the last line of Controller, the browser displays perfectly the Content. However How can I display both of my ShapeResults: shapeResultLeft , shapeResultRight on the Page same time ?
How do I return a list of ShapeResults and display it using the View/Layout file ?
You have multiple options for this:
Method 1
one most used in MVC (not Orchard specific) is a viewmodel:
public class MyViewModel {
public dynamic Shape1 { get; set; }
public dynamic Shape2 { get; set; }
}
public ActionResult CompareRevisions(List<String> Ids) {
// ..
var viewModel = new MyViewModel {
Shape1 = modelLeft,
Shape2 = modelRight
}
return View(viewModel)
}
view:
#model My.NameSpace.ViewModels.MyViewModel
#Display(Model.Shape1)
#Display(Model.Shape2)
Method 2
Without using strongly typed viewmodels, you can use orchard's dynamic viewmodel:
// inject IShapeFactory through Dependency Injection
public MyController(IShapeFactory shapeFactory) {
Shape = shapeFactory;
}
public dynamic Shape { get; set; } // inject with DI through IShapeFactory
public ActionResult CompareRevisions(List<String> Ids) {
// ..
var viewModel = Shape
.ViewModel() // dynamic
.Shape1(modelLeft)
.Shape2(modelRight);
return View(viewModel);
}
Method 3
Or with Orchard's list, when the number of shapes could vary:
public dynamic Shape { get; set; } // inject with DI through IShapeFactory
public ActionResult CompareRevisions(List<String> Ids) {
// ..
var list = Shape.List();
list.AddRange(myShapes); // myShapes is a collection of build shapes (modelLeft, modelRight)
var viewModel = Shape
.ViewModel()
.List(list);
return View(viewModel);
}
view:
#Display(Model.List);
Normally when I use the DropDownListFor helper I'm selecting which item is selected based on an ID (int), but now I have a situation where I need to display which item is selected based on the text (string) and not an ID. In the controller the model is being set correctly to the value that I want with this property:
model.Title
An example title would be "Front Office". I have the following code in my Controller:
ViewBag.Titles = new SelectList(jobTitles, "Name", "Name");
and on the view I have this:
#Html.DropDownListFor(model => model.Title, ViewBag.Titles as SelectList)
The DropDownList is populating correctly with all the expected job titles, it just isn't selecting the correct job title based on model.Title. What am I doing wrong?
UPDATE:
There seems to be something else potentially going wrong in my code, so I'm putting all of it here to see if I'm doing something wrong.
Controller:
public ActionResult Edit(int id = 0)
{
StaffMember staffmember = StaffMember.SelectByID(id); // gets staff member from db
ViewBag.Titles = new SelectList(JobTitle.SelectAll(), "Name", "Name", staffmember.Title); // JobTitle.SelectAll() returns List<JobTitle>
StaffEditModel model = new StaffEditModel();
model.ID = staffmember.ID;
model.ClientID = staffmember.ClientID;
model.FirstName = staffmember.FirstName;
model.MiddleInitial = staffmember.MiddleInitial;
model.LastName = staffmember.LastName;
model.Title = staffmember.Title;
model.Phone = staffmember.Phone;
model.Email = staffmember.Email;
model.Birthday = staffmember.Birthday;
model.HireDate = staffmember.HireDate;
model.Biography = staffmember.Biography;
if (staffmember == null)
{
return HttpNotFound();
}
return View(model);
}
Model:
public class StaffEditModel
{
public int ID { get; set; }
public int ClientID { get; set; }
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Middle Initial")]
public string MiddleInitial { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
public string Title { get; set; } // this is the property I'm trying to show in DropDown
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public string Birthday { get; set; }
[Display(Name = "Hire Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public string HireDate { get; set; }
public string Email { get; set; }
[MaxLength(14)]
public string Phone { get; set; }
public string Biography { get; set; }
}
View:
#Html.DropDownListFor(model => model.Title, new SelectList(ViewBag.Titles,"Value","Text", Model.Title))
This should work.
You can pass ViewBag.Titles as List<SelectListItem> from controller
var jobList = new List<SelectListItem>();
foreach (var job in jobTitles)
{
var item = new SelectListItem();
item.Value = job.PropertyName; //the property you want to display i.e. Title
item.Text = job.PropertyName;
jobList.Add(item);
}
ViewBag.Title = jobList;
and then in the view,
#Html.DropDownListFor(model => model.Title, new SelectList(ViewBag.Titles,"Value","Text", Model.Title))
I know I'm late to the party on this, but I had a similar problem and thought I would add what I've found.
I haven't figured out the why yet - I'm still researching (that's how I ended up here) - but I think it has something to do with the fact that you're using the word Title
I've got the exact same scenario and as soon as I changed my model to CustomerTitle instead of Title things worked as expected.
Initial thoughts are that the model binder is finding Title somewhere else in the DOM and getting tripped up. Complete speculation. If I have more time I'll keep digging for the why
I have one question about Windows Workflow Foundation 4. I have an activity named PositionArrayActivity. This activity has a Sequence activity inside it. I need that in Execute method (during the workflow execution) oneFund variable mapping his value to PORTFOLIO_NAME that is created in Create method.... What have I to do to mapping oneFund value to PORTFOLIO_NAME at runtime?
Thanks
public sealed class PositionArrayActivity : NativeActivity, IActivityTemplateFactory
{
[Browsable(false)]
public Dictionary<string, List<Entity>> dictionary = new Dictionary<string, List<Entity>>();
public ActivityAction<Entity[]> Body { get; set; }
public Entity[] PositionList { get; set; }
public SqlDataReader rdr;
public SqlDataReader sdr;
public Entity[] positionArray;
public List<String> fundList;
public String oneFund { get; set; }
public String date { get; set; }
public List<Entity> listToArrayPositions;
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddDelegate(Body);
}
protected override void Execute(NativeActivityContext context)
{
// A lot of code....
}
public Activity Create(DependencyObject target)
{
Variable<string> var = new Variable<string>
{
Name = "PORTFOLIO_NAME"
};
var fef = new PositionArrayActivity();
var aa = new ActivityAction<Entity[]>();
var da = new DelegateInArgument<Entity[]>();
da.Name = "positions";
fef.Body = aa;
aa.Argument = da;
aa.Handler = new Sequence
{
Variables = { var }
};
return fef;
}
}
You need to have an ActivityContext to set a variable value so first move the declaration of the var (did that name actually compile?) to a higher scope.
Then in Execute
var.Set(activityContext, oneFund);
One thing though, the oneFund property will only be set once at application startup so you may have some surprising results. If you wanted that to be for each instance, you need an inargument.