I have created a custom control with a collection property per the example on How do you build an ASP.NET custom control with a collection property?
When the control is added to a common ASP.Net aspx page it works as expected. However, when added to a Page Layout in Sharepoint the following error is thrown:
Unable to cast object of type 'System.Web.UI.CollectionBuilder' to type 'System.Collections.Generic.List`1[mytypes.mytype]'.
The code is pretty much identical to the code provided by the example shown in the link above. I do not think the fault lies in the control as it works fine in a plain web project.
I dont think you can use generic lists in sharepoint. Use an ArrayList or customised List collection instead (use asp:ListItem as an exampe, it has its own collection type)
[ParseChildren(true, "Names")]
public class MyControl : Control {
private List<PersonName> names;
public MyControl() {
names = new List<PersonName>();
}
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public List<PersonName> Names {
get { return this.names; }
}
}
public class PersonName {
public string Name { get; set; }
}
UPDATE
Ahh i see the problem now, it is not to do with the generic list, it is because of the way you are doing the initialization.
Create a private variable to hold the list private List<PersonName> names;
Ensure that the property does not have a setter
Related
I'm needing to adjust some of the field attributes for the Location.VCashAccountID field on the Vendors screen - AP303000. When I put the code below into a customization DLL, it compiles fine and there are not apparent issues on the screen. However, when I try to publish the customization project with the DLL included, I get an error.
Code:
public class VendorMaintDefLocationExtExt : PXGraphExtension<VendorMaint.DefLocationExt,
VendorMaint>
{
public void _(Events.CacheAttached<PX.Objects.CR.Standalone.Location.vCashAccountID> e) { }
}
Error:
"Method Boolean DoValidateAddresses(PX.Objects.CR.Extensions.ValidateAddressesDelegate) in graph extension is marked as [PXOverride], but the original method with such name has not been found in PXGraph"
What am I missing?
TIA!
The following implementation will override the vCashAccount attribute on AP303000
public class AAVendorMaintDefLocationExtExtension : PXGraphExtension<DefLocationExt, DefContactAddressExt, VendorMaint>
{
[PXMergeAttributes(Method = MergeMethod.Merge)]
[PXUIField(DisplayName = "I am override")]
public void _(Events.CacheAttached<PX.Objects.CR.Standalone.Location.vCashAccountID> e) { }
}
You will also require the following references
using PX.Data;
using PX.Objects.AP;
using static PX.Objects.AP.VendorMaint;
The result can be seen in the snip below
The main difficulty in this task was the multitude of graph extensions utilized by the page. Though it's a beneficial design to encapsulate functionality it can be finnicky to determine which order they should be declared in a new extension.
You're graph extension extends VendorMaint.DefLocationExt which contains DoValidateAddresses. Try just extending VendorMaint.
Could anybody help me with acumatica? I have an application, witch connect to acumatica with acumatica libraries: Auth, Default_18.200.001, RESTClient from here: https://github.com/Acumatica/AcumaticaRESTAPIClientForCSharp I am using it like submodules - if somesings will change on git - I always can update it
I needed to add custom fields to the Project form. I did it - added fields, posted changes. After that, I created a new endpoint inheriting it from default endpoint - and the new fields became available to me when working with the Project entity. I achieved this by creating classes that inherit from api and the base model with overriding the GetEntityName() method.
If I need to change the fields, I will have to create new inheritance classes.
Perhaps there is some standard way to support customization fields? And I just didn't find it.
My Api extension:
public class ProjectExtApi : EntityAPI<ProjectExt>
{
public ProjectExtApi(Configuration configuration) : base(configuration)
{ }
protected override string GetEntityName()
{
return "Project"; //base code: return typeof(EntityType).Name;
}
}
and model:
[DataContract]
public class ProjectExt : Project
{
[DataMember(Name = "TestDate", EmitDefaultValue = false)]
public DateTimeValue TestDate { get; set; }
}
My answer is related to custom fields inside Acumatica product. Custom fields should be added with DAC extensions (PXCacheExtension class), example:
public class MyContractExt : PXCacheExtension<Contract>
{
#region UsrDateTime
[PXDBDateAndTime]
public virtual DateTime? UsrDateTime { get; set; }
public abstract class usrDateTime : BqlDateTime.Field<usrDateTime> { }
#endregion
}
Note that PMProject DAC in Acumatica is inheriting from Contract DAC and the field will be persisted in the Contract table.
If the field requires to be persisted in database (with PXDBxyz.. attribute instead of PXxyz..) it requires a database script.
It is recommended to add custom field with a customization. When creating a custom field in the Data Access section of a customization the wizard will automatically create the database script to add the column to the table.
External link, tutorial:
https://www.acu-connect.com/2019/02/27/customizing-acumatica-erp-adding-a-new-field-to-an-existing-screen/
I am working with the Contoso University tutorial and was trying it with both a modular attempt (separate projects for Models, DAL and WebUI - top picture in the attached picture) and a single project (containing all layers - bottom picture). In both cases the solution compiles without errors. However when I go to the details section for a student in web browser the modular project throws an error when I go to the second break point, starting:
Exception Details:
System.NullReferenceException: Object reference not set to an instance of an object.
The same model is passed into the view for each project,
#model ContosoUniversity.Models.Student
And a null reference exception occurs after the line:
#foreach (var item in Model.Enrollments){
I thought it may have been a namespace conflict between the ContosoUniversity.Models project and the Models folder in the ContosoUniversity project, however renaming the folder doesn't resolve this. Is there something else related to multiple projects that would cause a null value (Enrollments.cs not being sent to the model) to be encountered here, but not for a single project?
If its something deeper in the code I can follow up with full view-code and model classes.
Screenshot of working and non working solutions in VS2015Community
Since this is such a commonly confusing error to new developers, I've authored a post on my blog to explain what the error means in detail and how to debug it. TL;DR: Object reference not set to an instance of an object is a runtime error (hence why your project compiles fine) that occurs when you're expecting a variable to be an instance of a particular class, but it actually resolves to null at runtime.
This commonly occurs when you're selecting an object from a database, but nothing matches, or you've neglected to initialize a property on your model that requires initialization, like a list. Based on the line of code you've posted, my guess is that either the model itself is null (perhaps because it's coming from the database and you're not checking for null before sending it to the view), or the Enrollments property is null because you've neglected to initialize it, or it's not marked as virtual if your model is an instance of an entity class.
Whenever you request a specific object from the database, you should always check for null and handle appropriately. For example, if you're working on a "detail" action, your code should look something like:
public ActionResult Detail(int id)
{
var foo = db.Foos.Find(id); // potentially null, if no matching id
if (foo == null)
{
return new HttpNotFoundResult();
}
return View(foo);
}
If you have a list-style property on your model, you should always initialize it via the class constructor or a custom getter:
public class Foo
{
public Foo()
{
Bars = new List<Bar>();
}
public List<Bar> Bars { get; set; }
}
Or
public class Foo
{
private List<Bar> bars;
public List<Bar> Bars
{
get
{
if (bars == null)
{
bars = new List<Bar>();
}
return bars;
}
set { bars = value; }
}
}
If you're utilizing C# 6, the last one can be simplified to:
public class Foo
{
public List<Bar> Bars { get; set; } = new List<Bars>();
}
Finally, this is not necessary if you're dealing with an Entity Framework POCO, as long as the property is virtual:
public virtual ICollection<Bar> Bars { get; set; }
As part of the lazy-loading facility, Entity Framework automatically overrides the property such that it will never be null, only an empty collection if there's truly nothing there. However, if you neglect the virtual keyword, EF cannot do the necessary override to handle this.
Long and short, you need to figure out what variable is null that you're expecting to have an actual value, and then either do proper null-checking (which is a good idea regardless) or figure out why it's null instead of the value you expect.
I created a custom part using the content picker field.
public int UpdateFrom1()
{
ContentDefinitionManager.AlterPartDefinition("BackgroundPart",
builder => builder.WithField("BackgroundImage",
fieldBuilder => fieldBuilder
.OfType("MediaPickerField")
.WithDisplayName("Background Image")));
return 2;
}
public int UpdateFrom2()
{
ContentDefinitionManager.AlterTypeDefinition("Background", cfg => cfg
.WithPart("BackgroundPart")
.Creatable()
.Indexed());
return 3;
}
The service code for getting the data:
public class BackgroundService : IBackgroundService
{
private readonly IRepository<BackgroundPartRecord> _repository;
public BackgroundService(
IRepository<BackgroundPartRecord> repository,
ISignals signals)
{
_repository = repository;
}
public IEnumerable<BackgroundPartRecord> Get()
{
return _repository.Table;
}
}
This works (i can pick content when I create an new item of this type).
Now I want to get a list of all items of my type. I created a service for that and I get a list of my created items. But the items in the list don't have the media picker field. How do I get this content? I want to use this in OnResultExecuting method in a FilterProvider class in my module.
That can't work because you're using the repository API. Repository is a low-level API that is used internally, but should rarely, if ever be used by modules. One of the reasons is that it won't get content items, just part records.
Instead, you need to use one of the querying APIs from ContentManager. That will give you real content items that you can do As on, that will give you access to the content item's fields (those are stored on the Infoset, which is on the content item record), etc.
This or one of the overloads and extension methods should do the trick:
_contentManager.Query<BackgroundPart>()
I am using the basic instructions (here) for creating a property driven by a custom ToolPart.
All is good, except for the part where, in order to access the webpart property within the ApplyChanges method I must cast the "this.ParentToolPane.SelectedWebPart" back to a concrete "SimpleWebPart" class.
public override void ApplyChanges()
{
SimpleWebPart wp1 = (SimpleWebPart)this.ParentToolPane.SelectedWebPart;
// Send the custom text to the Web Part.
wp1.Text = Page.Request.Form[inputname];
}
Doing this means that I must pair each toolpart with a specific webpart. Is there a better way?
I cannot create an interface as there is no way of specifying a property in one.
I ineptly tried an passing an event/eventhandler during toolpart creation, but that did not update the webpart property when called.
I could create a base class for all the webparts that have a public "Text" property, but that is fugly.
I could also get desperate and crack open the this.ParentToolPane.SelectedWebPart reference with Reflection and call any properties named "Text" that way.
Either way, I am staring down the barrel of a fair bit of faffing around only to find out each option is a dead end.
Has anyone done this and can recommend the correct method for creating a reusable toolpart?
I have used an interface instead of a specific instance of a webpart.
private class IMyProperty
{
void SetMyProperty(string value);
}
public override void ApplyChanges()
{
IMyProperty wp1 = (IMyProperty)this.ParentToolPane.SelectedWebPart;
// Send the custom text to the Web Part.
wp1.SetMyProperty(Page.Request.Form[inputname]);
}
But this does not give a compile time warning that the toolpart requires the parent webpart to implement the IMyProperty interface.
The simple solution to that is to add a property of the IMyProperty interface in the toolpart constructor and call this reference instead of the this.ParentToolPane.SelectedWebPart property.
public ToolPart1(IContentUrl webPart)
{
// Set default properties
this.Init += new EventHandler(ToolPart1_Init);
parentWebPart = webPart;
}
public override void ApplyChanges()
{
// Send the custom text to the Web Part.
parentWebPart.SetMyProperty(Page.Request.Form[inputname]);
}
public override ToolPart[] GetToolParts()
{
// This is the custom ToolPart.
toolparts[2] = new ToolPart1(this);
return toolparts;
}
This works fine, but I cannot get over the feeling that there is something nasty in the underlying SharePoint code that may trip me up later.