Why does an empty ReactiveCommand.CreateAsyncObservable() corrupt my Xamarin.Forms ListView, but simple ICommand works? - system.reactive

I have a very simple setup: A ContentPage has a ListView as it's only content. The list supports pull to refresh. It is binding to a command:
listView.SetBinding<MyViewModel>(ListView.RefreshCommandProperty, vm => vm.RefreshCommand);
listView.SetBinding<MyPageViewModel>(ListView.IsRefreshingProperty, vm => vm.IsRefreshing);
The list is empty and binds to an empty collection.
The refresh command is:
public ICommand LoadNodesCommand { get; set; }
If I use
LoadNodesCommand = new Command(o => {
IsRefreshing = true;
IsRefreshing = false;
});
the list refreshes if I pull and all is fine. Notice that I do not modify the list's data. It's an empty dummy command.
Next I use this as my command instead:
LoadNodesCommand = ReactiveCommand.CreateAsyncObservable(x =>
{
IsRefreshing = true;
IsRefreshing = false;
return System.Reactive.Linq.Observable.Empty<IEnumerable<Item>>();
}
Again, an empty dummy command. But this one makes the ListView behave very strangely. After the pull-to-refresh, it scrolls a bit. After each refresh it scrolls further. I do not use any other Rx methods (I'm not subscribing to the observable). Here's a screenshot of the list after three refreshes. Its first cell has been moved towards the bottom of the screen.
It really should not affect ListView...I'm not touching it, nor its data. I also tried using Device.BeginInvokeMainThread() to set the IsRereshing = false.

Related

UWP Custom ListView to scroll down

So, I have a listview and I want it whenever an item is created to scroll to that item (bottom). Because I am using MVVM I found really nice explanation on how to make a new control that inherits from listview that scrolls down. The problem is that this answer (the third) is referring to WPF 6 years ago.
I am making a UWP app, so I copied the code and tried to format it to my needs. The following code doesn't give any error or exception but instead it loads the "ChatListView" as I call it perfectly and then does nothing. The comments are only a bit edited compared to the original code.
What can I do ? Thank you in advance!
public class ChatListView : ListView
{
//Define the AutoScroll property. If enabled, causes the ListBox to scroll to
//the last item whenever a new item is added.
public static readonly DependencyProperty AutoScrollProperty =
DependencyProperty.Register(
"AutoScroll",
typeof(Boolean),
typeof(ChatListView),
new PropertyMetadata(
true, //Default value.
new PropertyChangedCallback(AutoScroll_PropertyChanged)));
//Gets or sets whether or not the list should scroll to the last item
//when a new item is added.
public bool AutoScroll
{
get { return (bool)GetValue(AutoScrollProperty); }
set { SetValue(AutoScrollProperty, value); }
}
//Event handler for when the AutoScroll property is changed.
//This delegates the call to SubscribeToAutoScroll_ItemsCollectionChanged().
//d = The DependencyObject whose property was changed.</param>
//e = Change event args.</param>
private static void AutoScroll_PropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SubscribeToAutoScroll_ItemsCollectionChanged(
(ChatListView)d,
(bool)e.NewValue);
}
//Subscribes to the list items' collection changed event if AutoScroll is enabled.
//Otherwise, it unsubscribes from that event.
//For this to work, the underlying list must implement INotifyCollectionChanged.
//
//(This function was only creative for brevity)
//listBox = The list box containing the items collection.
//subscribe = Subscribe to the collection changed event?
private static void SubscribeToAutoScroll_ItemsCollectionChanged(
ChatListView listView, bool subscribe)
{
INotifyCollectionChanged notifyCollection =
listView as INotifyCollectionChanged;
if (notifyCollection != null)
{
if (subscribe)
{
//AutoScroll is turned on, subscribe to collection changed events.
notifyCollection.CollectionChanged +=
listView.AutoScroll_ItemsCollectionChanged;
}
else
{
//AutoScroll is turned off, unsubscribe from collection changed events.
notifyCollection.CollectionChanged -=
listView.AutoScroll_ItemsCollectionChanged;
}
}
}
//Event handler called only when the ItemCollection changes
//and if AutoScroll is enabled.
//sender = The ItemCollection.
//e = Change event args.
private void AutoScroll_ItemsCollectionChanged(
object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
int count = Items.Count;
ScrollIntoView(Items[count - 1]);
}
}
//Constructor a new ChatListView.
public ChatListView()
{
//Subscribe to the AutoScroll property's items collection
//changed handler by default if AutoScroll is enabled by default.
SubscribeToAutoScroll_ItemsCollectionChanged(
this, (bool)AutoScrollProperty.GetMetadata(typeof(ChatListView)).DefaultValue);
}
}
If you want to create a chat application you can use the ItemsStackPanel's ItemsUpdatingScrollMode particular property to KeepLastItemInView value to scroll to the latest item.
Usage:
<ListView>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel ItemsUpdatingScrollMode="KeepLastItemInView" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
Note: KeepLastItemInView enum member was introduced in the 14393 SDK.
Related link:
https://learn.microsoft.com/en-us/uwp/api/Windows.UI.Xaml.Controls.ItemsStackPanel#properties_
The accepted answer is pretty nice. However I there is one thing it won't do (at least if I simply copy and paste the above XAML): it won't do its intended scrolling if, say, the user was away from that page while new items were added, and then they navigated to the page.
For that I had to hook into
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (MyListView.Items.Count == 0)
return;
object lastItem = MyListView.Items[MyListView.Items.Count - 1];
MyListView.ScrollIntoView(lastItem);
}

How to display a progress icon when clicking "Show more" on a CellTree?

I'm using the CellTree for the very first time and slowly getting a hang of it.
Right now I'm struggling how to display a progress icon (just like when opening a tree node) beside the "Show more" text when clicking on it.
Any ideas?
I guess it is a common problem that users are clicking on "Shore more" multiple times if they do not get any visual feedback - which leads to multiple server calls and a bunch of duplicated nodes (in my case).
Any time you send a call off to the server, you know that you are making the call. Likewise, you will get a call back into your code, whether it succeeded or failed.
For the sake of this example, I'm assuming you are using something like GWT-RPC (since the question doesn't specify):
// field to track if we're loading
private boolean isLoading = false
//...
// inside a method which needs to load data:
if (isLoading) {
return;//don't attempt to load again
}
isLoading = true;
//just before we start the call, show the loading indicator
loadingIndicator.flash();//or whatever you'd like to make it do
//then start the request
service.getMyData(param1, param2, new AsyncCallback<MyData> () {
public void onSuccess(MyData response) {
//loading was successful, so stop the loading marker
isLoading = false;
loadingIndicator.success();
//do something with the data
//...
}
public void onFailure(Throwable ex) {
//loading stopped, but it was an error, tell the user
isLoading = false;
loadingIndicator.error();
}
});

How do I prevent a CellTable RowElement from being redrawn after a SelectionChangehander fires?

I'm probably doing something else wrong but I've followed examples given here:
How to remove a row from the Cell Table
and
GWT get CellTable contents for printing or export
to accomplish my goal and the result is close but not quite right.
I have a page with two widgets. The first wiget contains a CellTable that uses an aSync ListDataProvider to pull results and populate a table. The table has a selection change event handler associated with it that loads further details about the selected item into the second widget below it.
public OrderAdminTable() {
selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
#Override
public void onSelectionChange(SelectionChangeEvent event) {
OrderAdminListProxy selected = selectionModel.getSelectedObject();
if (selected != null && orderSnapShot != null) {
orderSnapShot.loadSnapShot(selected);
}
}
});
initTable();
this.addStyleName("order-list fixed_headers BOM");
this.setSelectionModel(selectionModel);
}
Once the second widget has loaded the details about the selected item, the user can remove the item from the table/list by clicking a button in the RootPanel that is the parent of both widgets.
searchView.getCmdReview().addClickHandler(new ClickHandler() {
#Override public void onClick(ClickEvent event) {
searchView.getOrderAdminSnapshot().reviewOrder();//this line calls a web service that deletes the item from the server data
dataProvider.getList().remove(searchView.getOrderAdminSnapshot().getSelectedOrder());
for(int i=0;i<table.getRowCount();i++){
TableRowElement row = table.getRowElement(i);
for(int j=0;j<row.getCells().getLength();j++){
if(row.getCells().getItem(j).getInnerText().contains(searchView.getOrderAdminSnapshot().getSelectedOrder().getSalesOrderNumber())){
row.setAttribute("removed", "true");
row.addClassName("hidden");
}
}
}
}
});
This all works fine until you select another item in the table. When that happens, the selection change event seems to redraw the table and remove my custom attribute and class from the previously selected item. This makes it appear in the list again.
The ultimate goal here is to avoid a round trip to the server to pull new results when you remove an item from the list. The line "searchView.getOrderAdminSnapshot().reviewOrder();" makes a web service call that removes the item from the data on the server side so it does not appear in subsequent reloads.
Is there some way to force the selection change event to maintain the state of the table row that was previously selected? Is there a better way to remove the selected item from the list? Any advice would be appreciated.
Once you remove the object from the list dataProvider.getList().remove, it should disappear from the table. There is no need to hide the row - this row should be gone. So your loop should never find it.

How to automatically check the checkboxes in the gridview when displaying?

I'm using GXT 3 to build a GridView that will display "incidents".
What I want to do is that when it renders it, I want some checkboxes to be checked, others to empty, according to the boolean in the database.
Below you have my code:
CheckBoxSelectionModel<IncidentDto> isIncidentCM = new CheckBoxSelectionModel<IncidentDto>(incidentProperties.incident());
allColumns.add(isIncidentCM.getColumn());
ColumnModel<IncidentDto> columnModel = new ColumnModel<IncidentDto>(allColumns);
final Grid<IncidentDto> grid = new Grid<IncidentDto>(store, columnModel);
grid.setSelectionModel(isIncidentCM);
add(grid);
And the IncidentProperties value provider:
IdentityValueProvider<IncidentDto> incident();
I'm not sure if you can bind the selection value to a boolean property, but you could add a listener to the Grid to update the checkboxes based on the boolean condition.
grid.addBeforeShowHandler(BeforeShowEvent event) {
#Override
public void onBeforeShow(BeforeShowEvent event) {
List<IncidentDto> itemsToSelect = new ArrayList<IncidentDto>();
for (IncidentDto incident : store.getAll()) {
if (incident.getBooleanProperty()) { //whatever your property is called
itemsToSelect.add(incident);
}
}
isIncidentCM.setSelection(itemsToSelect);
}
}
There may be other implications in using a BeforeShowEvent depending on how/when you populate your store, render the grid, etc. but assuming your store is fully loaded and the property available from your store objects I believe this should accomplish your goal.

MVVM viewmodel property triggering update

I started implementing MVVM for one of my Silverlight applications.
(I'm not using any toolkit).
My page contains a section with two combo boxes. Selecting an item in one of these combos triggers a search that updates a grid visible below the combos.
Each combo's selected item is bound to a property in my view model. The setter of these properties raise the INotifyPropertyChanged property change and updates the data bound to the grid automatically.
Everything was fine until I needed to add a reset button which purpose is to reset the search parameters i.e.: each combo box should not indicate any item and the grid should be empty.
If the reset function in the viewmodel updates the backing fields, the UI won't reflect the changes as RaisePropertyChanged will not be called.
If the reset function in the viewmodel updates the properties, the UI will reflect the changes but the grid will be updated twice: when reseting the first property to null and also for the second
Any help appreciated
/// <summary>Selected user.</summary>
public User SelectedUser
{
get { return _selectedUser; }
set
{
_selectedUser = value;
RaisePropertyChanged("SelectedUser");
UpdateProducts();
}
}
/// <summary>Selected product category.</summary>
public ProductCategory SelectedProductCategory
{
get { return _selectedProductCategory; }
set
{
_selectedProductCategory = value;
RaisePropertyChanged("SelectedProductCategory");
UpdateProducts();
}
}
// Reset option 1
public void Reset()
{
_selectedUser = null;
_selectedProductCategory = null;
_products = null;
}
// Reset option 2
public void Reset()
{
SelectedUser = null;
SelectedProductCategory = null;
// No need to update Products which has already been updated twice...
}
This is something that really urks me in many frameworks, WPF included. What you need is some concept of delaying the response to change notifications so that the user never sees intermediate states. However, you can't change the way WPF responds to your notifications, so the best you can do is to delay your notifications until "after the dust has settled". In your case, you will want to change both of the backing fields before any notifications are sent. Your reset method can encode this idea as follows:
public void Reset()
{
_selectedUser = null;
_selectedProductCategory = null;
_products = null;
RaisePropertyChanged("SelectedUser");
RaisePropertyChanged("SelectedProductCategory");
}
In my opinion, the way WPF synchronously updates the display in response to notifications of change is just plain wrong. Their DependencyProperty system gives them an opportunity to merely mark dependencies as dirty and perform recalculation at a later time.
I use the idea of marking as dirty and asynchronous recalculation as a general solution to the problem you've noted in this question and these days I could not imagine programming without it. It's a shame that more frameworks do not work this way.
You can raise a single PropertyChanged event for all properties after you updated the backing fields:
RaisePropertyChanged(String.Empty);
If you use the backing Fields you would have to call
RaisePropertyChanged("SelectedUser");
RaisePropertyChanged("SelectedProductCategory");
in the Reset() method.