AspectGetter to get object from list - c#

I have a list of items that I would like to bind to my ObjectListView and I think AspectGetter needs to be used to achieve this. How would I go about doing this?
I have tried this to generate additional columns but I am still unable to bind the data to show list items
int count = 0;
foreach (var disk in vmObject.DisksList)
{
// create column with vhd+count
OLVColumn diskColumn = new OLVColumn("Attached VHD " + count, disk.Path);
// this lets you handle the model object directly
diskColumn.AspectGetter = delegate(object rowObject)
{
// check if that is the expected model type
if (rowObject is Model.HyperVTools.VMInfo)
{
// return the value of disklist
return ((Model.HyperVTools.VMInfo)rowObject).DisksList;
}
else
{
return "";
}
};
columnsList.Add(diskColumn);
count++;
}
objectListView2.Columns.AddRange(columnsList.Cast<System.Windows.Forms.ColumnHeader>().ToArray());
objectListView2.AddObject(vmObject);

The foreach loop is unnecessary. You only need to create a column once. The same goes for the AspectGetter.
As soon as you add objects to the OLV, it will call the AspectGetter delegates to get the values automatically and create the corresponding rows. I suggest you take another look at the tutorial / examples.

Related

How to change items in cache

Hello i want to change and alter values inside the cache of my acumatica cache i would like to know how to do it
for example i want to change the Ext. Cost value pro grammatically of the first line or the second line or can i check if there is already a "Data Backup" on transaction Descr.
public delegate void PersistDelegate();
[PXOverride]
public void Persist(PersistDelegate baseMethod)
{
if (Globalvar.GlobalBoolean == true)
{
PXCache cache = Base.Transactions.Cache;
APTran red = new APTran();
red.BranchID = Base.Transactions.Current.BranchID;
red.InventoryID = 10045;
var curyl = Convert.ToDecimal(Globalvar.Globalred);
red.CuryLineAmt = curyl * -1;
cache.Insert(red);
}
else
{
}
baseMethod();
}
this code add a new line on persist but if it save again it add the same line agaub u wabt ti check if there is already a inventoryID =10045; in the cache
thank you for your help
You can access your cache instance by using a view name or cache type. Ex: (Where 'Base' is the graph instance)
Base.Transactions.Cache
or
Base.Caches<APTran>().Cache
Using the cache instance you can loop the cached values using Cached, Inserted, Updated, or Deleted depending on which type of record you are looking for. You can also use GetStatus() on an object to find out if its inserted, updated, etc. Alternatively calling PXSelect will find the results in cache (PXSelectReadOnly will not).
So you could loop your results like so:
foreach (MyDac row in Base.Caches<MyDac>().Cache.Cached)
{
// logic
}
If you know the key values of the cache object you are looking for you can use Locate to find by key fields:
var row = (MyDac)Base.Transactions.Cache.Locate(new MyDac
{
MyKey1 = "",
MyKey2 = ""
// etc... must include each key field
});
As Mentioned before you can also just use a PXSelect statement to get the values.
Once you have the row to update the values you set the object properties and then call your cache Update(row) before the base persist and you are good to go. Similar if needing to Insert(row) or Delete(row).
So in your case you might end up with something like this in your persist:
foreach (APTran row in Base.Transactions.Cache.Cached)
{
if (Globalvar.GlobalBoolean != true || row.TranDesc == null || !row.TranDesc.Contains("Data Backup"))
{
continue;
}
//Found my row
var curyl = Convert.ToDecimal(Globalvar.Globalred);
row.CuryLineAmt = curyl * -1;
Base.Transactions.Update(row);
}

Set table values equal to value in other table

I have one table which functions as a viewModel. From this, it is supposed to be the source of my binding in my view. My object is to update this table/viewModel based on two other tables. I have one property from each of the table checklistTable and AnswerTable that is to be set in my viewModel checkViewModel.
At the moment I am querying the current elements I need from each of the tables, and trying to update the viewModel.
CodeBehind:
//Curent descriptions to be set in the viewModel
var currentDescription = (_check.Where(s => currentElements.Contains(s.defineId)).Select(s=> s.Description).ToList());
//Current colors to be set in the viewModel
var currentColors = (from current in _answer
where current.questionId == currentCheckId &&
current.buildingId == currentBuildingId
orderby current.dateReported descending
select current.backgroundColor).ToList();
After retrieving theese values, i try to update my viewModel, and this is where things go wrong:
for (int i =0 ; i < currentDescription.Count() - 1; i++)
{
currentViewTable.Description = currentDescription[i];
}
for (int i =0 ; i < currentColors.Count() - 1; i++)
{
currentViewTable.backgroundColor.Insert(i,currentColors[i]);
}
I get the error: reference not set to an instance of an object. Are there better ways to update my viewModel, of or any tips on what I am doing wrong?
I would first recommend to use a foreach loop instead of a regular for loop. I find it much easier to use foreach when working with lists, this will eliminate any clear "off by one" errors usually.
foreach (var description in currentDescription)
{
//do something given each element in the list
}
This seems to be a data integrity issue from what I can gather just looking at your code and given what the error is stating. I would recommend going into debug mode and looking in the Lists that you have generated to see if there are any indexes where the objects in your two lists are null, and handle these instances accordingly.

C# Auto Update List insted of .clear and refill the list

In my program I populate a list, but the next time I rerun my loop again I .clear the list to repopulate it again because some items in the list might update. How ever, is there somehow I can make it so it just auto updates the items in the list so I can't have to .clear and re add the items?
_players.Clear();
_weapons.Clear();
_entities.Clear();
var localPlayerPtr = Smurf.Memory.Read<IntPtr>(Smurf.ClientBase + Offsets.Misc.LocalPlayer);
LocalPlayer = new LocalPlayer(localPlayerPtr);
LocalPlayerWeapon = LocalPlayer.GetCurrentWeapon(localPlayerPtr);
for (var i = 0; i < _capacity; i++)
{
var entity = new BaseEntity(GetEntityPtr(i));
if (!entity.IsValid)
continue;
if (entity.IsPlayer())
_players.Add(new Player(GetEntityPtr(i)));
else if (entity.IsWeapon())
_weapons.Add(new LocalPlayerWeapon(GetEntityPtr(i)));
else
_entities.Add(new BaseEntity(GetEntityPtr(i)));
}
_lastUpdate = timeStamp;
Total repopulation of a list should be a fast enough way to deal with this. If you really need to go the other way, then your alternative is to when you run your loop:
Check your new element if it exists, and if yes, update it in the list (_players[index] = newPlayer; or something similar). Check by ID, or some other property that identifies your objects.
If it doesn't exist, add it as a new item to the list.
Store your new element in a separate list to keep track. (let's call it changesList)
(optional, if you need to prune) When you've finished going through all your new items, compare changesList with your other lists (you can probably use the Except(IEnumerable<T> list) method, and remove the objects that were not used.

Arrays/Array Lists

I am fairly new to C#
I am trying to retrieve some information from an external data source and store it in array, once it is in an array I wish to sort it by time.
I know how to do this for just one column in a row, however the information I require has multiple columns.
For example:
foreach (Appointment Appoint in fapts)
{
// Store Appoint.Subject, Appoint.Start, Appoint.Organiser.Name.ToString(), Appoint.Location in an array
}
// Sort my array by Appoint.Start
foreach ( item in myNewArray )
{
//print out Appoint.Subject - Appoint.Start, Appoint.Organiser.Name.ToString() and Appoint.location
}
Many thanks for your help.
EDIT:
I have multiple data sources which pull in this:
foreach (Appointment Appoint in fapts)
{
// Store Appoint.Subject, Appoint.Start, Appoint.Organiser.Name.ToString(), Appoint.Location in an array
}
Hence the need to sort the items in a new array, I know this isn't very efficent but there is no way of getting the information I need in any other way.
You can sort a list using the LINQ sorting operators OrderBy and ThenBy, as shown below.
using System.Linq;
and then...
var appointments = new List<Appointment>();
var sortedAppointments = list.OrderBy(l => l.Subject).ThenBy(l => l.Name).ToList();
This will create a new list of appointments, sorted by subject and then by name.
It's unclear what your final aim is but:
Use a generic List instead of an array:
See this SO question for more information as to why using a List is prefered.
List<Appointment> appointments = new List<Appointment>();
foreach (Appointment Appoint in fapts)
{
appointments.Add(Appoint);
}
foreach (var item in appointments)
{
Console.WriteLine(item.Subject);
Console.WriteLine(item.Foo);
// Here you could override ToString() on Appointment to print eveything in one Console.WriteLine
}
If the aim of your code is to order by time, try the following:
var sortedAppointments = fapts.OrderBy(a => a.Start); // assuming Start is a DateTime property of `Appointment`.
Consider a Dictionary Object instead of an array if the data is conceptually one row multiple columns.
foreach(KeyValuePair<string, string> entry in MyDic)
{
// do something with entry.Value or entry.Key
}
You already have a list of objects in fpts, sort that list itself:
fpts.OrderBy(x => x.Subject).ThenBy(x => x.Location).ToList();
LINQ is your friend here.
fapts appears to already be a collection so you could just operate on it.
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start).ToArray()
I've used the ToArray() call to force immediate evaluation and means that myNewArray is already sorted so that if you use it more than once you don't have to re-evaluate the sort.
Alternatively if you are only using this once you can just as easily miss the ToArray() portion out and then execution of the sort will be deferred until you try and enumerate through myNewArray.
This solution puts the source objects into the array, but if you are just wanting to store the specific fields you mention then you will need to use a select. You have two choices for the array item type, you can either use an anonymous class which provides difficulties if you are returning this array from a function or define a class.
For anonymous:
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start)
.Select(Appoint => new {
Start = Appoint.Start,
Organiser = Appoint.Organiser.Name.ToString(),
Location = Appoint.Location
}).ToArray();
For named class assuming class is MyClass:
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start)
.Select(Appoint => new MyClass {
Start = Appoint.Start,
Organiser = Appoint.Organiser.Name.ToString(),
Location = Appoint.Location
}).ToArray();
You have a wide range of options. The 2 most common are:
1) Create a class, then define an array or list of that class, and populate that
2) Create a structure that matches the data format and create an array or list of that
Of course, you could put the data into an XML format or dataset, but that's probably more work than you need.
public List<foo> appointments = new List<foo>();
public struct foo
{
public string subject ;
public DateTime start ;
public string name ;
public string location ;
}
public void foo1()
{
// parse the file
while (!File.eof())
{
// Read the next line...
var myRecord = new foo() ;
myRecord.subject = data.subject ;
myRecord.start = data.Start ;
myRecord.name = data.Name ;
//...
appointments.Add(myRecord);
}
}
Enjoy
(Since I can't comment and reply to the comment - it wasn't clear if he had a class, etc. or was just showing us what he wanted to do. I assumed it was just for demonstration purposes since there wasn't any info as to how the data was being read. If he could already put it into a class, than the first answer applied anyway. I just tossed the last 2 in there because they were options for getting the data first.)

Cache only parts of an object

I'm trying to achieve a super-fast search, and decided to rely heavily on caching to achieve this. The order of events is as follows;
1) Cache what can be cached (from entire database, around 3000 items)
2) When a search is performed, pull the entire result set out of the cache
3) Filter that result set based on the search criteria. Give each search result a "relevance" score.
4) Send the filtered results down to the database via xml to get the bits that can't be cached (e.g. prices)
5) Display the final results
This is all working and going at lightning speed, but in order to achieve (3) I've given each result a "relevance" score. This is just a member integer on each search result object. I iterate through the entire result set and update this score accordingly, then order-by it at the end.
The problem I am having is that the "relevance" member is retaining this value from search to search. I assume this is because what I am updating is a reference to the search results in the cache, rather than a new object, so updating it also updates the cached version. What I'm looking for is a tidy solution to get around this. What I've come up with so far is either;
a) Clone the cache when i get it.
b) Create a seperate dictionary to store relevances in and match them up at the end
Am I missing a really obvious and clean solution or should i go down one of these routes? I'm using C# and .net.
Hopefully it should be obvious from the description what I'm getting at, here's some code anyway; this first one is the iteration through the cached results in order to do the filtering;
private List<QuickSearchResult> performFiltering(string keywords, string regions, List<QuickSearchResult> cachedSearchResults)
{
List<QuickSearchResult> filteredItems = new List<QuickSearchResult>();
string upperedKeywords = keywords.ToUpper();
string[] keywordsArray = upperedKeywords.Split(' ');
string[] regionsArray = regions.Split(',');
foreach (var item in cachedSearchResults)
{
//Check for keywords
if (keywordsArray != null)
{
if (!item.ContainsKeyword(upperedKeywords, keywordsArray))
continue;
}
//Check for regions
if (regionsArray != null)
{
if (!item.IsInRegion(regionsArray))
continue;
}
filteredItems.Add(item);
}
return filteredItems.OrderBy(t=> t.Relevance).Take(_maxSearchResults).ToList<QuickSearchResult>();
}
and here is an example of the "IsInRegion" method of the QuickSearchResult object;
public bool IsInRegion(string[] regions)
{
int relevanceScore = 0;
foreach (var region in regions)
{
int parsedRegion = 0;
if (int.TryParse(region, out parsedRegion))
{
foreach (var thisItemsRegion in this.Regions)
{
if (thisItemsRegion.ID == parsedRegion)
relevanceScore += 10;
}
}
}
Relevance += relevanceScore;
return relevanceScore > 0;
}
And basically if i search for "london" i get a score of "10" the first time, "20" the second time...
If you use the NetDataContractSerializer to serialize your objects in the cache, you could use a [DataMember] attribute to control what gets serialized and what doesn't. For instance, you could store your temporarary calculated relevance value in a field that is not serialized.

Categories