Added PickerElement #25

Closed
wants to merge 2 commits into
from

Projects

None yet

2 participants

@Cheesebaron
Member

Hey Stuart,

Here is my PickerElement implementation, check it out. Not sure what to do in the Reflect.cs file, or if I even have to do anything in there.

@slodge
Contributor
slodge commented Sep 3, 2012

Thanks :)

I think Reflect.cs could have been OK left alone - dialog's not really used
the reflection way in mvx currently

Thanks again - will pull and build later (hopefully tonight)

Stuart

On 3 September 2012 13:08, Tomasz Cielecki notifications@github.com wrote:

Hey Stuart,

Here is my PickerElement implementation, check it out. Not sure what to do

in the Reflect.cs file, or if I even have to do anything in there.

You can merge this Pull Request by running:

git pull https://github.com/Cheesebaron/MvvmCross master

Or view, comment on, or merge it at:

#25
Commit Summary

  • Added PickerElement implementation.
  • Merge branch 'master' of git://github.com/slodge/MvvmCross.git

File Changes

  • _M_Cirrious/Cirrious.MvvmCross.Dialog/Cirrious.MvvmCross.Dialog.Touch.csproj
    (1)
  • _A_Cirrious/Cirrious.MvvmCross.Dialog/Dialog/Elements/PickerElement.cs (233)
  • M Cirrious/Cirrious.MvvmCross.Dialog/Dialog/Reflect.cs (2)
  • M Cirrious/Cirrious.MvvmCross.Dialog/MvxTouchDialogBindingSetup.cs
    (2)

Patch Links

@slodge
Contributor
slodge commented Sep 9, 2012

Hi CheeseBaron

It's been a much longer week at work than I hoped :/

But finally getting some time to build and run code today :)

Do you have a sample anywhere for this PickerElement - especially one including a ViewModel

I just had a play with a simpler one column picker element using the code below. I think this approach should enable the same ViewModel to be used in WP7, Droid and Touch - a bit like http://stackoverflow.com/questions/12160239/mvvmcross-binding-lists-in-monotouch - but doesn't have multiple components in the picker.

//
// SimplePickerElement.cs: Defines UIPickerView element
// Derived from PickerElement by Tomasz Cielecki (tomasz@ostebaronen.dk)
// In turn, based on DateTimeElement by Miguel de Icaza
//
// Author:
//
// Original code reused here from MIT X11 license
// But adapted and supplied here under Ms-PL
//

using System.Collections;
using System.Drawing;
using System.Globalization;
using Cirrious.MvvmCross.Converters;
using Cirrious.MvvmCross.Interfaces.Converters;
using MonoTouch.Foundation;
using MonoTouch.UIKit;

namespace Cirrious.MvvmCross.Dialog.Touch.Dialog.Elements
{
    public class SimplePickerElement : ValueElement<object>
    {
        private static readonly NSString Key = new NSString("SimplePickerElement");

        private UIPickerView _picker;

        public IList Entries { get; set; }

        public IMvxValueConverter DisplayValueConverter { get; set; }

        private class ToStringDisplayValueConverter : MvxBaseValueConverter
        {
            public override object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (value == null)
                    return string.Empty;

                return value.ToString();
            }
        }

        public SimplePickerElement(string caption, object value, IMvxValueConverter displayValueConverter = null)
            : base(caption, value)
        {
            DisplayValueConverter = displayValueConverter ?? new ToStringDisplayValueConverter();
        }

        protected override UITableViewCell GetCellImpl(UITableView tv)
        {
            var cell = tv.DequeueReusableCell(Key) ?? new UITableViewCell(UITableViewCellStyle.Value1, Key) { Accessory = UITableViewCellAccessory.DisclosureIndicator };

            UpdateDetailDisplay(cell);
            return cell;
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            if (disposing)
            {
                if (_picker != null)
                {
                    _picker.Model.Dispose();
                    _picker.Model = null;
                    _picker.Dispose();
                    _picker = null;
                }
            }
        }

        public virtual UIPickerView CreatePicker()
        {
            var picker = new UIPickerView(RectangleF.Empty)
            {
                AutoresizingMask = UIViewAutoresizing.FlexibleWidth,
                Model = new SimplePickerViewModel(this),
                ShowSelectionIndicator = true,
            };
            return picker;
        }

        static RectangleF PickerFrameWithSize(SizeF size)
        {
            var screenRect = UIScreen.MainScreen.ApplicationFrame;
            float fY = 0, fX = 0;

            switch (UIApplication.SharedApplication.StatusBarOrientation)
            {
                case UIInterfaceOrientation.LandscapeLeft:
                case UIInterfaceOrientation.LandscapeRight:
                    fX = (screenRect.Height - size.Width) / 2;
                    fY = (screenRect.Width - size.Height) / 2 - 17;
                    break;

                case UIInterfaceOrientation.Portrait:
                case UIInterfaceOrientation.PortraitUpsideDown:
                    fX = (screenRect.Width - size.Width) / 2;
                    fY = (screenRect.Height - size.Height) / 2 - 25;
                    break;
            }

            return new RectangleF(fX, fY, size.Width, size.Height);
        }

        private string GetString(int row)
        {
            if (Entries == null)
                return string.Empty;

            if (row >= Entries.Count)
                return string.Empty;

            var whichObject = Entries[row];
            return ConvertToString(whichObject);
        }

        private string ConvertToString(object whichObject)
        {
            return DisplayValueConverter.Convert(whichObject, typeof(string), null, CultureInfo.CurrentUICulture).ToString();
        }

        class SimplePickerViewModel : UIPickerViewModel
        {
            private readonly SimplePickerElement _owner;

            public SimplePickerViewModel(SimplePickerElement owner)
            {
                _owner = owner;
            }

            public override int GetComponentCount(UIPickerView picker)
            {
                return 1;
            }

            public override int GetRowsInComponent(UIPickerView picker, int component)
            {
                if (_owner.Entries == null)
                    return 0;

                return _owner.Entries.Count;
            }

            public override string GetTitle(UIPickerView picker, int row, int component)
            {
                return _owner.GetString(row) ?? string.Empty;
            }

            public override float GetComponentWidth(UIPickerView picker, int component)
            {
                // TODO - need to get this better (currently just using a fixed value like in http://weblogs.asp.net/wallym/archive/2010/01/07/uipicker-in-the-iphone-with-monotouch.aspx)
                return 300.0f;
            }

            public override float GetRowHeight(UIPickerView picker, int component)
            {
                // TODO - need to get this better (currently just using a fixed value like in http://weblogs.asp.net/wallym/archive/2010/01/07/uipicker-in-the-iphone-with-monotouch.aspx)
                return 40.0f;
            }

            public override void Selected(UIPickerView picker, int row, int component)
            {
                // TODO - update the value here...
                _owner.OnUserValueChanged(_owner.Entries[row]);
            }
        }

        class SimplePickerViewController : UIViewController
        {
            readonly SimplePickerElement _container;

            public SimplePickerViewController(SimplePickerElement container)
            {
                _container = container;
            }

            public override void DidRotate(UIInterfaceOrientation fromInterfaceOrientation)
            {
                base.DidRotate(fromInterfaceOrientation);
                _container._picker.Frame = PickerFrameWithSize(_container._picker.SizeThatFits(SizeF.Empty));
            }

            public bool Autorotate { get; set; }

            public override bool ShouldAutorotateToInterfaceOrientation(UIInterfaceOrientation toInterfaceOrientation)
            {
                return Autorotate;
            }
        }

        public override void Selected(DialogViewController dvc, UITableView tableView, NSIndexPath path)
        {
            var vc = new SimplePickerViewController(this)
            {
                Autorotate = dvc.Autorotate
            };
            _picker = CreatePicker();
            _picker.Frame = PickerFrameWithSize(_picker.SizeThatFits(SizeF.Empty));

            if (Entries != null)
            {
                var index = Entries.IndexOf(Value);
                if (index >= 0)
                {
                    _picker.Select(index, 0, true);
                }
            }

            vc.View.BackgroundColor = UIColor.Black;
            vc.View.AddSubview(_picker);
            dvc.ActivateController(vc);
        }

        protected override void UpdateDetailDisplay(UITableViewCell cell)
        {
            if (cell == null)
            {
                return;
            }

            if (cell.DetailTextLabel != null)
            {
                cell.DetailTextLabel.Text =  ConvertToString(Value);
            }
        }
    }
}

Still looking at this - but interested to look at this more - wondering if I can dig out some more advanced picker examples or if I can switch to a subtable-type approach...

Stuart

@Cheesebaron
Member

I was doing something quite hack-ish to update the ViewModel, as I did not want to have UI code in my model. So I kind of made a Property that reflected the values of the original Property, which I use on Android and WP7, so that the PickerElement will understand it. By this I mean that I made a Property of the type IList<IPickerComponent>, which took the values from the original Property and represented that. But I actually do not need to have multiple Components, in my case I just need to present some data in one Component with multiple rows.

Though I tried to make it so that it supported multiple Components, hence I made the two interfaces IPickerComponent and IPickerComponentRow. This way it was easier to know that the only kind of data the PickerElement accepts are derivatives of the IPickerComponent.

I was thinking of refining the PickerElement, with a default converter which magically could take any type of data and represent it in the PickerElement, but I have not come to do that yet. The SimplePickerElement would work for me, but it would be so neat if one could make a general implementation, which worked for all sorts of data.

@slodge slodge added a commit that referenced this pull request Nov 19, 2012
@slodge slodge PickerElement added - it requires Mvx so not in the external library!…
… Partly addresses #25
0d2f8ba
@slodge
Contributor
slodge commented Nov 22, 2012

This simple PickerElement is in vNext now - https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Dialog.Touch/Elements/PickerElement.cs

I'm not going to pull the more complex IPickerComponent PickerElement - I don't want to include it in the core library at present because I feel it requires too much knowledge/explanation in the ViewModel.

At some point, I'll try to find time to do some documentation and/or blogging about this - when this happens (when i find time) I will try to include this more complex example - I think it's exactly the sort of thing that people building really slick/full apps will want to do do.

Thanks for the comments - sorry I'm not pulling right now!

Stuart

@slodge slodge closed this Nov 22, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment