MVVMCross for android - how to do binding in code? - xamarin.android

I want to use MVVMCross, however for my android application I also want to use other libraries (sliding menu and action bar) which require me to inherit my activity classes from their custom class. This prevents me from inheriting MvxActivity, but I noticed that in MVVMCross for iOS, you can do all your bindings in code (see https://github.com/slodge/NPlus1DaysOfMvvmCross/blob/master/N-00-FirstDemo/FirstDemo.Touch/Views/FirstView.cs)
var set = this.CreateBindingSet<FirstView, FirstViewModel>();
set.Bind(textEditFirst).To(vm => vm.FirstName);
set.Bind(textEditSecond).To(vm => vm.LastName);
set.Bind(labelFull).To(vm => vm.FullName);
set.Apply();
Is there any way to do that in Android?

Yes - you can use fluent bindings in Android if you want to.
Exactly the same code should work.
You'll need to get references to the ui controls using FindViewById<Type>(), then you can bind them.
For example, in TipCalc you can add identified controls like:
<EditText
android:id="#+id/FluentEdit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:textSize="24dp"
android:gravity="right"
/>
and then implement binding using:
protected override void OnViewModelSet()
{
SetContentView(Resource.Layout.View_Tip);
var edit = this.FindViewById<EditText>(Resource.Id.FluentEdit);
var set = this.CreateBindingSet<TipView, TipViewModel>();
set.Bind(edit).To(vm => vm.SubTotal);
set.Apply();
// for non-default properties use 'For':
// set.Bind(edit).For(ed => ed.Text).To(vm => vm.SubTotal);
// you can also use:
// .WithConversion("converter", "optional parameter")
// .OneTime(), .OneWay() or .TwoWay()
}
Additionally, you can convert any FooActivity into a data-binding MvxFooActivity by:
inheriting from FooActivity to provide events from lifetime events in an EventSourceFooActivity
inheriting from EventSourceFooActivity to provide a datacontext in an MvxFooActivity
you can then write your code inside activities inheriting from MvxFooActivity
To see, the code required, see:
https://github.com/slodge/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Droid.Fragging/MvxEventSourceFragmentActivity.cs
https://github.com/slodge/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Droid.Fragging/MvxFragmentActivity.cs
You'll see the same code in all the mvx adapted Activities - MvxActivity, MvxTabActivity, ... There is a little cut-and-paste here, but as much code as possible is place in shared extension methods.
In previous versions, people have used this technique to bind monogame and google ads activities - eg see Insert a Monogame view inside MvvmCross monodroid Activity

Related

local:MvxLang failed to bind the resource text to TextView when it is part of itemtemplate of MvxListView in Xamarin app

Issue:- local:MvxLang failed to bind the resource text to TextView when it is part of itemtemplate of MvxListView in Xamarin app using localization feature in MVVMCross.
My application is Xamarin.android, using MVVMCross, used Localization feature of MVVMCross with resx file.
Running sample can be found here:https://github.com/pallaviak1/RestaurantBilling.Droid
I am getting localized string through code using below syntax in viewmodel (AllBillsViewModel) BillClickedCommand:-
_dialogService.ShowAlertAsync( string.Format(TextSource.GetText("InformationReceivedMessage"), bill.CustomerEmail, bill.AmountPaid), TextSource.GetText("InformationReceivedHeader"), TextSource.GetText("InformationReceivedButtonText"));
Also my main view page where local:mvxLang is button attribute, which shows button text from resource of selected culture, also works well.
<Button ... local:MvxLang="Text ViewBillsResourceText" local:MvxBind="Click NavigateAllBills" />
Problem:- However when I am using MvxLang in controls which are part of item template of MvxListView control the localized string is blank (not populated).
file:- RestaurantBilling.Droid\RestaurantBilling.Droid\Resources\layout\ListItem_Bill.axml
The control which is part of item template view looks like below:-
<TextView android:layout_alignParentBottom="true"
android:layout_marginLeft="50dp"
android:layout_marginRight="16dp"
android:layout_width="90dp"
android:layout_height="wrap_content"
local:MvxLang="Text CustomerEmailTextView" />
The mail list control looks like below:-
<Mvx.MvxListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="ItemsSource AllBills; ItemClick BillClickedCommand"
local:MvxItemTemplate="#layout/listitem_bill" />
CustomerEmailTextView resource key is present in resx files as Name: AllBillsViewModel.CustomerEmailTextView, value : "US Customer Email".
Just to add, my Localization code is as below:-
Resource files are present in library MVVMCross.Localization, whose reference is added in RestaurantBilling.core library as well as android project.
code in core, App.cs file as below:-
Mvx.RegisterSingleton<IMvxTextProvider>
(new ResxTextProvider(Strings.ResourceManager, currentCulture));
BaseViewModel has below code:-
public IMvxLanguageBinder TextSource =>
new MvxLanguageBinder("", GetType().Name);
Getting warning in visual studio output window as below:
[0:] MvxBind:Warning: 9.78 Unable to bind: source property source not found Property:TextSource on Bill
02-10 07:41:52.020 I/MvxBind ( 4357): 9.78 Unable to bind: source property source not found Property:TextSource on Bill
I could not find enough help on MVVMCross formal website, also could not find much discussion points on same. The same thing is working in sample downloaded "My Trains" when referred from pluralsight training.
Please help.
my problem is resolved. ref link: How to bind ItemClick in MvxListView in MvxListView
This link issue is somewhat similar. I resolved the issue in below way,
whatever TextSource property I have added in viewModel, I need to put in Bill.cs class which is model
public IMvxLanguageBinder TextSource
{
get {
//Mvx.Trace("****************TextSource get in bill.cs**************************");
return new MvxLanguageBinder("", GetType().Name);
}
}
Then, the resource text key looks like below:-
Bill.CustomerEmailTextView
This is kind of workaround, actually BaseViewModel has TextSource property however it is not useful in case of item template case.
Please let me know if you have better solution because we are kind of mixing viewmodel and model properties.

How to bind the value of slider from C# code in windows phone 8.1..?

I want to bind a custom seek bar to the position of media element.
From XAML we can achieve it by
Value="{Binding ElementName=mediaElement, Path=Position, Mode=TwoWay, Converter={StaticResource FormatConverter}}"
where FormatConverter is a converter class written to convert mediaElement object to slider value during binding.
But I am creating the media element from code, so I want to write C# code to achieve the above. How to do it..?
Please take a look at MSDN. I think this should work, if done like this:
Binding myBind = new Binding() { ElementName = "mediaElement", Path = new PropertyPath("Position"), Converter = this.Resources["FormatConverter"] as IValueConverter, Mode = BindingMode.TwoWay };
mySlider.SetBinding(Slider.ValueProperty, myBind);
Of course you need to clarify which Resources you use - page's or app's.

Setter on text only called after edit on Android, live on iOS

When I'm binding text to an input on iOS, my setter is called each time a character is added, on iOS, but not on Android.
If I put a breakpoint on a property that is binded to a TextField in iOS, each time a character is entered, the property setter will be called, but not on an Android EditText.
It makes more complex ViewModels with several input attached to getter/setter tested on iOS completely useless on Android since it cannot be used.
Is there a way to make the "MvxBind="Text SomeProperty" acting like iOS on Android?
Events like "AfterTextChanged" (any binding to a command) aren't property-friendly, and would break my ViewModel. I don't want to have a platform-dependent workaround.
[Edit]
// Droid. It calls the TotalAmount setter once the editing is done.
<EditText local:MvxBind="Text TotalAmount,
Mode=OneWayToSource; Text TotalAmountString, Mode=OneWay" />
// Touch. It calls the TotalAmount setter on key press.
set.Bind(MyTotalAmountTextField)
.For(v => v.Text)
.To(vm => vm.TotalAmount).OneWayToSource();
set.Bind(MyTotalAmountTextField)
.For(v => v.Text)
.To(vm => vm.TotalAmountString).OneWay();
By the way, the displayed property is always formatted with a dollar sign, that's why I'm using an half-duplex approach for binding.
Appart from this live (iOS) versus after-edit (Droid) problem, the bindings are working well.
The default behaviour for TwoWay Text binding on both Android and iOS is to do per character binding.
You can see this behaviour in, for example, the N=0 video at 18:43 - http://youtu.be/_DHDMNB_IeY?t=18m43s
If you are not seeing this behaviour in your EditText then I guess it might be down in some way to your app or perhaps to a bug (e.g. perhaps in OneWayToSource binding somehow - this certainly isn't as commonly used as other binding modes).
To workaround this, I can only think to suggest:
Log it as an issue with a reproducible case (github repo) on GitHub/MvvmCross - someone there might be able to help - or you might be able to fix it yourself.
Try TwoWay binding instead
Try creating your own custom binding or your own custom control - this is actually very easy to do - see the tutorials N=28 and n=18 on http://mvvmcross.blogspot.com - for example you could try inheriting from EditText to create something like;
public class MyEditText : EditText {
public MyEditText(Context c, IAttributeSet a) {
this.AfterTextChanged += (s,e) => MyTextChanged.Raise(this);
}
public event EventHandler MyTextChanged;
public string MyText {
get { return Text; }
set { Text = value; }
}
}

Does MvvmCross allow binding of ViewModel properties to controls created on the fly?

I have an application in which most of the controls are created in code and then added to the layout using AddView method. Does the framework allow binding of ViewModel properties to controls using code or it has to be done in the axml file only?
Alright, after a lot of struggle I finally got the answer.
I had to do the following things.
1) Added an import statement :
using Cirrious.MvvmCross.Binding.BindingContext;
2) Added the following code:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Hello);
TableLayout containerLayout = this.FindViewById<TableLayout>(Resource.Id.containerLayout);
if (containerLayout != null)
{
TableRow newRow = new TableRow(base.ApplicationContext);
newRow.SetMinimumHeight(50);
var txtRace = new EditText(ApplicationContext);
txtRace.Hint = "Race";
var bindingSet = this.CreateBindingSet<HelloView, HelloViewModel>();
bindingSet.Bind(txtRace).To(vm => vm.Race);
bindingSet.Apply();
newRow.AddView(txtRace);
containerLayout.AddView(newRow);
}
}
I already have a "TableLayout" in my HelloView.axml file and all that I am doing in this is creating a new EditText box control (txtRace) and adding it to the view and at the same time binding it to the "Race" property of HelloViewModel object.
I spend a lot of time trying to figure out in what namespace CreateBindingSet() method exists because VS2012 was not giving me any intelliscence on that.
Hope this helps someone facing similar issue.
Yes MvvmCross supports binding properties to controls created at runtime. You can watch this tutorial by the awesome Mr. Stuart in his N+1 series.
http://www.youtube.com/watch?feature=player_embedded&v=cYu_9rcAJU4
Note: He has shown this many a times in the series but I remember this one on the top of my head right now.

How to call a MXML class in ActionScript3.0 in Flex 3

I have a page made of custom components. In that page I have a button. If I click the button I have to call another page (page.mxml consisting of custom components). Then click event handler is written in Action-script, in a separate file.
How to make a object of an MXML class, in ActionScript? How to display the object (i.e. the page)?
My code:
page1.mxml
<comp:BackgroundButton x="947" y="12" width="61" height="22"
paddingLeft="2" paddingRight="2" label="logout" id="logout"
click="controllers.AdminSession.logout()"
/>
This page1.mxml has to call page2.mxml using ActionScript code in another class:
static public function logout():void {
var startPage:StartSplashPage = new StartSplashPage();
}
Your Actionscript class needs a reference to the display list in order to add your component to the stage. MXML is simply declarative actionscript, so there is no difference between creating your instance in Actionscript or using the MXML notation.
your function:
static public function logout():void {
var startPage:StartSplashPage = new StartSplashPage();
}
could be changed to:
static public function logout():StartSplashPage {
return new StartSplashPage();
}
or:
static public function logout():void {
var startPage:StartSplashPage = new StartSplashPage();
myReferenceToDisplayListObject.addChild( startPage );
}
If your actionscript does not have a reference to the display list, than you cannot add the custom component to the display list. Adding an MXML based custom component is no different than adding ANY other DisplayObject to the display list:
var mySprite:Sprite = new Sprite();
addChild(mySprite)
is the same as:
var startPage:StartSplashPage = new StartSplashPage();
myReferenceToDisplayListObject.addChild( startPage );
Both the Sprite and the StartSplashPage are extensions of DisplayObject at their core.
You reference MVC in the comments to another answer. Without knowing the specific framework you've implemented, or providing us with more code in terms of the context you are trying to perform this action in, it is difficult to give a more specific answer.
I assume that you are on a page with a set of components and want to replace this set of components on the page with a different set of components. My apologies in advance if this is not what you are trying to do.
You can do this using ViewStacks and switching the selected index on selection -- this can be done either by databinding or by firing an event in controllers.AdminSession.logout() and listening for that event in the Main Page and switching the selectedIndex of the view stack in the handler function.
MainPage.mxml
<mx:ViewStack>
<views:Page1...>
...
<comp:BackgroundButton x="947" y="12" width="61" height="22"
paddingLeft="2" paddingRight="2" label="logout" id="logout"
click="controllers.AdminSession.logout()"/>
</views:Page1...>
<views:Page2 ...>
...
<comp:Comp1 .../>
<comp:Comp2 .../>
</views:Page2>
I think you may use state to do you work.
You may take a look at http://blog.flexexamples.com/2007/10/05/creating-view-states-in-a-flex-application/#more-221
Edit:
I am not sure I fully understand your case.
As I know, you may make a new state in page1.mxml, and name it, eg. secondPageState, and then put the custom component page2.mxml in the secondPageState.
In the controller, you need an import statement to import the page1 component and make a public var for the page1 component, eg. firstPage.
Then, the code will similar to:
public function logout():voild
{
firstPage.currentState = "secondPageState";
}
Another solution:
If you don't like the change state solution, you may try to use the addchild, to add the custom component to your application.

Resources