Flex Mate - PropertyInjector fired twice when mapped between Manager and View - flex4

My application uses Flex 4 and Mate framework 0.9.1. I'm facing an issue with the PropertyInjector being fired twice when there's a mapping between a manager and a view.
I have not shared the original code here, but it looks similar to the following:
Based on an event, a property (someData) in MyManager is updated. A property injector updates this new value in a target view (MyView). The issue is - when onDataChanged is invoked and the property someData is updated, the method "set someData" in the view is fired twice. I know that the view is instantiated only once because I have debugged the init and creationComplete events. The source property in MyManager is also updated only once as per the trace.
This would indicate that the property injector is fired twice. Would anyone know under what conditions this can happen? Any pointers would be appreciated!
MyEventMap.mxml
<EventHandlers type="{DataChangedEvent.GET}" debug="true">
<MethodInvoker generator="{MyManager}" method="onDataChanged"
arguments="{[event.x,event.y,event.name]}">
</MethodInvoker>
</EventHandlers>
<Injectors target="{MyView}" debug="true">
<PropertyInjector targetKey="someData" source="{MyManager}"
sourceKey="someData">
</PropertyInjector>
</Injectors>
DataHolder.as
public class DataHolder
{
public function DataHolder()
{
}
public var x:Number;
public var y:Number;
public var name:String;
}
MyManager.as
public class MyManager extends EventDispatcher
{
....
[Bindable] public var someData:DataHolder;
public function onDataChanged(x:Number,y:Number,name:String):void{
trace("dataChanged");
var temp:DataHolder = new DataHolder();
temp.name=name;
temp.x=x;
temp.y=y;
someData = temp;
}
}
MyView.mxml
public function set someData(data:DataHolder):void {
trace("setSomeData x="+data.x+",y="+data.y+",name="+data.name);
}

I found the problem is due to a combination of BabelFX 2.0 and Mate framework 0.9.1. Below is the BabelFX related code. Using MyView as a target of the ResourceInjector causes all PropertyInjector's related to it being fired twice. Is there an issue in the code below?
testLocalization.mxml:
<?xml version="1.0" encoding="utf-8"?>
<LocaleMap enableLog="true"
xmlns="http://l10n.babelfx.org/"
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:factory="mx.core.*" >
<mx:Metadata>
[ResourceBundle("testprop")]
</mx:Metadata>
<ResourceInjector bundleName="testprop" target="{MyView}">
<ResourceSetter property="myButton.label" key="testsomething.title"/>
</ResourceInjector>
MyView.mxml (also the main app)
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" xmlns:local="*"
xmlns:mate="http://mate.asfusion.com/">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
<mate:Debugger level="{Debugger.ALL}" />
<local:testLocalization/>
<local:MyEventMap/>
</fx:Declarations>
<s:Button id="myButton">
</s:Button>
</s:Application>
testprop.properties
testsomething.title = SOMETHING

Related

ViewBinding for an inner CustomView

I have a next hierarchy of nesting for my views:
XML for a fragment has a MyCustomView1 inside.
MyCustomView1 creates MyCustomView2 programmatically inside (doesn't have it in XML) and uses it for setting up some parameter of MyCustomView1.
MyCustomView1 looks like:
class MyCustomView1 #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0) : ConstraintLayout(context, attrs, defStyle) {
val myCustomView2 = MyCustomView2(context, R.layout.my_custom_view_2)
init {
inflate(context, R.layout.my_custom_view_1, this)
...
}
fun setUpMyCustomView2() {
myCustomView2.view1 = ...
}
}
MyCustomView2 looks like:
class MyCustomView2(
context: Context,
layoutResource: Int) : BaseView(context, layoutResource) {
//...
}
MyCustomView2 XML file looks like:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/myCustomView2Field"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
And I need to get myCustomView2Field in my fragment with using view binding but can't see it without proper view binding for this view inside. I see that I just need to make an appropriate view binding for this view but don't understand completely where to do it with this chain of inner dependencies.
Did someone face with the similar problem? How did you resolve it?

Xamarin - PushAsync fails when using ContentPage.BindingContext

I have two pages, the main page, which displays informations, and a login page, which should be shown, if I'm not logged in. The main page get it's informations via the ContentPage.BindingContext attribute. I'm using following method to show the login screen: Application.Current.MainPage.Navigation.PushModalAsync(new LoginPage());
When I run the app with the ContentPage.BindingContext, the code fails with System.NullReferenceException: 'Object reference not set to an instance of an object.'. If I remove the BindingContext, start the app, and add the BindingContext with Xamarin Hot Reload, the login screen will be displayed.
Here is an excerpt from the code:
MainPage.xaml
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyCalendar"
xmlns:iOsSpecific="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
x:Class="MyCalendar.MainPage"
iOsSpecific:Page.UseSafeArea="True">
<ContentPage.BindingContext>
<local:MyViewModel/>
</ContentPage.BindingContext>
</ContentPage>
MyViewModel.cs
public class MyViewModel : INotifyPropertyChanged
{
private readonly MyData data;
public MyViewModel()
{
data = new MyData();
}
protected void RaisePropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
MyData.cs
public class MyData
{
public MyData()
{
// Fetching data .....
case Error:
Application.Current.MainPage.Navigation.PushModalAsync(new LoginPage(), true);
break;
case OK:
// Execute code
break;
default:
Console.WriteLine("SOME ERROR");
break;
}
}
Is there any way to show the login page?
The problem was that the BindingContext, when set in the .xaml, still executed before the MainPage was (fully) initialized. Accordingly, an NPE always came.
I was able to fix it simply by adding a new line MainPage.BindingContext = new MyViewModel(); in the App.xaml.cs and removing the BindingContext from the .xaml. This ensured that the BindingContext is only executed when the MainPage is completely loaded.

Xamarin Forms - Adding constructor to MasterDetailPage.Master

I have a Xamarin.Forms app with a master-detail page and it works well.
But I've recently needed to add a parameter to the constructor of the master page (AttendPageMaster), but now I need to pass this constructor.
How do I add a parameter to the xaml?
<MasterDetailPage.Master>
<pages:AttendPageMaster x:Name="MasterPage" />
</MasterDetailPage.Master>
The code behind page with constructor:
public AttendPageMaster(AttendanceViewModel viewModel)
{
}
Let me know if you need any more info.
You do not have to pass ViewModel to Page via constructor, you can set the Page's BindingContext:
<MasterDetailPage.Master>
<pages:AttendPageMaster x:Name="MasterPage">
<pages:AttendPageMaster.BindingContext>
<myViewModels:AttendanceViewModel />
</pages:AttendPageMaster.BindingContext>
</pages:AttendPageMaster>
</MasterDetailPage.Master>
This solution will work if your ViewModel does not expect any parameters in constructor. Otherwise you may consider using ViewModelLocator and DI to inject the constructor parameters.
Please note that myViewModels should be defined in the header of your XAML page as xmlns:myViewModels.
P.S.: Previously you mentioned that you got an exception while trying to use code behind approach. You could easily solve it by setting the Title property of the AttendPageMaster. Example:
new AttendPageMaster(new AttendanceViewModel()){ Title = " " };
I managed to do this from the code behind by creating the menu page in the constructor of the masterdetail and assigning it to the "Master" property:
AttendMasterPageMaster MasterPage;
public AttendMasterPage(AttendanceViewModel viewModel)
{
MasterPage = new AttendMasterPageMaster(viewModel);
Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(StartPage), viewModel));
Master = MasterPage;

Custom Page Transitions in Xamarin Forms when using ContentPage

I am creating a xamarin forms app and i want to change the transition when navigating between pages. I couldnt use https://components.xamarin.com/view/customnavpage because im using ContentPage. I also read the famous thread on xamarin forums but nothing worked for me. here is the link.
https://forums.xamarin.com/discussion/18818/custom-page-transitions-with-xamarin-forms
i'm using xamarin forms 2.3.4.247 and android api level 25 and appCompat 23.3.0.
any idea?
I found a workaround and I answer my own question maybe it helps someone else.
First we need to create a custom renderer for any platform(in this case android)
. create a new class under your Renderers folder in Droid target.
CustomTransitionsRenderer.cs
[assembly:ExportRenderer(typeof(NavigationPage),typeof(CustomTransitionsRenderer))]
namespace TesarSmartwatchForms.Droid.Renderers
{
public class CustomTransitionsRenderer : Xamarin.Forms.Platform.Android.AppCompat.NavigationPageRenderer
{
//SwitchContentAsync
protected override void SetupPageTransition(Android.Support.V4.App.FragmentTransaction transaction, bool isPush)
{
if (isPush)
transaction.SetCustomAnimations(Resource.Animator.move_in_left, 0);
else //prevView enter:
transaction.SetCustomAnimations(Resource.Animator.side_in_right, 0);
}
}
}
and then add your custom animations under Droid>Resources>animator like these:
move_in_left.xml
<?xml version="1.0" encoding="UTF-8" ?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/accelerate_interpolator">
<translate
android:fromXDelta="-100%p"
android:toXDelta="0"
android:duration="300"/>
</set>
and slide_in_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/linear_interpolator">
<translate
android:fromXDelta="100%p"
android:toXDelta="0"
android:duration="300" />
</set>
then we need to modify our android theme just adding this line
<item name="android:windowBackground">#android:color/transparent</item>
and remember to navigate between pages with the default navigation transition, just like this
Navigation.PushAsync(page);

Can't declare custom event in custom component in Flash Builder

I am trying to create a datagrid component and want it to dispatch an event to the main application when datagrid is created. However, I got errors saying
"Type was not found or was not a compile-time constant:dataGridComp"
"Call to a posibly undefined methoud dataGridComp"
my component
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Metadata> //declare my event
[Event(name="dataGridComp", type="flash.events.Event")]
</fx:Metadata>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
protected function dataGrid_creationCompleteHandler(event:FlexEvent):void
{
// TODO Auto-generated method stub
var e:dataGridComp = new dataGridComp("dataGridComp"); //problem here
dispatchEvent(e); //want to dispatch my event object when the datagrid is created
}
]]>
</fx:Script>
<s:DataGrid id="dataGrid" editable="true" x="51" y="34" width="734" height="153"
creationComplete="dataGrid_creationCompleteHandler(event)" requestedRowCount="4">
........
..........
</s:DataGrid>
</s:Group>
Any idea how to solve this? I appreciate any help. Thanks a lot.
A couple of things that you can change/consider:
1) To dispatch your custom event, you need to use the Event class, or create a custom class that extends Event, so you can dispatch a real Event object. In your case, w/ just a custom event type, use the Event class like this:
dispatchEvent( new Event("dataGridComp") );
2) The creationComplete event you are using, is dispatched before the component is added to the stage. Events dispatched by something that is not on the display list won't necessarily be heard by your main application.

Resources