How to bind IReadonlyReactiveList to ReactiveCollectionView - xamarin.ios

I have a view model that retrieve a list of data from rest service, and store inside a property.
private readonly ObservableAsPropertyHelper<IReadOnlyReactiveList<Customer>> _searchResultCustomer;
public IReadOnlyReactiveList<Customer> SearchResultCustomer => _searchResultCustomer.Value;
I am trying to do a binding between SearchResultCustomer to ReactiveCollectionView like this this.OneWayBind(ViewModel, vm => vm.SearchResultCustomer, v => v.cvResult.DataSource);.
Without surprise it doesn't work, and the type expected from v.cvResult.DataSource is IUICollectionViewDataSource.
How do I solve this, is there any available example for Xamarin.IOS? Thanks :)

Binding to a UI collection, such as UITableView or UICollectionView, works a bit differently. Conveniently, you don't even need to create your own UICollectionViewSource. Just create a custom cell with an initialize method and bind like this:
ViewModel.WhenAnyValue(vm => vm.SearchResultCustomer).BindTo<Customer, CustomerCell>(collectionView, cell => cell.Initialize());
Credit to Paul Reichelt at this thread who answered a similar question, except for UITableView.
I forked his example project and added a working example for UICollectionView. I use IReactiveList instead of IReadOnlyReactiveList, but you should be able to modify it to fit your needs. Here's the source. Hope it helps.

Related

Additional GS1 codes support in Acumatica

We need to add support for GS1 Barcode Customer Part Number in the Purchases - Receive and Put Away screen, it is not supported by default and I can't a find a way to add it.
From looking at the source code, it seems like I need to override GS1Support property or the GetGS1ApplicationSteps() method on PX.Objects.PO.WMS.ReceivePutAway class but I can't find a way to to this. I tried to override using PXGraphExtension method:
public class ReceivePutAway_Extension : PXGraphExtension<ReceivePutAway>
{
}
but then I get the following error:
CS0311 The type 'PX.Objects.PO.WMS.ReceivePutAway' cannot be used as type parameter 'Graph' in the generic type or method 'PXGraphExtension'. There is no implicit reference conversion from 'PX.Objects.PO.WMS.ReceivePutAway' to 'PX.Data.PXGraph' class.
UPDATE:
After updating the extension class declaration as suggested, now the error is gone but I'm still unable to find a way to override GetGS1ApplicationSteps() method on the BLC extension class PX.Objects.PO.WMS.ReceivePutAway, .
Does anybody know how to make the override work for a class like this or maybe has good suggestion on how to add support for additional GS1 barcodes?
ReceivePutAway is not a Graph, therefore you cannot do a simple Graph Extension directly on it. ReceivePutAway inherits from WMSBase which is actually defined as a Graph Extension. This means that you need to end up with a second level graph extension.
If you need to customize ReceivePutAway, I would suggest to try the approach mentioned here:
https://help-2021r1.acumatica.com/(W(1))/Help?ScreenId=ShowWiki&pageid=c86fdae8-fef9-4490-aa57-3528d0fa172e
Refer to section 'Second-Level BLC Extension' in the above link. In your case, it might be something like this:
public class ExtensioReceivePutAway_Extension :
PXGraphExtension<ReceivePutAway, ReceivePutAwayHost>
{
}

NSFetchedResultsController + UICollectionViewDiffableDataSource + CoreData - How to diff on the entire object?

I'm trying to use some of the new diffing classes built into iOS 13 along with Core Data. The problem I am running into is that controllerdidChangeContentWith doesn't work as expected. It passes me a snapshot reference, which is a reference to a
NSDiffableDataSourceSnapshot<Section, NSManagedObjectID>
meaning I get a list of sections/Object ID's that have changed.
This part works wonderfully. But the problem comes when you get to the diffing in the collection view. In the WWDC video they happily call
dataSource.apply(snapshot, animatingDifferences: true)
and everything works magically, but that is not the case in the actual API.
In my initial attempt, I tried this:
resolvedSnapshot.appendItems(snapshot.itemIdentifiersInSection(withIdentifier: section).map {
controller.managedObjectContext.object(with: $0 as! NSManagedObjectID) as! Activity
}, toSection: .all)
And this works for populating the cells, but if data is changed on a cell (IE. the cell title) the specific cell is never reloaded. I took a look at the snapshot and it appears the issue is simply that I have references to these activity objects, so they are both getting updated simultaneously (Meaning the activity in the old snapshot is equivalent to the one in the new snapshot, so the hashes are equal.)
My current solution is using a struct that contains all my Activity class variables, but that disconnects it from CoreData. So my data source became:
var dataSource: UICollectionViewDiffableDataSource<Section, ActivityStruct>
That way the snapshot actually gets two different values, because it has two different objects to compare. This works, but it seems far from elegant, is this how we were meant to use this? Or is it just in a broken state right now? The WWDC video seems to imply it shouldn't require all this extra boilerplate.
I ran into the same issue and I think I figured out what works:
There are two classes: UICollectionViewDiffableDataSource and UICollectionViewDiffableDataSourceReference
From what I can tell, when you use the first, you're taking ownership as the "Source of Truth" so you create an object that acts as the data source. When you use the second (the data source reference), you defer the "Source of Truth" to another data source (in this case, CoreData).
You would instantiate a ...DataSourceReference essentially the same way as a ...DataSource:
dataSourceReference = UICollectionViewDiffableDataSourceReference(collectionView: collectionView, cellProvider: { (collectionView, indexPath, object) -> UICollectionViewCell? in
let identifier = <#cell identifier#>
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
<#cell configuration#>
return cell
})
And then later when you implement the NSFetchedResultsControllerDelegate, you can use the following method:
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference)
{
dataSourceReference.applySnapshot(snapshot, animatingDifferences: true)
}
I watched the WWDC video as well and didn't see this referenced. Had to make a few mistakes to get here. I hope it works for you!

Problem with binding to MvxView with Xamarin.iOS and MvvmCross

I have a problem with implementing following scenario using Xamarin.iOS and MvvmCross (6.2.3.0). I have a view and in it's viewmodel I have a property which is a list of objects. I need to dynamically generate a label and a textfield for each of this list entry on my view. So I decided to implement a custom view for this. Here is what I tried so far:
In my viewcontroller, in CreateView I simply add my custom UIView. I can see it's content on my view. In ViewDidLoad() I create bindings:
private void CreateBindings()
{
var set = this.CreateBindingSet<MyController, MyViewModel>();
set.Bind(myCustomControl).For(x => x.DataContext).To(vm => vm.MyListOfObjects);
set.Apply();
}
MyCustomControl code is as follows:
public class MyCustomControl : MvxView
{
public MyCustomControl() {
//DataContext is always null here!
//I'd like to get access to list of objects here, add controls for each entry and make sure they are binded to a viewmodel's list of objects somehow.
}
}
I noticed that the list of objects in my viewmodel is set later than a constructor call for MyCustomControl is being made, so it makes sense that DataContext in MyCustom control is null. I'm missing something obvious I believe. Can someone point me in a proper direction? I would be very grateful.
I tried this example, which is exactly what I'm trying to achieve, but no luck so far ;(
N=32 - ViewModels and MvxView on the iPad - N+1 days of MvvmCross
https://www.youtube.com/watch?v=cYu_9rcAJU4&list=PLR6WI6W1JdeYSXLbm58jwAKYT7RQR31-W&index=35&t=1649s
Take a look at 23:43 of the video you posted.
You should do the view binding inside a DelayBind method.
this.DelayBind(() => {
var set = this.CreateBindingSet<MyCustomControl, MyItemViewModel>();
set.Bind(badgeLabel).For(v => v.Text).To(x => x.BadgeText);
set.Bind(topSummary).For(v => v.Text).To(x => x.TopSummary);
set.Bind(bottomSummary).For(v => v.Text).To(x => x.BottomSummary);
set.Bind(this.Tap()).For(v => v.Command).To(x => x.Edit);
set.Apply();
});
MvvmCross will do the binding for you.
Also Mind that the proper types have to be passed to CreateBindingSet.
On a side node, MvvmCross N+1 videos are from 2013 and some things has changed since then.
You can find some good examples there but sometimes it just won't work anymore.
If you are new to MvvmCross, download the source code of Playground project for reference:
https://github.com/MvvmCross/MvvmCross/tree/develop/Projects/Playground
and join the #mvvmcross slack channel if you need more help
https://xamarinchat.herokuapp.com/
MvvmCross is really great... once you grasp the basic concepts.

CodedUI- Best way to create and use UIObject Repository( that requires minimum effort when UI changes)

I started working with CodedUI few months before to automate a desktop Application(WPF).
Just checking out for the best ways to create a framework for my Application.
As, I have seen in other automation tools, I feel the heart of an automation framework using any tool(UI Based) is the way it's object Repository is created i.e. how well the UI objects are defined. A Cleaner and well defined Object Repository always proves to be very helpful when it comes to updating your tests.
I am trying to discover the best way to store my UIObjects so that in case of any UI changes in my Application, I have to put minimum effort to update my automation test.
Also, If an Object changes in application, updating it only at one place should solve the problem.
This can be any kind of change like :
->change in just a property(This I feel would be very easy to update in automation Test. The best and Easiet way I feel is to simply update the .uitest file(the xml file) if possible.)
->change in hierarchy and position
->entirely new object added
For the 2nd and 3rd changes, updating scripts become a difficult job, esp if the UIObject is being referred at may places, in many TestMethods, or Modules.
Also, I have generally seen that in Test Methods, Variable Declarations are done to create a reference to the UIMap objects and those variables are further used in the TestMethod Code.
So, in this case If the UI of my application changes, I will have to update the variable decalaration in each of the Test Methods. I want to reduce this effort to changing the variable decalaration only at one place. OfCourse, I cannot have all the code inside only one Test Method. One way that came to my mind is as:
Can't I have simply one common place for all these Variable decalarations. We can give a unique and understandable name to each UIObject e.g.: The decalratoions will look like:
UITabPage UITabPage = this.UIMap.UISimWindow.UISelectEquipmentTabList.UITabPage;
WpfRow UIRow = this.UIMap.UISimWindow.UISelectEquipmentTabList.UITabPage.UIEquipmentDetailsTable.UIRow;
WpfText UIEquipmentTagText = this.UIMap.UISimWindow.UISelectEquipmentTabList.UITabPage.UIEquipmentDetailsTable.UIRow.UITagCell.UIEquipmentTagText;
WpfCheckBox UIEquipmentCheckBox = this.UIMap.UISimWindow.UISelectEquipmentTabList.UITabPage.UIEquipmentDetailsTable.UIRow.UICheckBoxCell.UICheckBox;
....
....
and use these variables wherever required. Hence, In case of any chnages also, there will be only one place where you will need to update thse objects.
But for this, These varaibles must be made STATIC. What can be problem with making these Object Variables static?
Please provide your suggestion on this topic. May be what I am thinking is not possible or practical. I just want to choose the best way to start with before I go too far with the automation scripts and realize later that my approach wasn't a good one.
Thanks in Advance,
Shruti
Look into using descriptive programming instead of using the UIMaps.
Make a static class with generic functions to assist. Going to give you some examples of how to set it up.
For example:
public WinWindow parentwin(string ParentControlName)
{
var parentwin = new WinWindow();
parentwin.SearchProperties.Add("Control Name", ParentControlName);
return parentwin;
}
public WinWindow childwin(string ChildWinControlName, string ParentControlName)
{
var childwin = new WinWindow(parentwin(ParentControlName));
childwin.SearchProperties.Add("Control Name", ChildWinControlName);
return childwin;
}
public WinButton button(string ButtonName,string ChildWinControlName, string ParentControlName)
{
var childwin = childwin(ChildWinControlName,ParentControlName);
var button = new WinButton(childwin);
button.SearchProperties.Add("Name", ButtonName);
}
public void ClickButton(string ButtonName,string ChildWinControlName, string ParentControlName)
{
var button = button(ButtonName,ChildWinControlName,ParentControlName);
Mouse.Click(button);
}
public void ChangeFocus(WinWindow NewFocus)
{
var NewFocus = new NewFocus();
NewFocus.SetFocus();
}
public void ChangeFocus(WinWindow NewFocusChild, string c)
{
var a = new NewFocus();
a.SetFocus();
}
ChangeFocus(childwin("WelcomeForm", "MainForm");
ClickButton("&OK", "WelcomeForm", "MainForm");

Kohana helper attribute

I have a question that keeps bothering me. Currently, I have started using Kohana 3.2 Framework. I've written a helper to handle some functionality - I have a number of methods, which are (as it should be) declared STATIC. But, all of these methods are somehow working with the database, so I need to load a model. Currently, every method has a non-static variable like this:
$comment = new Model_Comments;
$comment->addComment("abc");
OK, it seems to be working, but then I wanted to get rid of this redundancy by using class attribute to hold the instance of the model (with is class as well).
Something like this:
private static $comment; // Declaring attribute
self::$comment = new Model_Comment; // This is done within helper __constuct method
self::$comment->addComment("abc"); // And call it within the method.
But, I got failed with: Call to a member function addComment() on a non-object
Question is: is it possible to do it ? Maybe there are some other approaches ?
Sorry for a long story and, thanks in advice! :P
A static method cannot call a non-static method without operating on an instance of the class. So, what you're proposing won't work. There may be a way do accomplish something similar, but what about trying the following:
You could implement the singleton or factory pattern for your "helper" class. Then, you could create the model (as an attribute) as you instantiate/return the instance. With an actual instance of your "helper" class, you won't have to worry about the static scope issues.
In other words, you can create a helper-like class as a "normal" class in your application that, upon creation, always has the necessary model available.
I'd be happy to help further if this approach makes sense.
David

Resources