Xamarin Forms and IEventAggregator - xamarin.forms

Making my APP for my job I'm getting stuck on a problem:
I have a base page to fill with information for an order
1 Client
2 Destination
3 Article
When I tap on a button I get a new page (different for what I'm serching Client, Destination, Article) with a listview of the item. On a tap of the item I would like to pass the parameter to the main page to compile the order.
I think that the better solution is to implement the IEventAggregator of PRISM, I have made also some test and it work with button, but if I try to put it on the itemtapped, it give me error.
I have created the class for the event to pass the payload.
This is the xaml.cs of the page that (in theory) must pass the selected client to the mainpage:
IEventAggregator _ea;
private Cliente cliente_selezionato = new Cliente();
...
public void TapCliente(object sender, ItemTappedEventArgs e, IEventAggregator ea )
{
cliente_selezionato = (Cliente)
((ListView)sender).SelectedItem;
_ea.GetEvent<CambioClienteEvent>().Publish(cliente_selezionato);
Navigation.PushModalAsync(new DatiTestataOC());
}
On the mainpage viewmodel I have:
public DatiTestataOCViewModel(IEventAggregator ea)
{
ea.GetEvent<CambioClienteEvent>().Subscribe(NuovoCliente);
}
private void NuovoCliente(Cliente Parameter)
{
Cliente = Parameter;
}
The error that I get are:
Errore XFC0002 EventHandler "TapCliente" with correct signature not found in type "Mokadroid.Views.SceltaClientePage".
Errore XFC0004 Missing default constructor for "Mokadroid.ViewModels.DatiTestataOCViewModel".
If you have Other solution more easy to implement I'm Open...
Thanks

I've understand that I can't simply use that part of the package, I need to start a new template using the prism template and use all the package.

Related

Custom ListPreference in AndroidX

I have customized my Android Application Setting page, I use API 21 or 26. I have added a CustomListPreference java class which was inherited from ListPreference and integrated it into the SettingActivity.
But, I relisted the system doesn't work, as SettingActivity has Setting fragment inherited from androidx.preference.PreferenceFragmentCompat and packages used for the Setting Activity are as follows:
androidx.preference.Preference
androidx.preference.ListPreference
androidx.preference.PreferenceFragmentCompat
If I use packages android.preference.Preference and android.preference.ListPreference for my Custom ListPreference, all my code stops working when Android creates objects for the Setting Activity. It crashes just after the custom ListPreference constructorwith error "Error inflating class com.signatact.doorbell.dialog.preference.AppListPreference".
Digging into details I found the reason of the crash as the last step for new object creation for Setting Activity is the cast to androidx.preference.Preference:
from PreferenceInflater.java:
import androidx.preference;
...
return (Preference) constructor.newInstance(args); // line 242
It is clear, the system fails with cast between android.preference.Preference and androidx.preference.Preference.
However, if I move my custom ListPreference file implementation to androidx, almost all method I used before for customization are not available, hereby is a list of methods which are not available, where I put my custom logic:
// Error(s): Methods don't override methods from its superclass
#Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder)
...
#Override
protected void onDialogClosed(boolean positiveResult)
It looks like Google dramatically changed their API, can anybody give idea how in AndroidX one can customize ListPreference?
In general, I need standard customization things as follows:
In a row I have a custom set of controls (3 ones - 2x text boxes and 1 checkbox) - I build a custom layout for each row in onPrepareDialogBuilder with my custom ArrayAdapter for the list
I need dynamically update the CustomListPreference values. I populate those values in onResume in SettingActivity
I need to get callback when the list is pressed and new value is selected
I found only one practical guidance here for my case which is as follows: How can I change the appearance of ListPreference Dialog but it is limited and short. I analysed the AndroidX API and it looks like I need more time to come out with a solution and thus any help / idea appreciated...
Thx, Vlad.
Simply override onClick() function to pop out an AlertDialog with custom layout. Remember to call setValue() when anything selected in the dialog.
public class ColorPreference extends ListPreference {
private CharSequence[] mEntries;
private CharSequence[] mEntryValues;
private String mValue;
private String mSummary;
private AlertDialog mDialog;
public ColorPreference(Context context) {
this(context, null);
}
public ColorPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setDefaultValue(Options.DEFAULT_PRIMARY_COLOR_STRING);
}
#Override
protected void onClick() {
mEntries = getEntries();
mEntryValues = getEntryValues();
mSummary = getSummary().toString();
mValue = getValue();
mClickedDialogEntryIndex = findIndexOfValue(mValue);
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setSingleChoiceItems(new ColorAdapter(getContext(), R.layout.pref_color_item),mClickedDialogEntryIndex,null);
mDialog = builder.create();
mDialog.show();
}
}

How do I redraw UICollectionView when MvxCollectionViewSource's data changes?

I am using Xamarin and MVVMCross to implement an IOS app. A view I'm working on is displaying correctly, but ONLY when I hard-code the data being bound to inside my ViewModel and NOT when (as by design and necessity) my data arrives late from SQLite, only an indeterminate time after the view is shown.
What I am using and accomplished so far:
Working/Showing storyboard for my View that has a UICollectionView
inside (called: CollectionView in code below)
Custom layout and XIB file for every UICollectionViewCell that also displays correctly in my view
A view that works and displays correctly only if ViewModel data is fully populated the moment ViewDidLoad() is called.
Problem:
My data in my ViewModel is updated by the Model's databases in an uncertain amount of time whilst the view is happily being shown. When I bind the data as shown below (and trying two-way/one-way bindings and the like as well), I don't get updates on my view as the final data comes in later.
What I can't seem to do:
Redraw the UICollectionView or maybe refresh the
MvxCollectionViewSource below to ensure that as the ViewModel's data changes, I can actually redraw the UICollectionView and show my
custom cells with new and updated data.
THE CODE(TM)
The CollectionView cells are implemented as follows. I followed all examples online and from that Stuart Bloke and his Kittens to make sure I implement all the patterns exactly the same:
[Register("MyCell")]
public partial class MyCell : MvxCollectionViewCell
{
public static readonly UINib Nib = UINib.FromName("MyCell", NSBundle.MainBundle);
public static readonly NSString Key = new NSString("MyCell");
public MyCell(IntPtr handle) : base(handle)
{
this.DelayBind(() => {
var set = this.CreateBindingSet<MyCell, SomeModelClass>();
set.Bind(Label1).To(item => item.Label1);
set.Bind(Label2).To(item => item.Label2);
set.Apply();
});
}
public static MyCell Create()
{
return (MyCell)Nib.Instantiate(null, null)[0];
}
}
My ViewDidLoad() in the View looks something like this:
CollectionView.RegisterNibForCell(MyCell.Nib, MyCell.Key);
var source = new MvxCollectionViewSource(CollectionView, MyCell.Key);
CollectionView.Source = source;
var set = this.CreateBindingSet<MyView, MyViewModel>();
set.Bind(source).To(vm => vm.ListOfStuff);
set.Apply();
CollectionView.ReloadData();
NB! The ListOfStuff shown above is really just a List of a custom class containing 2 strings right now.
TL:DR: I don't know ListOfStuff's values the moment I call the above code. When I hard-code them in the ViewModel, I get joy. If I don't, I don't, even as data gets updated correctly later.
I now reach out to you, the neurons of the brain of crowdsourcing...
Instead of using a List<T> use ObservableCollection<T> and new items should be added to the CollectionView.
The UI needs to know when the collection has changed. ObservableCollection<T> implements INotifyCollectionChanged and INotifyPropertyChanged and communicates with the UI when the collection changes.
You shouldn't need ReloadData() anymore if you're using ObservableCollection<T>.
This extension method might be of use when adding range of IEnumerable<T>
public static class ObservableCollectionExtensionMethod
{
public static void AddRange<TSource>(this ObservableCollection<TSource> source, IEnumerable<TSource> collection)
{
foreach (var i in collection) source.Add(i);
}
}

MVVM Light 5 - Navigation Service passing wrong parameter

I have upgraded to MVVM Light 5 and I changed my navigation methods from:
Messenger.Default.Send(new NavigateToPageMessage() { PageName = "UserDetailsPage", Parameter = id });
To the following:
_navigationService.NavigateTo(ViewModelLocator.UserDetailsPageKey, id);
The parameter I am passing does not seem to make it's way to the OnNavigatedTo event of the view anymore, the parameter is completely different, am I missing something?
EDIT:
This new method seems to give me the parameter I need:
GlobalNavigation.GetAndRemoveParameter(NavigationContext)
Although now, when the app is tombstoned, I lose that parameter entirely. Before, when the app was restored I would still have that parameter in the NavigatedTo args, this allowed me to re-hit the server with that ID and get fresh data. Why have I lost this capability
use this
protected override void OnNavigatedTo(NavigationEventArgs e)
{
GalaSoft.MvvmLight.Views.NavigationService navigationService = new GalaSoft.MvvmLight.Views.NavigationService();
var param = navigationService.GetAndRemoveParameter(this.NavigationContext);
base.OnNavigatedTo(e);
}

Notifying that all properties have changed on a ViewModel

I am working on a Silverlight application using V3 SP1 of MVVM Light Toolkit.
My application is fully French/English. All UI elements (buttons, labels, etc.) and all the data (models). I need dynamic language switching and this is fully implemented and works with anything coming from a resource file. What I am struggling with is the ViewModels.
The Models have language specific prperties (DescriptionEn, DescriptionFr) and an additional property call LocalizedDescription which uses the current culture to return call the language specific property.
When the language changes (via a button click) I raise and broadcast (via the Messenger) a property changed event.
In each of my ViewModels, I register to receive the property changed message for the language swap.
I want to notify all the properties of the ViewModel that something has changed.
From: http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged.aspx
The PropertyChanged event can indicate all properties on the object have changed by using either null or String.Empty as the property name in the PropertyChangedEventArgs.
However, since the toolkit abstracts the raising of the changed event with RaisePropertyChanged(...) I cannot get this to work. I have also examined the source of the tookit and discovered that RaisePropertyChanged calls VerifyPropertyName(..) which in turn returns an error is the property does not belong to the ViewModel. I also noticed that the VerifyPropertyName method is attributed with Conditional("DEBUG"), but even if I choose the Release configuration, the ArgumentException("Property not found") is still raised.
Does anyone know of a way to get this to work using the toolkit aside from manually calling RaisePropertyChanged for every property of the ViewModel?
Follow-up:
Based on the comment from Simon, I attempted to create my own class that extends ViewModelBase. I looked at the source on CodePlex and decided to create a single method called RaiseAllPropertyChanged(). It would simply be a copy of the RaisePropertyChanged(string propertyName) but without the parameter and without the call to VerifyPropertyName(...). I cannot get it to work. Here is what I have.
public class ViewModelBaseExtended : ViewModelBase
{
protected void RaiseAllPropertyChanged()
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(String.Empty));
}
}
}
But I get a compiler error: The event 'GalaSoft.MvvmLight.ViewModelBase.PropertyChanged' can only appear on the left hand side of += or -=. This is a copy of the code that is used in the ViewModelBase.
Can someone offer some advice as to how to get this to work?
Solution:
I copied all the code from ViewModelBase into a new class. I then added the method RaisePropertyChanged() mentioned above which instantiates the PropertyChangedEventArgs class with String.Empty. This is now the new subclass for my ViewModels.
Thanks again to Simon for leading the way!
In case you're reading this in 2016, you can use ObservableObject and notify that all of the properties have changed by doing:
RaisePropertyChanged(string.Empty);
Unfortunately this is not possible with the current code-base of MVVMLight
In the short term your have 2 options:
User your own custom base class. And by custom base class I mean "Do not inherit from the MVVMLight class".
Download and compile MVVMLight in Release mode. This will force the "VerifyPropertyName" method to be excluded. Of course then you don't get the value of property name checks.
I am sure Laurent Bugnion will have this fixed soon.
A lighter solution to this problem would have been to override RaisePropertyChanged(string propertyName) in your class :
protected override void RaisePropertyChanged(string propertyName)
{
if (propertyName != null)
{
base.RaisePropertyChanged(propertyName);
}
else
{
var handler = PropertyChangedHandler;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(null));
}
}
}

Intercept Unity 2.0 HandlerAttribute without an interface

I'm a first-time user of the AOP features of Unity 2.0 and would like some advice. My goal is to be able to log method calls in an ASPX page, like so:
public partial class Page2 : Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
[Log]
private void Testing()
{
}
}
Here is the code for the LogAttribute:
public class LogAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new LogHandler(Order);
}
}
Now the LogHandler:
public class LogHandler : ICallHandler
{
public LogHandler(int order)
{
Order = order;
}
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
string className = input.MethodBase.DeclaringType.Name;
string methodName = input.MethodBase.Name;
string preMethodMessage = string.Format("{0}.{1}", className, methodName);
System.Diagnostics.Debug.WriteLine(preMethodMessage);
return getNext()(input, getNext);
}
public int Order { get; set; }
}
The problem I have is how to use the [Log] attribute. I've seen plenty of example of how to configure the interception settings, for example:
container.AddNewExtension<Interception>();
container.Configure<Interception>().SetDefaultInterceptorFor<ILogger>(new InterfaceInterceptor());
But this implies that I have an interface to intercept, which I don't. I have the ASPX page which uses the [Log] attribute.
so how can I configure Unity to make use of the [Log] attribute? I've done this before using PostSharp and would like to be able to use Unity to do the same.
Cheers.
Jas.
You're unfortunately not going to get this to work in an ASP.NET page with Unity interception.
Unity interception uses a runtime interception model. Depending on the interceptor you choose, you'll either get a subclass with virtual method overrides to call the call handlers (VirtualMethodInterceptor) or a separate proxy object (Interface or TransparentProxyInterceptor) which execute the call handlers and then forward to the real object.
Here's the issue - ASP.NET controls creation and calls to your page, and there's no easy way to hook into them. Without controlling the creation of the page object, you can't use the VirtualMethodInterceptor, because that requires that you instantiate a subclass. And you can't use the proxy version either, because you need ASP.NET to make calls through the proxy.
PostSharp gets around this because it's actually rewriting your IL at compile time.
Assuming you could hook into the creation of the page object, you'd have to use the VirtualMethodInterceptor here. It's a private method, so you want logging on "self" calls (calls from one method of the object into another method on the same object). The proxy-based interceptors can't see those, since the proxy is a separate instance.
I expect there is a hook somewhere to customize how ASP.NET creates object - BuildManager maybe? But I don't know enough about the details, and I expect it'll require some pretty serious hacking to get work.
So, how do you get around this? My recommendation (actually, I'd recommend this anyway) is to use the Model-View-Presenter pattern for your ASP.NET pages. Make the page object itself dumb. All it does is forward calls to a separate object, the Presenter. The Presenter is where your real logic is, and is independent of the details of ASP.NET. You get a huge gain in testability, and you can intercept calls on the presenter without all the difficulty that ASP.NET gives you.

Resources