Data binding to a nested property with possibly null parent - c#

I want to perform data-binding to Room.ToNorth.ImageName while ToNorth may be null.
I'm trying to write my own text adventure and have images next to each of the directions a player can go (ie. open/closed door, pathway). When there isn't a room in a given direction I have the controls bound to show a 'no exit' image, this works great so long the player starting location has an exit in all 4 directions, however when one is null I get the following exception
System.ArgumentNullException: 'Value cannot be null. Parameter name:
component'
This is isn't an issue on a new game but I'm making randomly generated dungeons so it can't be guaranteed on a load game. I realise I could probably fudge it by creating a safe space while the bindings are set then moving the player to where they need to be but is there a proper way to handle this?
The closest answer I've found is this question, but I'm not sure I can apply it here due to how it's bound.
private GameSession _CurrentGame;
private BindingSource _BindToPlayer = new BindingSource();
private void BtnNewGame_Click(object sender, EventArgs e)
{
_CurrentGame = new GameSession();
_BindToPlayer.DataSource = _CurrentGame.CurrentPlayer;
PicBoxNorth.DataBindings.Add("ImageLocation", _BindToPlayer,
"CurrentRoom.ToNorth.ImageName", true, DataSourceUpdateMode.OnPropertyChanged,
"Images\\Wall.png");
}
The ToNorth property just gets the object from an an array of exits, but telling it to return an empty exit if it's null (like suggested in the above link) would be problematic in that the exit wouldn't have any rooms to connect to and thus fail to initialise likely throwing an exception itself along the way. To explain better, I have my rooms set up as follows
public Class Room
{
public int ID { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public String FarDescription { get; set; }
public CoOrds Co_Ords { get; set; }
public Boolean UniqueRoom { get; set; }
public Exit[] ExitArr { get; set; }
public Exit ToNorth => ExitArr[0];
public Exit ToSouth => ExitArr[1];
public Exit ToWest => ExitArr[2];
public Exit ToEast => ExitArr[3];
public Exit ToUp => ExitArr[4];
public Exit ToDown => ExitArr[5];
public Exit ToIn => ExitArr[6];
public Exit ToOut => ExitArr[7];
public Room(int id, String name, String desc, String fardesc,bool unique = false)
{
ID = id;
Name = name;
Description = desc;
FarDescription = fardesc;
Co_Ords = new CoOrds();
ExitArr = new Exit[8];
UniqueRoom = unique;
}
And my exits are set up like so
public class Exit
{
public String Description {get;}
public Room RoomA { get; set; }
public Room RoomB { get; set; }
public Boolean CanExitA { get; set; } = true;
public Boolean CanExitB { get; set; } = true;
public Room NextRoom
{
get
{
if (RoomA.PlayerHere && CanExitA)
{ return RoomB; }
else if (RoomB.PlayerHere && CanExitB)
{ return RoomA; }
else
return null;
}
}
public String ImageName
{
get
{
string imagePath = "Images\\";
if (DoorHere != null)
{
return imagePath + DoorHere.ImageName;
}
else
return imagePath + "Pathway.png";
}
}
public Door DoorHere { get; set; }
public Exit(Room roomA, int Adir, Room roomB, int Bdir, Door door = null)
{
RoomA = roomA;
RoomA.ExitArr[Adir] = this;
RoomB = roomB;
RoomB.ExitArr[Bdir] = this;
DoorHere = door;
}
Door is just an unnamed object with a few bools for IsOpen, IsLocked etc. and shouldn't be needed if you want to test this but exits are created anonymously and add themselves to the Exit array in both connecting rooms, hence creating empty ones would fail.
Any suggestions would be greatly appreciated.

As an option, you can create a property to do null-checking and get/set second level property using that property.
For example in your code, you can create ToNorthImageName property to get/set the value of ToNorth.ImageNamein Room class:
public string ToNorthImageName
{
get { return ToNorth?.ImageName }
//set { if (ToNorth != null) ToNorth.ImageName = value; }
}
The property setter is optional, you can remove it if you just want to read the image name.
Then setup data-binding to that property:
PicBoxNorth.DataBindings.Add("ImageLocation", _BindToPlayer, "ToNorthImageName",
true, DataSourceUpdateMode.OnPropertyChanged);

Related

C# How to initialize an object containing a list<T> with standard values

This question is related to this question. I managed to get one step further, but I am now unable to initialize my whole object with default values in order to prevent it from being null at list level. The goal of this is to hand down the "null" values to my SQL query. Ultimately what I want is one record in my DB that will express: This row has been recorded, but the related values were "null".
I have tried Brian's fiddle and it does not seem to work for me to initialize the whole model with standard values.
Expectation: Upon object initialisation the "null" values should be used and then overwritten in case there is a value coming through JSON deserialisation.
Here is what I have tried. None of this will have the desired effect. I receive this error:
Application_Error: System.ArgumentNullException: Value cannot be null.
Every time I try to access one of the lists in the data model.
namespace Project.MyJob
{
public class JsonModel
{
public JsonModel()
{
Type_X type_x = new Type_X(); // This works fine.
List<Actions> action = new List<Actions>(); // This is never visible
/*in my object either before I initialise JObject or after. So whatever
gets initialised here never makes it to my object. Only Type_X appears
to be working as expected. */
action.Add(new Actions {number = "null", time = "null", station =
"null", unitState = "null"}) // This also does not prevent my
//JsonModel object from being null.
}
public string objectNumber { get; set; }
public string objectFamily { get; set; }
public string objectOrder { get; set; }
public string location { get; set; }
public string place { get; set; }
public string inventionTime { get; set; }
public string lastUpdate { get; set; }
public string condition { get; set; }
public Type_X Type_X { get; set; }
public List<Actions> actions { get; set; }
}
public class Actions
{
public Actions()
{
// None of this seems to play a role at inititialisation.
count = "null";
datetime = "null";
place = "null";
status = "null";
}
// public string count { get; set; } = "null"; should be the same as above
// but also does not do anything.
public string count { get; set; }
public string datetime { get; set; }
public string place { get; set; }
public string status { get; set; }
}
public class Type_X
{
public Type_X
{
partA = "null"; // This works.
}
public string partA { get; set; }
public string PartB { get; set; }
public string partC { get; set; }
public string partD { get; set; }
public string partE { get; set; }
}
}
This is how I now initialize the object based on Brian's answer.
JObject = JsonConvert.DeserializeObject< JsonModel >(json.ToString(), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore});
When I try to iterate over Actions' content, it (logically) gives me above mentioned null error.
for (int i = 0, len = JObject.actions.Count(); i < len; i++)
My current understanding of constructor initialisations:
If I define values such as count = "null"; they should appear in any new object that is created.
If default values are present I would then also expect that a list that has items with default values (such as count for ex.) would be of Count() 1 and not null. How is that even possible?
This will get you out of your bind:
private List<Actions> _actions = new List<Actions>();
public List<Actions> actions { get => _actions; set => _actions = value ?? _actions; }
This causes trying to set actions to null to set it to the previous value, and it is initially not null so it can never be null.
I'm not absolutely sure I'm reading your question right, so here's the same fragment for partA:
private string _partA = "null";
public string partA { get => _partA; set => _partA = value ?? _partA; }
I have found that in some cases, initializing generic lists with their default constructor on your model increases ease of use. Otherwise you will always want to validate they are not null before applying any logic(even something as simple as checking list length). Especially if the entity is being hydrated outside of user code, i.e. database, webapi, etc...
One option is to break up your initialization into two parts. Part 1 being the basic initialization via default constructor, and part 2 being the rest of your hydration logic:
JObject = new List < YourModel >();
... < deserialization code here >
Alternatively you could do this in your deserialization code, but it would add a bit of complexity. Following this approach will allow you to clean up your code in other areas since each access will not need to be immediately proceeded by a null check.

Setter of a singleton from an custome class in .Net C# not triggered if only one property changed

I have this class
public class SPP {
static PPModel PP= null;
public PPModel sPP
{
set
{
if (PP != value)
{
PP = value;
//save PP in database
}
}
get
{
if (PP == null)
{
//lazy loading of PP
}
return PP;
}
}
}
public class PPModel
{
public string property1 { get; set; }
public int property2 { get; set; }
public int property3 { get; set; }
public int property4 { get; set; }
public Dictionary<string, int> property5 { get; set; }
}
Now if I set sPP in this way, everything works fine:
SSP.Instance.sPP = new PPModel(){...};
But I want to know if it is possible to just update one property of this singleton and trigger the setter in this way:
SSP.Instance.sPP.property4 = 7;
If I do it in this way, the singleton sPP has the updated value, but the setter seems not to be triggered what means the additional code like database save will not work.
Any chances to get this working?
Thanks in advance.
a good solution for this is to change the model to have full properties definitions, and on the setters, call the DB update, as such:
public class PPModel
{
private string _fieldOne;
public string Property1
{
get { return _fieldOne; }
set
{
_fieldOne = value;
// update DB
}
}
private int _field2;
public int Property2
{
get { return _field2; }
set
{
_field2 = value;
// update DB
}
}
// and so on for all properties
}
You can't trigger setter in your singleton by updating internal stored object properties.
You can trigger your setter like this:
SSP.Instance.sPP = new PPModel(){...};
var instance = SSP.Instance.sPP;
instance.property4 = 7
SSP.Instance = instance;
This way you'll reset the exact variable to your instance and trigger update.
But, as you're implementing singleton, it is NOT responsible for making actions related to the object it stores.
You have to monitor changes in your PPModel object, as only this class is responsible for it's internal state.

c# Set Parent class property in child setter

Im trying to set a parent class property in child property setter.
I have one main class :User, which has a child class LIST ArrayPositions, which in turn has a child class list of ExpressionMember.
Whenever the property ExpressionMemValue in ExpressionMember class is set, i want to update it's parent class ArrayPosition aswell.
However the current solution does not update the corresponding parent.
Here's the code:
public List<User> users = new List<User>();
public class User
{
public string ImageName { get; set; }
private string _PartName = "";
public string PartName
{
get
{
return this._PartName;
}
set {
_PartName=value;
}
}
public List <ArrayPosition> ArrayPositions { get; set; }
public override string ToString()
{
return this.PartName.ToString();
}
}
public class ArrayPosition:User
{
public string myArrayPos = "";
public string PartId { get; set; }
public string ArrayPos
{
get
{
return this.myArrayPos;
}
set
{
this.myArrayPos = value;
}
}
public List<ExpressionMember> ExpressionMembers { get; set; }
}
public class ExpressionMember : ArrayPosition
{
public string ExpressionMem { get; set; }
public string MyExpressionMemValye="";
public string ExpressionMemValue
{
get
{
return MyExpressionMemValye;
}
set
{
MyExpressionMemValye = value;
// this.ArrayPos = value; //set parent value, this will not update it
}
}
}
It would appear that you need to not use inheritance and instead use composition which you are kind of already doing. Try doing this instead. It's not perfect by any means but I'm trying not to change your general strategy too much.
public class User
{
public string ImageName { get; set; }
private string _PartName = "";
public string PartName
{
get
{
return this._PartName;
}
set {
_PartName=value;
}
}
public List <ArrayPosition> ArrayPositions { get; set; }
public override string ToString()
{
return this.PartName.ToString();
}
}
public class ArrayPosition
{
public string myArrayPos = "";
public string PartId { get; set; }
public string ArrayPos
{
get
{
return this.myArrayPos;
}
set
{
this.myArrayPos = value;
}
}
public List<ExpressionMember> ExpressionMembers { get; set; }
}
public class ExpressionMember
{
private ArrayPosition _parentArrayPosition;
public string ExpressionMem { get; set; }
public string MyExpressionMemValye="";
public string ExpressionMemValue
{
get
{
return MyExpressionMemValye;
}
set
{
MyExpressionMemValye = value;
this._parentArrayPosition.ArrayPos = value;
}
public ExpressionMember(ArrayPosition parent) {
_parentArrayPosition = parent;
}
}
}
You are definitely not using inheritance and composition correctly. You are looking to build a tree of objects where the object itself has child objects. Something that might clarify things in your mind is instead of calling them child/parent classes, refer to them as sub/super classes in the case of inheritance and parent/child objects in the case of composition. A parent object is an instance of a class that contains another instance of a class (child object). A subclass inherits the members of another class.
Your inheritance is very strange. The exact responsibilities of your classes are not clear to me.
Apart from that, you could protect the property ExpressionMembers by making it read-only. Implement a new method to add or remove elements. Add an event (e.g. ExpressionMemValueChanged) to ExpressionMember . This event is triggered when an item is added. Whenever an element is added or removed you register/deregister ArrayPosition to/from this event. Inside the event handler you can then set your ArrayPos value.
(You can use an ObservableCollection for your ExpressionMembers and react to the CollectionChanged event instead of writing a getter/setter.)

Values returned from class revert to null

I create an new Contractor object "gc" that calls a method GetContractor() to return all the properties. The results it is returning is correct, however the "gc" object shows all "NULL". I assume I doing something incorrectly in my aspx.cs page?
aspx.cs
protected void fvWasteCollected_ItemCommand(object sender, FormViewCommandEventArgs e)
{
if (e.CommandName.Equals("Insert")){
ValidationSummaryWasteDetail.ValidationGroup = "WasteReceivedDetail";
if (IsValid) {
odsMRWWasteCollectedDetail.InsertParameters["WasteTypeId"].DefaultValue = ddlWasteCollectedType.SelectedValue;
odsMRWWasteCollectedDetail.InsertParameters["DisposalMethodId"].DefaultValue = ddl_disposalMethod.SelectedValue;
Contractor gc = new Contractor();
gc.GetContractor(2);
var contractorName = gc.MRWContractorName;
}
}
}
.cs
public class Contractor
{
public Contractor GetContractor(int MRWContractorId)
{
using (DataAccessLINQDataContext db = new DataAccessLINQDataContext())
{
var result = db.MRWContractors.Where(c => c.MRWContractorId == MRWContractorId).Select(c => new Contractor
{
MRWContractorId = c.MRWContractorId,
MRWContractorName = c.MRWContractorName,
MRWContractorAddress = c.MRWContractorAddress,
MRWContractorCity = c.MRWContractorCity,
MRWContractorStateCode = c.MRWContractorStateCode,
MRWContractorZipCode = c.MRWContractorZipCode,
MRWContractorPhone = c.MRWContractorPhone,
MRWContractorFax = c.MRWContractorFax,
MRWContractorEmail = c.MRWContractorEmail
}).SingleOrDefault();
return result;
}
}
public int MRWContractorId { get; set; }
public string MRWContractorName { get; set; }
public string MRWContractorAddress { get; set; }
public string MRWContractorCity { get; set; }
public string MRWContractorStateCode { get; set; }
public int? MRWContractorZipCode { get; set; }
public string MRWContractorPhone { get; set; }
public string MRWContractorFax { get; set; }
public string MRWContractorEmail { get; set; }
}
You are loosing the value of gc when you dont assign it to something.
Try this instead:
var contractor = gc.GetContractor(2);
var contractorName = contractor.MRWContractorName;
You are creating one empty instance of the object that is only used to call the GetContractor method. The GetContractor method creates another instance that contains data, which is returned, but you just throw that instance away and expect the data to be available in the first instance that never got populated.
Make the GetContractor method static so that you don't need an instance to call it:
public static Contractor GetContractor(int MRWContractorId)
Now you can call the method to get that instance that contains the data, without first creating an empty instance:
Contractor gc = Contractor.GetContractor(2);
string contractorName = gc.MRWContractorName;

The right way to build a c#'s app (CF .NET 3.5), i need advice

Over the past two years I developed apps for the CF .NET 3.5 to be runned on warehouse's portable device(windows mobile).
From the beginning I just jumped into the process and made a lot of mistakes that I'm gradually correcting. What has came out are apps made in this way:
a main form to start the whole process which automatically creates a data-form, that will stay alive for the whole time. This data-form will keep all the datas that the user will insert or request from the server. The other forms are basically views of the data with methods to manipulate them.
It works but...am I doing this in the right way? Or maybe am I missing something really fundamental?
So, you created a data form, and you are using it like RAM. You never display the data, you simply store it there to access.
If someone ever has to take over your job (like you leave the company or die), they are going to hate you so bad.
A better technique would be to create a Class that houses all of this data.
The good part is, since you already have a data form, you probably already know how everything is organized!
Now, just use that knowledge of your data to create your class that you can read and write to.
If you have groups of similar items, create other classes that your main class will contain.
If you have several of these similar items, create publically accessible Lists of these items.
Make it as dead simple or as complex as you'd like!
Consider these classes, which are all generic enough to modify however you would need and demonstrate some extras added:
public class DataForm {
private GroupedItem m_item2;
public event EventHandler Item2Changed;
public DataForm() { // this is your constructor
Item1 = new GroupedItem();
Item2 = new GroupedItem();
ItemCollection = new GroupCollectionItems("Group1");
}
public float Value1 { get; set; }
public float Value2 { get; set; }
public GroupedItem Item1 { get; set; }
public GroupedItem Item2 {
get { return m_item2; }
set {
if (m_item2 != value) {
m_item2 = value;
if (Item2Changed != null) {
Item2Changed(this, EventArgs.Empty); // notify whoever is listening for the change
}
}
}
}
public GroupCollectionItems ItemCollection { get; set; }
}
public class GroupedItem {
public GroupedItem() { // this is your constructor
}
public string Name { get; set; }
public object Value { get; set; }
}
public class GroupCollectionItem {
private GroupCollectionItem() { // this is your constructor
}
public static GroupCollectionItem Create(string groupName, string itemName, object itemValue) {
var item = new GroupCollectionItem() {
Group = groupName,
Name = itemName,
Value = itemValue
};
return item;
}
public string Group { get; private set; }
public string Name { get; private set; }
public object Value { get; set; }
}
public class GroupCollectionItems : List<GroupCollectionItem> {
public GroupCollectionItems(string name) { // this is your constructor
Name = name;
}
public string Name { get; private set; }
}

Categories

Resources