1

I have an MonoTouch project using MvvmCross. I am at the point where I could get my core project code compiling (with quite a bit of effort), and now I am creating the views for iOS. Running the latest stable releases of Xamarin tools, as well as MvvmCross. Also, I am running against iOS7 SDK with XCode 5 installed.

To start off I created a very basic view with a text field binding to my main view model. The relevant code in the view is as follows:

[Register("MainView")]
public partial class MainView : MvxViewController
{
    public override void ViewDidLoad()
    {
        View = new UIView { BackgroundColor = UIColor.White };

        base.ViewDidLoad();

        var uiTextField = new UITextField(new RectangleF(0, 100, 320, 100));
        Add(uiTextField);

        this.CreateBinding(uiTextField).To<MainViewModel>(vm => vm.IsDebug).Apply();

    }
}

The binding however throws a NullReference exception with the following stack trace:

System.NullReferenceException: Object reference not set to an instance of an object at Cirrious.MvvmCross.Binding.BindingContext.MvxBaseFluentBindingDescription1[MonoTouch.UIKit.UITextField].SourcePropertyPath[MainViewModel] (System.Linq.Expressions.Expression1 sourceProperty) [0x00000] in :0 at Cirrious.MvvmCross.Binding.BindingContext.MvxFluentBindingDescription1[MonoTouch.UIKit.UITextField].To[MainViewModel] (System.Linq.Expressions.Expression1 sourceProperty) [0x00000] in :0 at ProjectX.Views.MainView.ViewDidLoad () [0x000a4] in /Users/jerriepelser/Development/1degree Software/ProjectX/Source/ProjectX.MonoTouch/Views/MainView.cs:26 at at (wrapper managed-to-native) MonoTouch.ObjCRuntime.Messaging:void_objc_msgSend (intptr,intptr) at MonoTouch.UIKit.UIWindow.MakeKeyAndVisible () [0x00010] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIWindow.g.cs:129 at ProjectX.AppDelegate.FinishedLaunching (MonoTouch.UIKit.UIApplication app, MonoTouch.Foundation.NSDictionary options) [0x0003c] in /Users/jerriepelser/Development/1degree Software/ProjectX/Source/ProjectX.MonoTouch/AppDelegate.cs:27 at at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr) at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38 at ProjectX.Application.Main (System.String[] args) [0x00008] in /Users/jerriepelser/Development/1degree Software/ProjectX/Source/ProjectX.MonoTouch/Main.cs:16

If I set a breakpoint before the binding I can confirm that the ViewModel is set correctly on the base MvxViewController class, so it is not a problem with the ViewModel not being set.

I also tried the following way of doing the data binding:

var set = this.CreateBindingSet<MainView, MainViewModel> ();
set.Bind (uiTextField).To (vm => vm.IsDebug);
set.Apply ();

Still get a NullReference exception, but with the following stack trace:

System.NullReferenceException: Object reference not set to an instance of an object at Cirrious.MvvmCross.Binding.BindingContext.MvxBaseFluentBindingDescription1[MonoTouch.UIKit.UITextField].SourcePropertyPath[MainViewModel] (System.Linq.Expressions.Expression1 sourceProperty) [0x00000] in :0 at Cirrious.MvvmCross.Binding.BindingContext.MvxFluentBindingDescription2[MonoTouch.UIKit.UITextField,OneLove.Core.ViewModels.MainViewModel].To (System.Linq.Expressions.Expression1 sourceProperty) [0x00000] in :0 at OneLove.Views.MainView.ViewDidLoad () [0x000a6] in /Users/jerriepelser/Development/1degree Software/OneLove/Source/OneLove.MonoTouch/Views/MainView.cs:29 at at (wrapper managed-to-native) MonoTouch.ObjCRuntime.Messaging:void_objc_msgSend (intptr,intptr) at MonoTouch.UIKit.UIWindow.MakeKeyAndVisible () [0x00010] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIWindow.g.cs:129 at OneLove.AppDelegate.FinishedLaunching (MonoTouch.UIKit.UIApplication app, MonoTouch.Foundation.NSDictionary options) [0x0003c] in /Users/jerriepelser/Development/1degree Software/OneLove/Source/OneLove.MonoTouch/AppDelegate.cs:27 at at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr) at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38 at OneLove.Application.Main (System.String[] args) [0x00008] in /Users/jerriepelser/Development/1degree Software/OneLove/Source/OneLove.MonoTouch/Main.cs:16

Looking at the MvvmCross debug output gives me no hint as to what is wrong. Any ideas?

4

3 回答 3

3

好的,我解决了。我认为这个答案可能并不适用于所有情况,但我的情况是它解决了问题。

我在 Setup 类的 InitializeLastChance 方法中进行了一些特定于平台的 IOC 注册,但没有调用 base.InitializeLastChance()。这不会在 WinRT 或 Windows Phone 8 上造成问题,但在 MonoTouch 上会导致我遇到的错误。所以我对基类方法做了一个简单的调用,一切都解决了:)

protected override void InitializeLastChance()
{
    base.InitializeLastChance ();

    // Do some platform specific registration here...
}

如果那里的其他人遇到类似的事情,这可能是错误的可能原因。

编辑:只是一个快速的事后思考......查看基本 MvxTouchSetup 类的源代码,这实际上现在非常有意义:

protected override void InitializeLastChance()
{
    InitialiseBindingBuilder();
    base.InitializeLastChance();
}

请参阅对 InitialiseBindingBuilder() 的调用...

于 2013-09-24T04:29:49.807 回答
0

这应该是一个更好的答案:

  • 第 1 步:您在搞混:如果您创建自己的视图,请覆盖 LoadView 而不是 ViewDidLoad。并确保没有关联的 Xib。
  • 第二步:在loadview中调用ViewDidLoad,因为一个MvvmCross的缺点(它会在ViewDidLoad之后绑定ViewModel,但是ViewDidLoad只在一个Xib之后调用)。

我认为 MvvmCross 包含对原始视图的引用,而不是您在 ViewDidLoad 中定义的(后期创建的)视图。

在没有 Xib 的情况下使用 MvvmCross 可以正常工作(我不再创建任何 Xib),我真的推荐 XibFree(使用我的 fork 来扩展功能)。

于 2013-09-23T14:22:14.747 回答
0

我使用从代码生成的视图遇到了同样的问题。

Viewmodel 已设置,但 bindingcontext 未完全设置。这是我项目中的一些示例代码,可帮助您启动并运行您将遇到的其他问题:

public abstract class IndexBaseFragment<T> : MvxFragment where T : class, IMvxViewModel {
        public static int WRAP = ViewGroup.LayoutParams.WrapContent;
        public static int FILL = ViewGroup.LayoutParams.FillParent;

        public LinearLayout NewVerticalLinearLayout(Orientation orientation, Boolean fillparent) {
            var ll = new LinearLayout (Activity) { Orientation = orientation };
            ll.LayoutParameters = new ViewGroup.LayoutParams (FILL, fillparent ? FILL : WRAP);
            return ll;
        }

        public new T ViewModel { get { return base.ViewModel as T; } set { base.ViewModel = value; } } 

        // These are the available controls
        protected ListView list {get; set;}
        protected MvxListView ListAsMvxListView {get { return (MvxListView) list; }}
        protected TextView emptytext { get; set; }
        protected TextView title { get; set; }
        protected Button twobuttonklein { get; set; }
        protected Button twobuttongroot { get; set; }
        protected EditText zoekvak {get; set;}



        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            base.OnCreateView (inflater, container, savedInstanceState);

            var layout = NewVerticalLinearLayout (Orientation.Vertical, true);


            if (ViewModel == null)
                ViewModel = Mvx.IocConstruct<T> ();
            BindingContext = new MvxAndroidBindingContext (Activity, new MvxSimpleLayoutInflater(inflater), ViewModel);
            using (new MvxBindingContextStackRegistration<IMvxAndroidBindingContext>(((IMvxAndroidBindingContext) this.BindingContext)))
            {
                //var layout = NewVerticalLinearLayout (Orientation.Vertical, true);
                layout.SetBackgroundColor (Color.ParseColor("#FFFFFF"));
                var titlelayer = NewVerticalLinearLayout (Orientation.Horizontal, false);
                titlelayer.LayoutParameters.Height = 40;
                title = new TextView (Activity) { LayoutParameters = new ViewGroup.LayoutParams (FILL, FILL) };
                title.SetTextColor (Color.ParseColor ("#212121"));
                title.SetPadding(0, 10, 0, 0);
                title.TextSize = 18;
                var titlebutton = new Button (Activity) { LayoutParameters = new ViewGroup.LayoutParams (WRAP, FILL) };
                titlelayer.AddView (title);
                titlelayer.AddView (titlebutton);
                layout.AddView (titlelayer);

                var twobuttonlayer = NewVerticalLinearLayout (Orientation.Horizontal, false);
                twobuttonlayer.LayoutParameters.Height = 40;
                twobuttonklein = new Button (Activity) { LayoutParameters = new ViewGroup.LayoutParams (FILL, WRAP) };
                twobuttonklein.Text = "Klein";
                twobuttonklein.SetBackgroundResource (Resource.Drawable.kleingrootbutton);
                twobuttongroot = new Button(Activity){ LayoutParameters = new ViewGroup.LayoutParams (FILL, WRAP) };
                twobuttongroot.Text = "Middel/groot";
                twobuttongroot.SetBackgroundResource (Resource.Drawable.kleingrootbutton);
                twobuttonlayer.AddView (twobuttonklein);
                twobuttonlayer.AddView (twobuttongroot);
                layout.AddView (twobuttonlayer);

                zoekvak = new EditText (Activity) { LayoutParameters = new ViewGroup.LayoutParams (FILL, WRAP) };
                zoekvak.Hint = "Zoek in inhoudsopgave";
                layout.AddView (zoekvak);

                emptytext = new TextView (Activity) { LayoutParameters = new ViewGroup.LayoutParams (FILL, FILL),
                    Text = "Er zijn geen resultaten." };
                layout.AddView (emptytext);

                CreateList();

                layout.AddView (list);


            }




            //var view = this.BindingInflate(Resource.Layout.IndexTocView, null);
            Console.WriteLine ("LAYOUT RETURNED");

            return layout;
        }

        protected abstract void BindView();

        protected MvxAdapter listadapter { get; set; }


        protected virtual void CreateList () {
            // let op, deze code is nog voor de speciale situatie, en dat moet juist andersom.
            listadapter = new MvxAdapter(this.Activity, (IMvxAndroidBindingContext)BindingContext);

            list = new MvxListView(Activity, null, listadapter) { LayoutParameters = new ViewGroup.LayoutParams(FILL, FILL) };
            list.SetMinimumHeight(50);

        }

        public override void OnResume ()
        {
            base.OnResume ();
            BindView ();
        }




    }
于 2013-09-23T14:14:15.557 回答