Is it possible to use multiple NIBs mixed in a UICollectionView? - xamarin.ios

I have to create a view such as the iTunes Appstore with 3 different types of blocks: one for an image (ImageBlock), another for a list of object1 (Type1List) and a third for a list of object2 (Type2List).
I have functional MvxCollectionViewCells for all of the three, but I'd need to make an UICollectionView out of these three. I have an list of these lists (Items). A single list (Item) has a value called "type", which is an integer value 1, 2 or 3. I'd have to get this integer value for each Item and determine the NIB I want to use for this specific cell.
//Collection = UICollectionView
//ViewModel.Items = MvxObservableCollection<Item>
public override void ViewDidLoad()
{
base.ViewDidLoad();
var set = this.CreateBindingSet<View, ViewModel>();
// NIBs for types 1, 2 and 3 respectively
Collection.RegisterNibForCell(Type1List.Nib, Type1List.Key);
Collection.RegisterNibForCell(Type2List.Nib, Type2List.Key);
Collection.RegisterNibForCell(ImageBlock.Nib, ImageBlock.Key);
// This shows all items of type 1 correctly, and others using type 1 XIB with no contents
Source = new MvxCollectionViewSource(Collection, Type1List.Key);
// This gives a Null Reference Exception if used in place of the previous one
//Source = new MvxCollectionViewSource(Collection);
set.Bind(Source)
.To(vm => vm.Items);
// This takes the items and calculates the height for Collection in this View
// All types will differ in size
set.Bind(CollectionHeight)
.For(v => v.Constant)
.To(vm => vm.Items)
.WithConversion("IosCollectionHeight", Collection);
Collection.Source = Source;
// Set to 200 for now to see the cells
// TODO: set a working variable size
(Collection.CollectionViewLayout as UICollectionViewFlowLayout).ItemSize = new CoreGraphics.CGSize(UIScreen.MainScreen.Bounds.Width, 200);
//(Collection.CollectionViewLayout as UICollectionViewFlowLayout).EstimatedItemSize = new CoreGraphics.CGSize(UIScreen.MainScreen.Bounds.Width, 100);
set.Apply();
Collection.ReloadData();
}
So I have currently 2 problems:
I don't know how to use different NIBs by type for my UICollectionView
I don't know how to resize cells by type
All three cell NIBs are tested and working as intended, but this CollectionView is problematic.
-Pasketi

Related

Adding multiple objects to ArrayList that match a selected string

What I am trying to do: Fill the namesArray with all the objects that match a certain criteria.
The Activity object is made of four strings "category, name, note, time"
The getCategory(activityArray) returns a string which is the selected category, I'm trying to sort the objects where their category matches the selected category into the new namesArray
So this namesArray will end up containing only Activity objects with selected category.
This is my code:
public static void viewActivity(ArrayList<Activity> activityArray) {
String categ = getCategory(activityArray);
String holder;
ArrayList<Activity> namesArray = new ArrayList<Activity>();
for(Activity obj : activityArray) {
holder = obj.getCategory();
if(holder == categ) {
System.out.println(obj.getName());
namesArray.add(obj);
}
}
}
When I run the debugger, first iteration works, and the first object where it's category matches the selected category is added to the namesArray. But then the if statement just seems to stop working, the holder String does not change and stays the same.
So how do I get my method to add every matching object to the namesArray instead of only the first one?
Thanks
Solved by changing the
if(holder == categ) {
to
if(holder.equals(categ)) {

Update a row in google sheets based on duplicate

I'm designing a script that takes an object (jsonData[data]) and inputs its values into a different sheet based on which product it is.
Currently the script inputs all the data into a new row each time the form reaches a new stage, however the form goes through 4 stages of approval and so I'm finding each submission being entered into 4 different rows. Each submission has an "Id" value within the object which remains the same (but each submission could also be on any row in the sheet as it's used a lot).
I'm checking whether the ID exists in the sheet and using iteration to find the row number:
function updatePlatformBulkInfo(jsonData) {
var sheetUrl = "https://docs.google.com/spreadsheets/d/13U9r9Lu2Fq1WTT8pQ128heCm6_gMmH1R4O6u8e7kvBo/edit#gid=0";
var sheetName = "PlatformBulkSetup";
var doc = SpreadsheetApp.openByUrl(sheetUrl);
var sheet = doc.getSheetByName(sheetName);
var rowList = [];
var formId = jsonData["Id"];
var allSheetData = sheet.getDataRange().getValues();
setLog("AllSheetData = " + allSheetData[1][11]) //Logs to ensure data is collected correctly
var rowEdited = false;
var rowNumber = 0;
//Check whether ID exists in the sheet
for (var i = 0; i < allSheetData.length; i++) {
if(allSheetData[i][11] == formId) {
rowEdited = true;
} else {
rowNumber += 1;
}
}
My issue is with the next part:
//Append row if ID isn't duplicate or update row if duplicate found
if (rowEdited == false) {
for (var data in jsonData) {
rowList.push(jsonData[data])
}
setLog("***Row List = " + rowList + " ***");
setLog("***Current Row Number = " + rowNumber + " ***");
sheet.appendRow(rowList);
} else if(rowEdited == true){
var newRowValue = jsonData[data];
sheet.getRange(rowNumber, 1).setValues(newRowValue);
}
Everything works fine if the duplicate isn't found (the objects values are appended to the sheet). But if a duplicate is found I'm getting the error:
Cannot find method setValues(string)
This looks to me like i'm passing a string instead of an object, but as far as I'm aware I've already converted the JSON string into an object:
var jsonString = e.postData.getDataAsString();
var jsonData = JSON.parse(jsonString);
How can I modify my script to write the updated data to the matched row?
It's unclear based on your code whether or not you will actually write to the correct cell in the case of a duplicate. As presented, it looks as though you loop over the sheet data, incrementing a row number if the duplicate is not found. Then, after completing the loop, you write to the sheet, in the row described by rowNumber, even though your code as written changes rowNumber after finding a duplicate.
To address this, your loop needs to exit upon finding a duplicate:
var duplicateRow = null, checkedCol = /* your column to check */;
for(var r = 0, rows = allSheetData.length; r < rows; ++r) {
if(allSheetData[r][checkedCol] === formId) {
// Convert from 0-base Javascript index to 1-base Range index.
duplicateRow = ++r;
// Stop iterating through allSheetData, since we found the row.
break;
}
}
In both cases (append vs modify), you seem to want the same output. Rather than write the code to build the output twice, do it outside the loop. Note that the order of enumeration specified by the for ... in ... pattern is not dependable, so if you need the elements to appear in a certain order in the output, you should explicitly place them in their desired order.
If a duplicate ID situation is supposed to write different data in different cells, then the following two snippets will need to be adapted to suit. The general idea and instructions still apply.
var dataToWrite = [];
/* add items to `dataToWrite`, making an Object[] */
Then, to determine whether to append or modify, test if duplicateRow is null:
if(dataToWrite.length) {
if(duplicateRow === null) {
sheet.appendRow(dataToWrite);
} else {
// Overwriting a row. Select as many columns as we have data to write.
var toEdit = sheet.getRange(duplicateRow, 1, 1, dataToWrite.length);
// Because setValues requires an Object[][], wrap `dataToWrite` in an array.
// This creates a 1 row x N column array. If the range to overwrite was not a
// single row, a different approach would be needed.
toEdit.setValues( [dataToWrite] );
}
}
Below is the most basic solution. At the end of this post, I'll expand on how this can be improved. I don't know how your data is organized, how exactly you generate new unique ids for your records, etc., but let's assume it looks something like this.
Suppose we need to update the existing record with new data. I assume your JSON contains key-value pairs for each field:
var chris = {
id:2,
name: "Chris",
age: 29,
city: "Amsterdam"
};
Updating a record breaks down into several steps:
1) Creating a row array from your object. Note that the setValues() method accepts a 2D array as an argument, while the appendRow() method of the Sheet class accepts a single-dimension array.
2) Finding the matching id in your table if it exists. The 'for' loop is not very well-suited for this idea unless you put 'break' after the matching id value is found. Otherwise, it will loop over the entire array of values, which is redundant. Similarly, there's no need to retrieve the entire data range as the only thing you need is the "id" column.
IMPORTANT: to get the row number, you must increment the array index of the matching value by 1 as array indices start from 0. Also, if your spreadsheet contains 1 or more header rows (mine does), you must also factor in the offset and increment the value by the number of headers.
3) Based on the matching row number, build the range object for that row and update values. If no matching row is found, call appendRow() method of the Sheet class.
function updateRecord(query) {
rowData = [];
var keys = Object.keys(query);
keys.forEach(function(key){
rowData.push(query[key]);
})
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheets()[0];
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var idColumn = 1;
var ids = sheet.getRange(2, idColumn, sheet.getLastRow() - 1, 1).getValues();
var i = 0;
var matchedRow;
do {
if (ids[i] == query.id) { matchedRow = i + 2; }
i++;
} while (!matchedRow && i < ids.length);
if (matchedRow) {
var row = sheet.getRange(matchedRow, idColumn, 1, rowData.length);
row.setValues([rowData]);
} else {
sheet.appendRow(rowData);
}
}
NOTE: if your query contains only some fields that need to be updated (say, the 'id' and the 'name' field), the corresponding columns for these fields will be
headers.indexOf(query[key]) + 1;
Possible improvements
If the goal is to use the spreadsheet as a database and define all CRUD (Create, Read, Write, Delete) operations. While the exact steps are beyond the scope of the answer, here's the gist of it.
1) Deploy and publish the spreadsheet-bound script as a web app, with the access set to "anyone, even anonymous".
function doGet(e) {
handleResponse(e);
}
function doPost(e) {
handleRespone(e);
}
function handleResponse(e) {
if (e.contentLength == -1) {
//handle GET request
} else {
//handle POST request
}
}
2) Define the structure of your queries. For example, getting the list of values and finding a value by id can be done via GET requests and passing parameters in the url. Queries that add, remove, or modify data can be sent as payload via POST request. GAS doesn't support other methods besides GET and POST, but you can simulate this by including relevant methods in the body of your query and then selecting corresponding actions inside handleResponse() function.
3) Make requests to the spreadsheet URL via UrlFetchApp. More details on web apps https://developers.google.com/apps-script/guides/web

Select UI Element by filtering properties in coded ui

I have a web application. And I am using coded ui to write automated tests to test the application.
I have a dropdown with a text box. Which on entering values in the textbox, the values in the dropdown gets filtered based on the text entered.
If I type inside textbox like 'Admin', I will get below options like this:
And I need to capture the two options displayed.
But using IE Developer tool (F12), I am not able to capture the filtered options, because the options that are displayed do not have any unique property (like this below). And the options that are NOT displayed have a class="hidden" property
Any way to capture the elements that are displayed by applying some kind of filter like 'Select ui elements whose class != hidden'
Thanks in advance!!
HI please try below code will it works for you or not.By traversing all those controls that have class ="hidden"
WpfWindow mainWindow = new WpfWindow();
mainWindow.SearchProperties.Add(HtmlControl.PropertyNames.ClassName, "hidden");
UITestControlCollection collection = mainWindow.FindMatchingControls();
foreach (UITestControl links in collection)
{
HtmlHyperlink mylink = (HtmlHyperlink)links;
Console.WriteLine(mylink.InnerText);
}
I'm not sure there is a way to do it by search properties, but there are other approaches.
One way would be to brute force difference the collections. Find all the list items, then find the hidden ones and do a difference.
HtmlControl listControl = /* find the UL somehow */
HtmlControl listItemsSearch = new HtmlControl(listControl);
listItemsSearch.SearchProperties.Add(HtmlControl.PropertyNames.TagName, "li");
HtmlControl hiddenListItemsSearch = new HtmlControl(listControl);
hiddenListItemsSearch.SearchProperties.Add(HtmlControl.PropertyNames.TagName, "li");
hiddenListItemsSearch.SearchProperties.Add(HtmlControl.PropertyNames.ClassName, "hidden");
var listItems = listItemsSearch.FindMatchingControls().Except(hiddenListItemsSearch.FindMatchingControls());
You will only be able to iterate this collection one time so if you need to iterate multiple times, create a function that returns this search.
var listItemsFunc = () => listItemsSearch.FindMatchingControls().Except(hiddenListItemsSearch.FindMatchingControls());
foreach(var listItem in listItemsFunc()){
// iterate 1
}
foreach(var listItem in listItemsFunc()){
// iterate 2
}
The other way I would consider doing it would be to filter based on the controls which have a clickable point and take up space on the screen (ie, not hidden).
listItemsSearch.FindMatchingControls().Where(x => {
try { x.GetClickablePoint(); return x.Width > 0 && x.Height > 0; } catch { return false; }
});

GXT re-arrange data array index in TreeStore

Currently, I'm using gxt 3.0.6
I have a TreeStore let's called it "treeStore", with model data "ParentDto".
private TreeStore<ParentDto> treeStore;
treeStore = new TreeStore<ParentDto>(new ModelKeyProvider<ParentDto>(){
#Override
public String getKey(ParentDto item){
return String.valueOf(item.getParentId());
}
});
Inside ParentDto there is a list of ChildDto. If there is ParentDto data which has list of ChildDto, I want to show it in a tree grid. I use basic tree grid from this link
https://www.sencha.com/examples/#ExamplePlace:basictreegrid
Using that reference, if I try to add 1 ParentDto everything works fine, but when the problem is when I add many Parent Dto.
Here is my code for adding data into the treeStore
public void fillTreeStore(List<ParentDto) listParent){
treeStore.clear();
for(ParentDto parentDto : listParent){
treeStore.add(parentDto);
if(parentDto.getListChild().size() > 0){
for(ChildDto childDto : parent.getListChild()){
treeStore.add(parentDto,childDto);
}
}
}
In my case, I only need 1 level parent and child tree so this code is enough.
I try to debug my code use this expression
treeStore.getAll().get(index);
When I add 1 ParentDto (parentA) which has 1 Child (childA). The result will be
treeStore.getAll().get(0) -> contain parentA
treeStore.getAll().get(1) -> contain childA
But if I add 2 ParentDto (parentA, parentB) and each of them have 1 child (childA,childB). The result will be
treeStore.getAll().get(0) -> contain parentA
treeStore.getAll().get(1) -> contain parentB
treeStore.getAll().get(2) -> contain childA
treeStore.getAll().get(3) -> contain childB
But in the grid, those data will be shown perfectly fine :
row 1 : parentA (this row can expand)
row 2 : childA (the expanded row form parentA)
row 3 : parentB (this row can expand)
row 4 : childB (the expanded row form parentB)
I need to render icon if the data is "parent" so I use this code :
(icon_variable).addBeforeRenderIconCellEventHandler(new BeforeRenderIconCellEventHandler() {
#Override
public void onBeforeRenderIconCell(BeforeRenderIconCellEvent event) {
if(treeStore.getParent(treeStore.get(event.getSelectedRowIndex())) == null){
//#render icon here
}
}
});
The problem is at this code
treeStore.get(event.getSelectedRowIndex())
When parentB is added it will trigger addBeforeRenderIconCellEventHandler method. event.getSelectedRowIndex() will get the row index based on "grid's perspective". At the second row, from grid's perspective (childA), event.getSelectedRowIndex() will return 1. But from "treeStore's perspective", index 1 is "parentB", so my icon render is messed up.
That's why, the result I need in treeStore is like this
treeStore.getAll().get(0) -> contain parentA
treeStore.getAll().get(1) -> contain childA
treeStore.getAll().get(2) -> contain parentB
treeStore.getAll().get(3) -> contain childB
My solution :
To solve this problem, for now, I use 2 Stores, the first one is TreeStore, and the second one is ListStore. Each time parent and child are added, I insert them at TreeStore and ListStore. In the ListStore, I keep parent's and child's index to always match with grid's perspective, so that whenever addBeforeRenderIconCellEventHandler is triggered, I use ListStore to get the data.
In my opinion, this solution is not good enough but because in my case, the maximum data can be added into the store less than 50, it's enough.
It looks like this is default behavior. You didn't say what it is you are trying to do but my guess is you can do it with the methods they provide. I'm guessing you are trying to traverse the tree by looking at the parent and then all of it's children before moving on to the next parent. Something like this would do it.
for (ParentDto parent : treeStore.getRootItems()){
for (ChildDto child : treeStore.getChildren(parent)){
}
}

How can I customize SharePoint list column aggregation (total) calculations?

I have a SharePoint list column of type 'Single line of text'. Out of the box SharePoint only provides the ability to display a 'Count' total for this column type. I would like to be able to perform a custom aggregation on the data (specifically to sum numeric data held as text to overcome this deficiency).
I have found examples for doing something similar for calculated columns using XSLT and Javascript but I believe that both of these approaches fail where the data is paginated (only aggregating the subset of the list content displayed on screen).
I want to retain the functionality of the ListViewWebPart (rendering, sorting, filtering, view definition, action menus etc.) but add this functionality. How can I do this?
The only things you can do with totals are:
Average; Count; Max; Min; Sum; Standard Deviation; Variance
Not sure how to calculate anything else.
I've not had a chance to fully test it but this is the best I could come up with:
Create a WebPart which contains two controls:
A ViewToolBar with the context of the list/view to be displayed
A Literal containing the rendered HTML of the view to be displayed
This will then render as the original list/view.
On rendering the WebPart, get the items from the view, specifying the RowLimit as the maximum value so that all items in are retrieved (not just the first page).
Iterate over the items, calculating the total in a suitable data type to retain precision.
Render the total as a hidden value in the HTML and overwrite the rendered Count total with Javascript such as by the method described here.
A rough sketch of the code:
public sealed class TextAggregatingWebPart : WebPart {
protected override void CreateChildControls() {
base.CreateChildControls();
var web = SPContext.Current.Web;
var list = web.Lists[SourceList];
var view = list.Views[ViewOfSourceList];
var toolbar = new ViewToolBar();
var context = SPContext.GetContext(
Context, view.ID, list.ID, SPContext.Current.Web);
toolbar.RenderContext = context;
Controls.Add(toolbar);
var viewHtml = new Literal {Text = view.RenderAsHtml()};
Controls.Add(viewHtml);
}
protected override void Render(HtmlTextWriter writer) {
EnsureChildControls();
base.Render(writer);
var web = SPContext.Current.Web;
var list = web.Lists[SourceList];
var view = list.Views[ViewOfSourceList];
var items = list.GetItems(new SPQuery(view) {RowLimit = uint.MaxValue});
foreach (SPItem item in items) {
// Calculate total
}
// Render total and Javascript to replace Count
}
}
Note that this doesn't solve the problem with the Modify View screen only showing Count as a total for text columns. Also there is a possibility that changes to the list between the initial rendering by the view and the retrieval of the items for aggregation could produce discrepancies between the total and the displayed items.

Resources