How do I dinamically create and add view of MvxFrameControl in another layout - mvvmcross

How can I dynamically create and add view to specific region of current view, for example create and add androids MvxFrameControl ?
I need some like on Android
var frameHelpControl = (MvxFrameControl)ViewFactory.CreateByModel(frameHelpControlModel);
regionForHelpFrame.AddView(frameHelpControl, new ViewGroupParameters(.....));
I need ability to create View by model via DI Factory and adding it to any region. I know that there exist such factory inside standart presenter but what if i need create controller for me without presenter?
For example, I have code in old iOS project
private void AddBubble(BubbleMC newBubbleModel)
{
var bubble = (BubbleVC)ViewContollerFactory.ViewControllerByModel(newBubbleModel, true);
bubble.View.Frame = View.Bounds;
AddChildViewController(bubble);
Add(bubble.View);
bubble.DidMoveToParentViewController(this);
}
How can I rewrite it on mvvmCross Android / iOS (i think it will the same) for BubbleVC that will inherits MvxFrameControl and will needed to be added in View.

You can load a ViewModel like this:
var loaderService = Mvx.Resolve<IMvxViewModelLoader>();
var viewModelRequest = MvxViewModelRequest<MyViewModel>.GetDefaultRequest();
var viewModel = loaderService.LoadViewModel(viewModelRequest, null);
Then you can just create a MvxFrameControl and assign the BindingContext the the newly created ViewModel.
However, instead of creating views and adding to a parent view, you should rely on the mechanic of something like RecyclerView on Android and TableView/CollectionView on iOS.

Related

MvvmCross, Mvx.MvxListView and custom binding in MvxItemTemplate

In a MvvmCross app, I have a page with the classic chat behavior (WhatsApp like): this page shows the history of messages exchanged between two users with the last message at the bottom of the list.
I've successfully implemented the view in Windows Phone 8.1, but I'm struggling with a problem in Android.
I'll give you a short introduction and description of my problem and next I'll go through technical details.
INTRODUCTION
Actually, my need is to apply different style to messages sent by different users: tipically align left messages sent from other user and align right messages sent by me (I do this through the weight property); I need to apply a different drawable background and set different gravity property also.
I use custom binding because, AFAIK, those properties cannot be binded with classic binding: local:MvxBind="Gravity MyPropery" doesn't work because there is no Gravity property.
So, I have of course two axml files:
the first one contains the Mvx.MvxListView
the second one contains the item template for MvxListView
And I've created three different custombinding (for Background, Gravity and Weight) following these guides:
http://slodge.blogspot.it/2013/06/n28-custom-bindings-n1-days-of-mvvmcross.html
In MvvmCross how do I do custom bind properties
THE PROBLEM
I want that, when a user opens the chat View, the list widget shows automatically the last message. To accomplish this, I scroll programmatically the list to the last message and this seems to be the problem.
If I don't scroll programmatically, when I open the page and scroll manually to the end of the page, all custom bindings are applied successfully: I can see messages aligned right and left, with correct background and weight applied.
If I force the scroll programmatically, when I open the page I see a strange behavior: all the messages are present (classic binding, such as Text property, have been successfully applied), but custom bindings are missing. All the messages have the same background and are all left aligned.
BUT, if I scroll manually up and down, the custom binding are processed and the messages are displayed with right style.
DEBUG ANALYSIS
To debug the behaviour I've put a simple static counter in a custom binding procedure to track every time the function is processed.
public class LinearLayoutWeightTargetBinding : MvxAndroidTargetBinding
{
public static int debugCounter = 0;
public LinearLayoutWeightTargetBinding(object target) : base(target)
{
}
protected LinearLayout MyTarget
{
get { return (LinearLayout)Target; }
}
public override Type TargetType { get { return typeof(bool); } }
protected override void SetValueImpl(object target, object value)
{
var ll = (LinearLayout)target;
var itsMe = (bool)value;
var weight = itsMe ? (float)20.0 : (float)5.0;
var layoutParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WrapContent, weight);
ll.LayoutParameters = layoutParams;
Log.Debug("MeeCHAT", string.Format("LinearLayoutWeightTargetBinding::SetValueImpl::ItsMe:{0} - counter:{1}", itsMe, ++debugCounter));
}
public override MvxBindingMode DefaultMode { get {return MvxBindingMode.TwoWay;} }
}
By this way I saw that actually by scrolling up and down the custom bindings are applied (debugCounter increases correctly).
BUT when I apply the programmatically scroll, only the first 10 items are processed by the custom bindings and this seems the reason why I see the messages without the right style. Because I have a long list, only the first 10 items are processed but they are not visible (they are out of the visible area) and the visibile items have not been processed.
TECHNICAL DETAILS
Here are some details related to technical aspects of my app. I try to give you all important aspects.
ORGANIZATION OF THE VIEWS
By following the approach described by Greg Shackles in this article http://gregshackles.com/presenters-in-mvvmcross-navigating-android-with-fragments/ I have just one general Activity for the app and one Fragment for each View; then through a Presenter is possible to activate the right ViewModel and manage the stack of the navigation.
The Fragment for the View where I have the Mvx.MvxListView widget is
public class MyMatchersChatView : MvxFragment
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var ignore = base.OnCreateView(inflater, container, savedInstanceState);
var result = this.BindingInflate(Resource.Layout.MyMatchersChatView, null);
var headerFrame = result.FindViewById<FrameLayout>(Resource.Id.headerFrameMyMatchersChatView);
var headerWidget = new HeaderWidget() { ViewModel = this.ViewModel };
var tran = ChildFragmentManager.BeginTransaction();
tran.Add(headerFrame.Id, headerWidget, "headerMyMatchersChat");
tran.Commit();
var listView = result.FindViewById<MvxListView>(Resource.Id.messagesList);
listView.SetSelection(listView.Adapter.Count - 1); // Scroll to the end of the list
return result;
}
}
The statement listView.SetSelection(listView.Adapter.Count - 1); force the list to scroll to the end.
Last two things: how the custom bindings are registered and how are applied in axml file.
REGISTRATION OF CUSTOM BINDING
In Setup.cs I have:
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterFactory(new MvxCustomBindingFactory<LinearLayout>("CustomWeight",
(b) => new LinearLayoutWeightTargetBinding(b)));
}
APPLYING OF CUSTOM BINDING
In my axml I have:
<LinearLayout
android:orientation="horizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
local:MvxBind="CustomWeight IsCurrentUser">
LISTVIEW AND VIEWMODEL
Here is the code of ListView
<Mvx.MvxListView
android:id="#+id/messagesList"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="ItemsSource MyMessages"
local:MvxItemTemplate="#layout/mymatcherschatview_itemtemplate" />
and the property in ViewModel
private IEnumerable<MyMatchMessageModel> _myMessages;
public IEnumerable<MyMatchMessageModel> MyMessages
{
get { return _myMessages; }
set
{
_myMessages = value;
RaisePropertyChanged(() => MyMessages);
}
}
ENVIRONMENT
Finally, here is my environment:
Visual Studio 2015
MvvmCross 3.5.1
Core targets: .NET Framework 4.5, Windows 8, ASP.NET Core 5.0, Windows Phone 8.1, Xamarin.Android, Xamarin.iOS, Xamarin.iOS (Classic)
The Android app target is API Level 19 (Xamarin.Android v4.4 Support)
Xamarin 3.11.1450.0
Xamarin.Android 5.1.6.7
Someone can help me to understand if I'm doing something wrong?
Thanks for reading and for any help!
>>EDIT 1<<
I've changed my layout by adding stackFromBottom and transcriptMode properties and by removing the scrolling to below programmatically in Fragment obtaining an auto-scroll behavior, but the problem still remains: to see messages with correct style I have to manually scroll up and down (to activate the custom bindings)
Here is the new axml...
<Mvx.MvxListView
android:id="#+id/messagesList"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stackFromBottom="true"
android:transcriptMode="alwaysScroll"
local:MvxBind="ItemsSource MyMessages"
local:MvxItemTemplate="#layout/mymatcherschatview_itemtemplate" />
...and the new code in Fragment
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var ignore = base.OnCreateView(inflater, container, savedInstanceState);
var result = this.BindingInflate(Resource.Layout.MyMatchersChatView, null);
var headerFrame = result.FindViewById<FrameLayout>(Resource.Id.headerFrameMyMatchersChatView);
var headerWidget = new HeaderWidget() { ViewModel = this.ViewModel };
var tran = ChildFragmentManager.BeginTransaction();
tran.Add(headerFrame.Id, headerWidget, "headerMyMatchersChat");
tran.Commit();
return result;
}
First thing I would do is to make sure that your custom binding is always getting called.
Set a breakpoint on the SetValueImpl() method and check it´s getting called on those problematic items. If that happens, then the issue relies on the view no getting updated for any reason and you should work on that. If it doesn´t break, you will know for sure it´s a custom binding problem (possibly a bug) in MvxAdapter.
If you find out it´s the second one. I would suggest getting rid of your custom binding and creating your own ChatListAdapter : MvxAdapter as follows:
public class CoolChatListAdapter : MvxAdapter
{
public CoolChatListAdapter(Context context, IMvxAndroidBindingContext bindingContext) : base(context, bindingContext)
{
}
protected override View GetBindableView(View convertView, object source, int templateId)
{
var item = source as MyMatchMessageModel;
var weight = item.IsCurrentUser ? (float) 20.0 : (float) 5.0;
var ll = (LinearLayout) convertView;
var layoutParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WrapContent, weight);
ll.LayoutParameters = layoutParams;
return base.GetBindableView(convertView, source, templateId);
}
}
Then, in your android view:
var adapter = new ChatListAdapter(this, (IMvxAndroidBindingContext)BindingContext);
_chatList = FindViewById<MvxListView>(Resource.Id.chat_list_view);
_chatList.Adapter = adapter;

mvvmcross: NavigationService.Navigate throws an MvxException "Unable to find incoming mvxviewmodelrequest"

In my WP8 app, I have MainView referencing MainViewModel. MainView is a menu where users can navigate to other views to do some tasks. Navigating from MainView works perfectly as I use ShowViewModel. However, navigating from other views when user completes a task, back to MainView using NavigationService.Navigate(URI) throws an exception "Unable to find incoming mvxviewmodelrequest".
To avoid this exception, I have construct the URI like below
var req = "{\"ViewModelType\":\"MyApp.Core.ViewModels.MainViewModel, MyApp.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\",\"ClearTop\":\"true\",\"ParameterValues\":null,\"RequestedBy\":null}";
NavigationService.Navigate(new Uri("/MainView.xaml?ApplicationUrl=" + Uri.EscapeDataString(req), UriKind.Relative));
Does anyone have a better way to use NavigationService.Navigate?
Most navigations in the MvvmCross samples are initiated by either MvxAppStart objects or by MvxViewModels. Both of these classes inherit from MvxNavigatingObject and use the ShowViewModel methods exposed there - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross/ViewModels/MvxNavigatingObject.cs
From MvxNavigatingObject, you can see that MvvmCross routes the navigation call to the IMvxViewDispatcher which in WindowsPhone is a very thin object - all it does is marshall all calls to the UI thread and to pass them on to the IMvxViewPresenter - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/MvxPhoneViewDispatcher.cs
The presenter is an object created in Setup - and the default implementation uses an IMvxPhoneViewModelRequestTranslator to convert the navigation call into a uri-based navigation - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/MvxPhoneViewPresenter.cs
Silverlight/WindowsPhone then uses this uri for navigation, creates the necessary Xaml page, and then calls OnNavigatedTo on this page. As part of the base.OnNavigatedTo(); handing in MvxPhonePage, MvvmCross then calls the OnViewCreated extension method. This method checks if there is already a ViewModel - if there isn't one then it attempts to locate one using the information in the uri - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/MvxPhoneExtensionMethods.cs
With this explanation in mind, if any app ever wants to initiate an MvvmCross navigation from a class which doesn't already inherit from MvxNavigatingObject - e.g. from some Service or from some other class, then there are several options:
You can provide a shim object to do the navigation - e.g.:
public class MyNavigator : MvxNavigatingObject {
public void DoIt() {
ShowViewModel<MyViewModel>();
}
}
// used as:
var m = new MyNavigator();
m.DoIt();
You can instead use IoC to locate the IMvxViewDispatcher or IMvxViewPresenter and can call their Show methods directly
var request = MvxViewModelRequest<MyViewModel>.GetDefaultRequest();
var presenter = Mvx.Resolve<IMvxViewPresenter>();
presenter.Show(request);
You can write manual code which mimics what the IMvxViewPresenter does - exactly as you have in your code - although it might be "safer" to use the IMvxPhoneViewModelRequestTranslator.cs to assist with generate the url - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/IMvxPhoneViewModelRequestTranslator.cs
var request = MvxViewModelRequest<MyViewModel>.GetDefaultRequest();
var translator = Mvx.Resolve<IMvxPhoneViewModelRequestTranslator>();
var uri = translator.GetXamlUriFor(request);
One other option that Views always have is that they don't have to use the standard MvvmCross navigation and ViewModel location. In WindowsPhone, your code can easily set the ViewModel directly using your own logic like:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (ViewModel == null) {
ViewModel = // something I locate
}
// if you are doing your own logic then `base.OnNavigatedTo` isn't really needed in winphone
// but I always call it anyway
base.OnNavigatedTo(e);
}
Alternatively in WindowsPhone, you can even replace MvxPhonePage with a different base class that uses it's own logic for viewmodel location. This is easy to do in WindowsPhone as all Xaml pages have built-in data-binding support.

How to do CreateBindingSet() on Windows Phone?

In the N+1 video #34 (Progress), there was an example of using CreateBindingSet() for the Android version, which is not typical. But the narrator also mentioned briefly that the same can be done on the Windows platform.
As much as I tried, however, I am unable to get a View's property to be bound to its ModelView on the Windows Phone. I always get a NullReferenceException.
The closest I came was the code below, including suggestions from ReSharper. Here's my FirstView.xaml.cs:
using Cirrious.MvvmCross.Binding.BindingContext;
using Whatever.ViewModels;
namespace Whatever {
// inheriting from IMvxBindingContextOwner was suggested by ReSharper also
public partial class FirstView : BaseView, IMvxBindingContextOwner {
public class MyBindableMediaElement
{
private string _theMediaSource = "whatever";
public string TheMediaSource
{
get
{
return _theMediaSource;
}
set
{
_theMediaSource = value;
}
}
}
public FirstView()
{
InitializeComponent();
_mediaElement = new MyBindableMediaElement(this.theMediaElement);
var set = this.CreateBindingSet<FirstView, FirstViewModel>();
// the corresponding view model has a .SongToPlay property with get/set defined
set.Bind(_mediaElement).For(v => v.TheMediaSource).To(vm => vm.SongToPlay);
set.Apply();
}
public IMvxBindingContext BindingContext { get; set; } // this was suggested by ReSharper
}
I get a NullReferenceException in MvxBaseFluentBindingDescription.cs as soon as the view is created. The exact location is below:
protected static string TargetPropertyName(Expression<Func<TTarget, object>> targetPropertyPath)
{
var parser = MvxBindingSingletonCache.Instance.PropertyExpressionParser; // <----- exception here**
var targetPropertyName = parser.Parse(targetPropertyPath).Print();
return targetPropertyName;
}
I have not seen a working example of creating a binding set on a Windows Phone emulator. Has anyone gotten this to work? Thanks.
I can confirm that the narrator said that remark a little too flippantly without actually thinking about how he might do it...
However, with a little effort, you definitely can get the CreateBindingSet to work in Windows if you want to.
Before you start, do consider some alternatives - in particular, I suspect most people will use either Windows DependencyProperty binding or some hand-crafted code-behind with a PropertyChanged event subscription.
If you do want to add CreateBindingSet code to a Windows project then:
Add the Binding and BindingEx assemblies to your Ui project - the easiest way to do this is using nuget to add the BindingEx package.
In your Setup class, override InitializeLastChance and use this opportunity to create a MvxWindowsBindingBuilder instance and to call DoRegistration on that builder. Both these first two steps are covered in the n=35 Tibet binding video - and it's this second step that will initialise the binding framework and help you get past your current 'NullReferenceException' (for the code, see BindMe.Store/Setup.cs)
In your view, you'll need to implement the IMvxBindingContextOwner interface and you'll need to ensure the binding context gets created. You should be able to do this as simply as BindingContext = new MvxBindingContext();
In your view, you'll need to make sure the binding context is given the same DataContext (view model) as the windows DataContext. For a Phone Page, the easiest way to do this is probably just to add BindingContext.DataContext = this.ViewModel; to the end of your phone page's OnNavigatedTo method. Both steps 3 and 4 could go in your BaseView if you intend to use Mvx Binding in other classes too.
With this done, you should be able to use the CreateBindingSet code - although do make sure that all binding is done after the new MvxBindingContext() has been created.
I've not got a windows machine with me right now so I'm afraid this answer code comes untested - please do post again if it does or doesn't work.
I can confirm it works almost perfectly; the only problem is, there are no defaults register, so one has to do the full binding like:
set.Bind(PageText).For(c => c.Text).To(vm => vm.Contents.PageText).OneTime();
to fix this, instead of registering MvxWindowsBindingBuilder, I am registering the following class. Note: I have just created this class, and needs testing.
public class UpdatedMvxWindowsBindingBuilder : MvxWindowsBindingBuilder
{
protected override void FillDefaultBindingNames(IMvxBindingNameRegistry registry)
{
base.FillDefaultBindingNames(registry);
registry.AddOrOverwrite(typeof(Button), "Command");
registry.AddOrOverwrite(typeof(HyperlinkButton), "Command");
//registry.AddOrOverwrite(typeof(UIBarButtonItem), "Clicked");
//registry.AddOrOverwrite(typeof(UISearchBar), "Text");
//registry.AddOrOverwrite(typeof(UITextField), "Text");
registry.AddOrOverwrite(typeof(TextBlock), "Text");
//registry.AddOrOverwrite(typeof(UILabel), "Text");
//registry.AddOrOverwrite(typeof(MvxCollectionViewSource), "ItemsSource");
//registry.AddOrOverwrite(typeof(MvxTableViewSource), "ItemsSource");
//registry.AddOrOverwrite(typeof(MvxImageView), "ImageUrl");
//registry.AddOrOverwrite(typeof(UIImageView), "Image");
//registry.AddOrOverwrite(typeof(UIDatePicker), "Date");
//registry.AddOrOverwrite(typeof(UISlider), "Value");
//registry.AddOrOverwrite(typeof(UISwitch), "On");
//registry.AddOrOverwrite(typeof(UIProgressView), "Progress");
//registry.AddOrOverwrite(typeof(IMvxImageHelper<UIImage>), "ImageUrl");
//registry.AddOrOverwrite(typeof(MvxImageViewLoader), "ImageUrl");
//if (_fillBindingNamesAction != null)
// _fillBindingNamesAction(registry);
}
}
This is a skeleton from Touch binding, and so far I have only updated three controls to test out (Button, HyperButton and TextBlock)

Remove ViewController from stack

In our App we have a log-in ViewController A. On user log-in, a request navigate is automatically called to navigate to the next ViewController B. However when this is done we want to remove the log-in ViewController A from the stack so the user cannot "go back" to the log-in view but goes back the previous ViewController before the log-in instead.
We thought about removing the ViewController A from the stack when ViewController B is loaded, but is there a better way?
In the Android version of the App we've set history=no (if I recall correctly) and then it works.
Is there an similar way to achieve this in MonoTouch and MvvmCross?
I ended up with removing the unwanted viewcontroller from the navigation controller. In ViewDidDisappear() of my login ViewController I did the following:
public override void ViewDidDisappear (bool animated)
{
if (this.NavigationController != null) {
var controllers = this.NavigationController.ViewControllers;
var newcontrollers = new UIViewController[controllers.Length - 1];
int index = 0;
foreach (var item in controllers) {
if (item != this) {
newcontrollers [index] = item;
index++;
}
}
this.NavigationController.ViewControllers = newcontrollers;
}
base.ViewDidDisappear(animated);
}
This way I way remove the unwanted ViewController when it is removed from the view. I am not fully convinced if it is the right way, but it is working rather good.
This is quite a common scenario... so much so that we've included two mechanisms inside MvvmCross to allow this....
a ClearTop parameter available in all ViewModel navigations.
a RequestRemoveBackStep() call in all ViewModels - although this is currently NOT IMPLEMENTED IN iOS - sorry.
If this isn't enough, then a third technique might be to use a custom presenter to help with your display logic.
To use : 1. a ClearTop parameter available in all ViewModel navigations.
To use this, simply include the ClearTop flag when navigating.
This is a boolean flag - so to use it just change:
this.RequestNavigate<ChildViewModel>(new {arg1 = val1});
to
this.RequestNavigate<ChildViewModel>(new {arg1 = val1}, true);
For a standard simple navigation controller presenter, this will end up calling ClearBackStack before your new view is shown:
public override void ClearBackStack()
{
if (_masterNavigationController == null)
return;
_masterNavigationController.PopToRootViewController (true);
_masterNavigationController = null;
}
from https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Touch/Views/Presenters/MvxTouchViewPresenter.cs
If you are not using a standard navigation controller - e.g. if you had a tabbed, modal, popup or split view display then you will need to implement your own presentation logic to handle this.
You can't: 2. RequestRemoveBackStep().
Sadly it proved a bit awkward to implement this at a generic level for iOS - so currently that method is:
public bool RequestRemoveBackStep()
{
#warning What to do with ios back stack?
// not supported on iOS really
return false;
}
from https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Touch/Views/MvxTouchViewDispatcher.cs
Sorry! I've raised a bug against this - https://github.com/slodge/MvvmCross/issues/80
3. You can always... Custom ideas
If you need to implement something custom for your iOS app, the best way is to do this through some sort of custom Presenter logic.
There are many ways you could do this.
One example is:
for any View or ViewModel which needs to clear the previous view, you could decorate the View or ViewModel with a [Special] attribute
in Show in your custom Presenter in your app, you could watch for that attribute and do the special behaviour at that time
public override void Show(MvxShowViewModelRequest request)
{
if (request.ViewModelType.GetCustomAttributes(typeof(SpecialAttribute), true).Any())
{
// do custom behaviour here - e.g. pop current view controller
}
base.Show(request);
}
Obviously other mechanisms might be available - it's just C# and UIKit code at this stage
I don't know about mvvm but you can simply Pop the viewcontroller (AC A) without animation and then push the new viewcontoller (AC B) with animation
From within AC A:
NavigationController.PopViewControllerAnimated(false);
NavigationController.PushViewController(new ACb(), true);

How can I give flash stage instances unique properties in Flash Professional to pass to AS3 script?

I've started building a rough game engine framework in Flash Professional and I'm curious how I can create objects in the Flash library that I'm able to drag onto the stage and assign properties that are accessible from AS3.
Example:
I want to create a switch object (e.g. a light switch), so that when the player interactes with it, it triggers something specific in code such as a light in the room turns on.
I understand that Flash has built in UI components that you can define properties within the Flash Professional environment (see image below), and I'm wondering if there's a way to create my own custom style components so that I can essentially have my level file open in flash (.fla) and then drag a switch component from my library, and type in some information such as what light it is controlling, and any other information I want.
(above is an example of the type of parameter control I'm looking for)
I've read a bit about extending the flash UIComponent class but I feel that that's not the right approach because it's overkill for what I want. All I want is to pass some basic parameters from a library stage instance into AS3. I do not want to pass data via the instance name because this seems very messy if I want to have more complex interaction.
Thanks!
I would create a "switch" movie clip and export it to actionscrip, same with a "light" movie clip. The in the main class .as file I would inset them into the stage, using addChild (clips) and then add a click listener to the "switch" movie clip to control the "light".
This can be easily done.
Component(s) are wrong approach in my opinion.
Firstly you would want to setup Actionscript linkage / label your Library item.
In Library Panel.
- Right Click on "yourMC" >> click "Properties".
- In Properties dialog Tick "Export for Action Script"
- Then Name your Class eg "yourMC_Class"
now MC is ready to be referenced in your code.
next you would want to Dynamically add your "yourMC" from library to stage.
which can be done like such.
// first reference library item
var yourMC_ref:yourMC_Class = new yourMC_Class();
// Then load dynamic mc item into var
var your_MC_OBJ = yourMC_ref;
// then add your MC to stage.
this.addChild(your_MC_OBJ);
your_MC_OBJ.x = 200;
your_MC_OBJ.y = 100;
in a nutshell that's how I add library items to stage.
Obviously thats the basic function / code.
In a project I would have all code in an external class, in which case you would just set vars as public vars
public var yourMC_ref:yourMC_Class = new yourMC_Class();
public var your_MC_OBJ = yourMC_ref;
and the last 3 lines of code into a public function
public function ADD_First_MC()
{
this.addChild(your_MC_OBJ);
your_MC_OBJ.x = 200;
your_MC_OBJ.y = 100;
}
Now 'your_MC_OBJ' can be used in more complex ways.
eg. to create a light switch there are many options depending on how you need to approch functionality.
eg. Apply a different MC library item to "your_MC_OBJ"
play specific frame within MCs.
However If it was me I would just use mouse function to switch light on or off using addChild removeChild.
eg.
public var LightON = 0;
public var yourMC_ref:yourMC_Class = new yourMC_Class();
public var your_MC_OBJ = yourMC_ref;
then create a public function that handles on / off events
public function LightON_OFF()
{
if(LightON == 1)
{
this.addChild(your_MC_OBJ);
your_MC_OBJ.x = 200;
your_MC_OBJ.y = 100;
}
if(LightON == 0)
{
this.removeChild(your_MC_OBJ);
}
}
Hope this helps.
So, for what you want, while it may not be the best way to do what you want, I understand it's your experience you are constructing.
Use components, yes...in the following way (the most simple one):
Create a Movie Clip
Right-click it in library
Click on "Component Definitions"
Add a property, set a name, a variable name (var test, for this matter) and a default value
Click OK
Open your movie clip
Open code for the first frame and declare the variable without an initial value (var test:String;)
Trace it's value ( trace( test ); )
Go back to the stage root
Drag and drop the item from library to stage
Test it (Cmd/Ctrl + Enter) (maybe it will print null, dunno why, it ignores the default value sometimes)
Select your component on stage
Open the properties panel (Windows > Properties)
Go to Component Parameters on this panel and change the property value
You should see the value traced on console
And, I think, like this you can use properties from components for what you want, like using a String and getting the controlled mc by its name.
Good luck
I think what people are trying to say is that you can have the whole thing is data driven, and so you can combine the IDE with the data to come up with your final game.
But consider this ... it might be what you want.
If you have, for instance, a BaseSwitch Class:
public Class BaseSwitch extends MovieClip {
private var _lightName:String;
private var _light:Light;
public function get lightName():String {
return lightName;
}
public function set lightName(value:String):void {
if (value != _lightName) {
_lightnName = value;
//Note I don't advocate having children reach into their parents like this,
//but you sound like you don't want the parent involved in the process, so
//this is one way you could do it.
if (parent.hasOwnProperty(lightName) && parent[lightName] is Light) {
_light = parent[lightName];
} else {
trace('Could not find light', _lightName);
}
}
}
//other code to listen for gestures and operate the light
}
Now, when you want a switch to operate a specific light name, create a library instance and set its base class to BaseSwitch. When you close the dialog where you set the base Class, you'll notice that it gives you a dialogue that it couldn't find the Class in the Class path and one will be generated. You're going to replace it with a Class that sets the lightName. Create a new AS3 Class in the root directory with the same name as your library instance. It should look something like this:
public class SpecificSwitch {
public function SpecificSwitch() {
super();
lightName = 'theSwitch';
}
}
Other possible choices involve having the parent Class match up instances of switch with instances of light based on name, so if it finds a light1 and a light1Switch, it either gives a reference to the light to the switch or it simply sets up a mapping in its own event listening system.