示例#1
0
        private RootElement RenderEditItem(Item item, bool renderListInfo)
        {
            // get itemType for this item
            ItemType itemType = null;
            try
            {
                itemType = App.ViewModel.ItemTypes.Single(it => it.ID == item.ItemTypeID);
            }
            catch (Exception)
            {
                // if can't find the folder type, use the first
                itemType = App.ViewModel.ItemTypes[0];
            }

            // render the primary fields
            Section primarySection = RenderEditItemFields(item, itemType, true);

            // HACK: insert a dummy element inside the primary section.  This dummy element
            // implements IElementSizing and returns a 0-sized cell (so it is invisible).
            // This is to work around an issue in MT.Dialog where it doesn't honor IElementSizing
            // on elements that are added after a dialog is already drawn.  This element must be
            // inserted after the first element but before the last element, to avoid losing the
            // rounded rectangle look of the first and last elements of a Section.
            if (primarySection.Count > 0)
                primarySection.Insert(1, new DummyElement());

            // render more button
            var moreButton = new Button() { Background = "Images/darkgreybutton.png", Caption = "more details" };
            var sse = new ButtonListElement() { Margin = 0f };
            sse.Buttons.Add(moreButton);
            var moreSection = new Section() { sse };

            // render save/delete buttons
            var actionButtons = new ButtonListElement()
            {
                //new Button() { Caption = "Save", Background = "Images/greenbutton.png", Clicked = SaveButton_Click },
                new Button() { Caption = "Delete", Background = "Images/redbutton.png", Clicked = DeleteButton_Click },
            };
            actionButtons.Margin = 0f;

            // create the dialog with the primary section
            RootElement editRoot = new RootElement(item.Name)
            {
                primarySection,
                moreSection,
                new Section() { actionButtons },
            };

            moreButton.Clicked += (s, e) =>
            {
                // remove the "more" button
                editRoot.Remove(moreSection);

                // render the non-primary fields as a new section
                editRoot.Insert(1, RenderEditItemFields(item, itemType, false));

                // create a separate section with the advanced information (parent, type)
                var advancedSection = new Section();

                Field field = new Field() { Name = "ParentID", DisplayName = "List", DisplayType = DisplayTypes.Lists };
                advancedSection.Add(RenderEditItemField(item, field));

                field = new Field() { Name = "ItemTypeID", DisplayName = "Type", DisplayType = DisplayTypes.ItemTypes };
                advancedSection.Add(RenderEditItemField(item, field));

                editRoot.Insert(2, advancedSection);
            };

            return editRoot;
        }
示例#2
0
        private Element RenderEditItemField(Item item, Field field)
        {
            PropertyInfo pi = null;
            object currentValue = null;
            object container = null;

            // get the current field value.
            // the value can either be in a strongly-typed property on the item (e.g. Name),
            // or in one of the FieldValues
            try
            {
                // get the strongly typed property
                pi = item.GetType().GetProperty(field.Name);
                if (pi != null)
                {
                    // store current item's value for this field
                    currentValue = pi.GetValue(item, null);

                    // set the container - this will be the object that will be passed
                    // to pi.SetValue() below to poke new values into
                    container = item;
                }
            }
            catch (Exception)
            {
                // an exception indicates this isn't a strongly typed property on the Item
                // this is NOT an error condition
            }

            // if couldn't find a strongly typed property, this property is stored as a
            // FieldValue on the item
            if (pi == null)
            {
                // get current item's value for this field, or create a new FieldValue
                // if one doesn't already exist
                FieldValue fieldValue = item.GetFieldValue(field.ID, true);
                currentValue = fieldValue.Value;

                // get the value property of the current fieldvalue (this should never fail)
                pi = fieldValue.GetType().GetProperty("Value");
                if (pi == null)
                    return null;

                // set the container - this will be the object that will be passed
                // to pi.SetValue() below to poke new values into
                container = fieldValue;
            }

            // most elements will be Entry Elements - default to this
            EntryElement entryElement = new EntryElement(field.DisplayName, "", "");
            Element element = entryElement;

            bool notMatched = false;
            // render the right control based on the type
            switch (field.DisplayType)
            {
                case DisplayTypes.Text:
                    //StyledMultilineElement stringElement = new StyledMultilineElement(field.DisplayName, (string) currentValue);
                    entryElement.KeyboardType = UIKeyboardType.Default;
                    entryElement.Value = (string) currentValue;
                    entryElement.AutocorrectionType = UITextAutocorrectionType.Yes;
                    entryElement.Changed += delegate {
                        pi.SetValue(container, entryElement.Value, null); };
                    //element = stringElement;
                    break;
                case DisplayTypes.TextArea:
                    MultilineEntryElement multilineElement = new MultilineEntryElement(field.DisplayName, (string) currentValue) { Lines = 3 };
                    multilineElement.Changed += delegate { pi.SetValue(container, multilineElement.Value, null); };
                    //var multilineElement = new MultilineEntryElement3(field.DisplayName, (string) currentValue) { Editable = true };
                    //multilineElement.Changed += delegate { pi.SetValue(container, multilineElement.Value, null); };
                    element = multilineElement;
                    //entryElement.Value = (string) currentValue;
                    //entryElement.Changed += delegate { pi.SetValue(container, entryElement.Value, null); };
                    break;
                case DisplayTypes.Phone:
                    entryElement.Value = (string) currentValue;
                    entryElement.KeyboardType = UIKeyboardType.PhonePad;
                    entryElement.Changed += delegate { pi.SetValue(container, entryElement.Value, null); };
                    break;
                case DisplayTypes.Link:
                    entryElement.Value = (string) currentValue;
                    entryElement.KeyboardType = UIKeyboardType.Url;
                    entryElement.Changed += delegate { pi.SetValue(container, entryElement.Value, null); };
                    break;
                case DisplayTypes.Email:
                    entryElement.Value = (string) currentValue;
                    entryElement.KeyboardType = UIKeyboardType.EmailAddress;
                    entryElement.Changed += delegate { pi.SetValue(container, entryElement.Value, null); };
                    break;
                case DisplayTypes.Address:
                    entryElement.Value = (string) currentValue;
                    entryElement.AutocorrectionType = UITextAutocorrectionType.Yes;
                    entryElement.Changed += delegate { pi.SetValue(container, entryElement.Value, null); };
                    break;
                case DisplayTypes.Priority:
                    var priorities = new RadioGroup(field.DisplayName, 0);
                    priorities.Selected =
                        ((int?) currentValue) != null ?
                        (int) currentValue :
                        1;  // HACK: hardcode to "Normal" priority.  this should come from a table.
                    var priSection = new Section();
                    priSection.AddAll(
                        from pr in App.ViewModel.Constants.Priorities
                             select (Element) new RadioEventElement(pr.Name, field.DisplayName));
                    var priorityElement = new ThemedRootElement(field.DisplayName, priorities) { priSection };
                    // augment the radio elements with the right index and event handler
                    int i = 0;
                    foreach (var radio in priorityElement[0].Elements)
                    {
                        RadioEventElement radioEventElement = (RadioEventElement) radio;
                        int index = i++;
                        radioEventElement.OnSelected += delegate { pi.SetValue(container, index, null); };
                    }
                    element = priorityElement;
                    break;
                case DisplayTypes.Lists:
                    // create a collection of lists in this folder, and add the folder as the first entry
                    var lists = App.ViewModel.Items.
                        Where(li => li.FolderID == item.FolderID && li.IsList == true && li.ItemTypeID != SystemItemTypes.Reference).
                            OrderBy(li => li.Name).
                            ToObservableCollection();
                    lists.Insert(0, new Item()
                    {
                        ID = Guid.Empty,
                        Name = folder.Name
                    });
                    // a null value for the "list" field indicates a Folder as a parent (i.e. this item is a top-level item)
                    if (currentValue == null)
                        currentValue = Guid.Empty;
                    Item currentList = lists.FirstOrDefault(li => li.ID == (Guid) currentValue);
                    var listsGroup = new RadioGroup (field.DisplayName, 0);
                    listsGroup.Selected = Math.Max(lists.IndexOf(currentList), 0);
                    var listsSection = new Section();
                    listsSection.AddAll(
                        from l in lists
                            select (Element) new RadioEventElement(l.Name, field.DisplayName));
                    var listsElement = new ThemedRootElement(field.DisplayName, listsGroup) { listsSection };
                    // augment the radio elements with the right index and event handler
                    int index = 0;
                    foreach (var radio in listsElement[0].Elements)
                    {
                        int currentIndex = index;  // make a local copy for the closure
                        RadioEventElement radioEventElement = (RadioEventElement) radio;
                        radioEventElement.OnSelected += delegate(object sender, EventArgs e)
                        {
                            pi.SetValue(container, lists[currentIndex].ID, null);
                        };
                        index++;
                    }
                    element = listsElement;
                    break;
                case DisplayTypes.DatePicker:
                    DateTime? dateTime = String.IsNullOrEmpty((string) currentValue) ? (DateTime?) null : Convert.ToDateTime((string) currentValue);
                    DateEventElement dateElement = new DateEventElement(field.DisplayName, dateTime);
                    dateElement.ValueSelected += delegate
                    {
                        pi.SetValue(container, ((DateTime)dateElement.DateValue).ToString("yyyy/MM/dd"), null);
                        folder.NotifyPropertyChanged("FirstDue");
                        folder.NotifyPropertyChanged("FirstDueColor");
                    };
                    element = dateElement;
                    break;
                case DisplayTypes.DateTimePicker:
                    DateTime? dt = String.IsNullOrEmpty((string) currentValue) ? (DateTime?) null : Convert.ToDateTime((string) currentValue);
                    DateTimeEventElement dateTimeElement = new DateTimeEventElement(field.DisplayName, dt);
                    dateTimeElement.ValueSelected += (s, e) =>
                    {
                        pi.SetValue(container, dateTimeElement.DateValue == null ? null : ((DateTime) dateTimeElement.DateValue).ToString(), null);
                        folder.NotifyPropertyChanged("FirstDue");
                        folder.NotifyPropertyChanged("FirstDueColor");
                    };
                    element = dateTimeElement;
                    break;
                case DisplayTypes.Checkbox:
                    CheckboxElement checkboxElement = new CheckboxElement(field.DisplayName, currentValue == null ? false : (bool) currentValue);
                    checkboxElement.Tapped += delegate { pi.SetValue(container, checkboxElement.Value, null); };
                    element = checkboxElement;
                    break;
                case DisplayTypes.TagList:
                    // TODO
                    element = null;
                    break;
                case DisplayTypes.ImageUrl:
                    // TODO: wire up to picture picker, and upload to an image service
                    element = null;
                    break;
                case DisplayTypes.LinkArray:
                    var linkArrayElement = new MultilineEntryElement(field.DisplayName, (string) currentValue) { Lines = 3, AcceptReturns = true };
                    if (!String.IsNullOrEmpty((string) currentValue))
                    {
                        try
                        {
                            var linkList = JsonConvert.DeserializeObject<List<Link>>((string)currentValue);
                            linkArrayElement.Value = String.Concat(linkList.Select(l => l.Name != null ? l.Name + "," + l.Url + "\n" : l.Url + "\n").ToList());
                        }
                        catch (Exception)
                        {
                        }
                    }
                    linkArrayElement.Changed += delegate
                    {
                        // the expected format is a newline-delimited list of Name, Url pairs
                        var linkArray = linkArrayElement.Value.Split(new char[] { '\r','\n' }, StringSplitOptions.RemoveEmptyEntries);
                        var linkList = new List<Link>();
                        foreach (var link in linkArray)
                        {
                            var nameval = link.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                            if (nameval.Length == 0)
                                continue;
                            if (nameval.Length == 1)
                                linkList.Add(new Link() { Url = nameval[0].Trim() });
                            else
                                linkList.Add(new Link() { Name = nameval[0].Trim(), Url = nameval[1].Trim() });
                        }
                        var json = JsonConvert.SerializeObject(linkList);
                        pi.SetValue(container, json, null);
                    };
                    element = linkArrayElement;
                    break;
                case DisplayTypes.Hidden:
                    // skip rendering
                    element = null;
                    break;
                case DisplayTypes.ContactList:
                    StringElement contactsElement = new StringElement(field.DisplayName);
                    Item currentContacts = CreateValueList(item, field, currentValue == null ? Guid.Empty : new Guid((string) currentValue));
                    contactsElement.Value = CreateCommaDelimitedList(currentContacts);
                    Item contacts = new Item()
                    {
                        Items = App.ViewModel.Items.
                            Where(it => it.ItemTypeID == SystemItemTypes.Contact && it.IsList == false).
                            Select(it => new Item() { Name = it.Name, FolderID = folder.ID, ItemTypeID = SystemItemTypes.Reference, ParentID = currentContacts.ID, ItemRef = it.ID }).
                            ToObservableCollection(),
                    };
                    contactsElement.Tapped += delegate
                    {
                        // put up the list picker dialog
                        ListPickerPage listPicker = new ListPickerPage(
                            SystemItemTypes.Contact,
                            editViewController.NavigationController,
                            contactsElement,
                            pi,
                            container,
                            field.DisplayName,
                            currentContacts,
                            contacts);
                        listPicker.PushViewController();
                    };
                    element = contactsElement;
                    break;
                case DisplayTypes.LocationList:
                    StringElement locationsElement = new StringElement(field.DisplayName);
                    Item currentLocations = CreateValueList(item, field, currentValue == null ? Guid.Empty : new Guid((string) currentValue));
                    locationsElement.Value = CreateCommaDelimitedList(currentLocations);
                    Item locations = new Item()
                    {
                        Items = App.ViewModel.Items.
                            Where(it => it.ItemTypeID == SystemItemTypes.Location && it.IsList == false).
                            Select(it => new Item() { Name = it.Name, FolderID = folder.ID, ItemTypeID = SystemItemTypes.Reference, ParentID = currentLocations.ID, ItemRef = it.ID }).
                            ToObservableCollection(),
                    };
                    locationsElement.Tapped += delegate
                    {
                        // put up the list picker dialog
                        ListPickerPage listPicker = new ListPickerPage(
                            SystemItemTypes.Location,
                            editViewController.NavigationController,
                            locationsElement,
                            pi,
                            container,
                            field.DisplayName,
                            currentLocations,
                            locations);
                        listPicker.PushViewController();
                    };
                    element = locationsElement;
                    break;
                case DisplayTypes.ItemTypes:
                    var itemTypePickerElement = new ItemTypePickerElement("Type", (Guid) currentValue);
                    itemTypePickerElement.OnSelected += (sender, e) => { pi.SetValue(container, itemTypePickerElement.SelectedItemType, null); };
                    element = itemTypePickerElement;
                    break;
                default:
                    notMatched = true;
                    break;
            }

            // if wasn't able to match field type by display type, try matching by FieldType
            if (notMatched == true)
            {
                switch (field.FieldType)
                {
                    case FieldTypes.String:
                    default:
                        entryElement.KeyboardType = UIKeyboardType.Default;
                        entryElement.Value = (string) currentValue;
                        entryElement.AutocorrectionType = UITextAutocorrectionType.Yes;
                        entryElement.Changed += delegate { pi.SetValue(container, entryElement.Value, null); };
                        break;
                    case FieldTypes.Integer:
                        entryElement.Value = (string) currentValue;
                        entryElement.KeyboardType = UIKeyboardType.NumberPad;
                        entryElement.Changed += delegate { pi.SetValue(container, entryElement.Value, null); };
                        break;
                    case FieldTypes.DateTime:
                        DateTime dateTime = currentValue == null ? DateTime.Now.Date : Convert.ToDateTime ((string) currentValue);
                        DateEventElement dateElement = new DateEventElement(field.DisplayName, dateTime);
                        dateElement.ValueSelected += delegate
                        {
                            pi.SetValue(container, ((DateTime)dateElement.DateValue).ToString("yyyy/MM/dd"), null);
                            folder.NotifyPropertyChanged("FirstDue");
                            folder.NotifyPropertyChanged("FirstDueColor");
                        };
                        element = dateElement;
                        break;
                    case FieldTypes.Boolean:
                        CheckboxElement checkboxElement = new CheckboxElement(field.DisplayName, currentValue == null ? false : (bool) currentValue);
                        checkboxElement.Tapped += delegate { pi.SetValue(container, checkboxElement.Value, null); };
                        element = checkboxElement;
                        break;
                }
            }

            return element;
        }
示例#3
0
文件: Item.cs 项目: ogazitt/zaplify
        // get FieldValue of this Item for the given Field
        // if create == true, a new FieldValue with for this Field will be created if none exists
        public FieldValue GetFieldValue(Field field, bool create = false)
        {
            FieldValue fieldValue = null;

            // initialize the dictionary if necessary
            if (fieldValueDict == null)
                fieldValueDict = new Dictionary<string, FieldValue>();

            // try to get the FieldValue out of the dictionary (if it was already retrieved)
            if (fieldValueDict.TryGetValue(field.Name, out fieldValue) == true)
                return fieldValue;

            // get the fieldvalue associated with this field
            // this may fail if this item doesn't have this field set yet
            fieldValue = fieldValues.FirstOrDefault(fv => fv.FieldName == field.Name);
            if (fieldValue != null)
            {
                // store the new FieldValue in the dictionary
                fieldValueDict[field.Name] = fieldValue;
            }
            else
            {
                // if the caller wishes to create a new FieldValue, do so now
                if (create)
                {
                    fieldValue = new FieldValue()
                    {
                        FieldName = field.Name,
                        ItemID = this.ID,
                        Value = FieldTypes.DefaultValue(field.FieldType)
                    };

                    // store the new FieldValue in the dictionary
                    fieldValueDict[field.Name] = fieldValue;

                    if (fieldValues == null)
                        fieldValues = new ObservableCollection<FieldValue>();

                    // add the new FieldValue in the FieldValues collection
                    fieldValues.Add(fieldValue);
                }
            }

            return fieldValue;
        }
示例#4
0
 private Item CreateValueList(Item item, Field field, Guid itemID)
 {
     Item list;
     if (itemID == Guid.Empty)
     {
         list = new Item()
         {
             ID = itemID, // signal new list
             Name = field.Name,
             IsList = true,
             FolderID = folder.ID,
             ParentID = item.ID,
             ItemTypeID = SystemItemTypes.Reference,
         };
     }
     else
     {
         // get the current value list
         list = CreateItemCopyWithChildren((Guid) itemID);
     }
     return list;
 }
示例#5
0
文件: Item.cs 项目: ogazitt/zaplify
        // get FieldValue of this Item by FieldName
        // if create == true, a new FieldValue for this FieldName will be created if none exists
        public FieldValue GetFieldValue(string fieldName, bool create = false)
        {
            FieldValue fieldValue = null;

            // initialize the dictionary if necessary
            if (fieldValueDict == null)
                fieldValueDict = new Dictionary<string, FieldValue>();

            // try to get the FieldValue out of the dictionary (if it was already retrieved)
            if (fieldValueDict.TryGetValue(fieldName, out fieldValue) == true)
                return fieldValue;

            // get the fieldvalue associated with this field
            // this may fail if this item doesn't have this field set yet
            if (fieldValues.Any(fv => fv.FieldName == fieldName))
            {
                fieldValue = fieldValues.First(fv => fv.FieldName == fieldName);

                // store the new FieldValue in the dictionary
                fieldValueDict[fieldName] = fieldValue;
                return fieldValue;
            }

            if (create)
            {
                // try to find the current item's itemtype (this should succeed)
                ItemType it;
                if (ItemType.ItemTypes.TryGetValue(this.ItemTypeID, out it) == false)
                    return null;

                // try to find the fieldName among the "supported" fields of the itemtype
                // this may fail if this itemtype doesn't support this field name
                Field field = it.Fields.FirstOrDefault(f => f.Name == fieldName);
                if (field == null)
                {
                    // manufacture a synthetic, generic field which has a default FieldType
                    field = new Field() { Name = fieldName, FieldType = FieldTypes.String };
                }

                // delegate to GetFieldValue(Guid fieldID)
                return GetFieldValue(field, create);
            }

            return null;
        }
示例#6
0
文件: Field.cs 项目: ogazitt/zaplify
 public Field(Field field)
 {
     Copy(field);
 }
示例#7
0
文件: Field.cs 项目: ogazitt/zaplify
        public void Copy(Field obj)
        {
            if (obj == null)
                return;

            // copy all of the properties
            foreach (PropertyInfo pi in this.GetType().GetProperties())
            {
                var val = pi.GetValue(obj, null);
                pi.SetValue(this, val, null);
            }
        }
示例#8
0
        private void RenderEditItemFields(Item item, ItemType itemtype, bool primary, bool renderListField)
        {
            // render fields
            foreach (Field f in itemtype.Fields.Where(f => f.IsPrimary == primary).OrderBy(f => f.SortOrder))
                RenderEditItemField(item, f);

            if (renderListField == true)
            {
                Field field = new Field() { Name = "ParentID", DisplayName = "List", DisplayType = DisplayTypes.Lists };
                RenderEditItemField(item, field);

                field = new Field() { Name = "ItemTypeID", DisplayName = "Type", DisplayType = DisplayTypes.ItemTypes };
                RenderEditItemField(item, field);
            }

            // refresh the keyboard tabstops
            keyboardHelper.RefreshTabbedControls(null);
        }
示例#9
0
        private void RenderEditItemField(Item item, Field field)
        {
            // skip rendering a hidden field
            if (field.DisplayType == DisplayTypes.Hidden)
                return;

            PropertyInfo pi = null;
            object currentValue = null;
            object container = null;

            // get the current field value.
            // the value can either be in a strongly-typed property on the item (e.g. Name),
            // or in one of the FieldValues
            try
            {
                // get the strongly typed property
                pi = item.GetType().GetProperty(field.Name);
                if (pi != null)
                {
                    // store current item's value for this field
                    currentValue = pi.GetValue(item, null);

                    // set the container - this will be the object that will be passed
                    // to pi.SetValue() below to poke new values into
                    container = itemCopy;
                }
            }
            catch (Exception)
            {
                // an exception indicates this isn't a strongly typed property on the Item
                // this is NOT an error condition
            }

            // if couldn't find a strongly typed property, this property is stored as a
            // FieldValue on the item
            if (pi == null)
            {
                // get current item's value for this field, or create a new FieldValue
                // if one doesn't already exist
                FieldValue fieldValue = item.GetFieldValue(field.ID, true);
                currentValue = fieldValue.Value;

                // get the value property of the current fieldvalue (this should never fail)
                pi = fieldValue.GetType().GetProperty("Value");
                if (pi == null)
                    return;

                // set the container - this will be the object that will be passed
                // to pi.SetValue() below to poke new values into
                container = fieldValue;
            }

            ListBoxItem listBoxItem = new ListBoxItem();
            StackPanel EditStackPanel = new StackPanel();
            listBoxItem.Content = EditStackPanel;
            EditStackPanel.Children.Add(
                new TextBlock()
                {
                    Text = field.DisplayName,
                    Style = (Style)App.Current.Resources["PhoneTextNormalStyle"]
                });

            // create a textbox (will be used by the majority of field types)
            double minWidth = App.Current.RootVisual.RenderSize.Width;
            if ((int)minWidth == 0)
                minWidth = ((this.Orientation & PageOrientation.Portrait) == PageOrientation.Portrait) ? 480.0 : 800.0;

            TextBox tb = new TextBox() { DataContext = container, MinWidth = minWidth, IsTabStop = true };
            tb.SetBinding(TextBox.TextProperty, new Binding(pi.Name) { Mode = BindingMode.TwoWay });

            bool notMatched = false;
            // render the right control based on the DisplayType
            switch (field.DisplayType)
            {
                case DisplayTypes.Text:
                    tb.InputScope = new InputScope() { Names = { new InputScopeName() { NameValue = InputScopeNameValue.Text } } };
                    tb.LostFocus += new RoutedEventHandler(delegate { pi.SetValue(container, tb.Text, null); });
                    tb.TabIndex = tabIndex++;
                    tb.KeyUp += new KeyEventHandler(TextBox_KeyUp);
                    EditStackPanel.Children.Add(tb);
                    break;
                case DisplayTypes.TextArea:
                    tb.InputScope = new InputScope() { Names = { new InputScopeName() { NameValue = InputScopeNameValue.Text } } };
                    tb.AcceptsReturn = true;
                    tb.TextWrapping = TextWrapping.Wrap;
                    tb.Height = 150;
                    tb.TabIndex = tabIndex++;
                    tb.LostFocus += new RoutedEventHandler(delegate { pi.SetValue(container, tb.Text, null); });
                    EditStackPanel.Children.Add(tb);
                    break;
                case DisplayTypes.Phone:
                    tb.InputScope = new InputScope() { Names = { new InputScopeName() { NameValue = InputScopeNameValue.TelephoneNumber } } };
                    tb.LostFocus += new RoutedEventHandler(delegate { pi.SetValue(container, tb.Text, null); });
                    tb.TabIndex = tabIndex++;
                    tb.MinWidth -= 64;
                    tb.MaxWidth = tb.MinWidth;
                    StackPanel innerPanel = RenderImageButtonPanel(tb);
                    ImageButton imageButton = (ImageButton)innerPanel.Children[1];
                    imageButton.Click += new RoutedEventHandler(delegate
                    {
                        PhoneNumberChooserTask chooser = new PhoneNumberChooserTask();
                        chooser.Completed += new EventHandler<PhoneNumberResult>((sender, e) =>
                        {
                            if (e.TaskResult == TaskResult.OK && e.PhoneNumber != null && e.PhoneNumber != "")
                                pi.SetValue(container, e.PhoneNumber, null);
                        });
                        chooser.Show();
                    });
                    EditStackPanel.Children.Add(innerPanel);
                    break;
                case DisplayTypes.Link:
                    tb.InputScope = new InputScope() { Names = { new InputScopeName() { NameValue = InputScopeNameValue.Url } } };
                    tb.LostFocus += new RoutedEventHandler(delegate { pi.SetValue(container, tb.Text, null); });
                    tb.TabIndex = tabIndex++;
                    tb.KeyUp += new KeyEventHandler(TextBox_KeyUp);
                    EditStackPanel.Children.Add(tb);
                    break;
                case DisplayTypes.Email:
                    tb.InputScope = new InputScope() { Names = { new InputScopeName() { NameValue = InputScopeNameValue.EmailSmtpAddress } } };
                    tb.LostFocus += new RoutedEventHandler(delegate { pi.SetValue(container, tb.Text, null); });
                    tb.TabIndex = tabIndex++;
                    tb.KeyUp += new KeyEventHandler(TextBox_KeyUp);
                    tb.MinWidth -= 64;
                    tb.MaxWidth = tb.MinWidth;
                    innerPanel = RenderImageButtonPanel(tb);
                    imageButton = (ImageButton)innerPanel.Children[1];
                    imageButton.Click += new RoutedEventHandler(delegate
                    {
                        EmailAddressChooserTask chooser = new EmailAddressChooserTask();
                        chooser.Completed += new EventHandler<EmailResult>((s, e) =>
                        {
                            if (e.TaskResult == TaskResult.OK && !String.IsNullOrEmpty(e.Email))
                            {
                                pi.SetValue(container, e.Email, null);
                                // find the contact using the email address
                                Contacts contacts = new Contacts();
                                contacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>((sen, ev) =>
                                {
                                    // save the contact info as a new contact
                                    var contact = ev.Results.FirstOrDefault();
                                    if (contact == null)
                                        return;
                                    ContactPickerHelper.AddContactInfo(contact, item);
                                });
                                contacts.SearchAsync(e.Email, FilterKind.EmailAddress, null);
                            }
                        });
                        chooser.Show();
                    });
                    EditStackPanel.Children.Add(innerPanel);
                    break;
                case DisplayTypes.Address:
                    tb.InputScope = new InputScope()
                    {
                        Names =
                            {
                                new InputScopeName() { NameValue = InputScopeNameValue.AddressStreet },
                                new InputScopeName() { NameValue = InputScopeNameValue.AddressCity },
                                new InputScopeName() { NameValue = InputScopeNameValue.AddressStateOrProvince },
                                new InputScopeName() { NameValue = InputScopeNameValue.AddressCountryName },
                            }
                    };
                    tb.LostFocus += new RoutedEventHandler(delegate { pi.SetValue(container, tb.Text, null); });
                    tb.TabIndex = tabIndex++;
                    tb.KeyUp += new KeyEventHandler(TextBox_KeyUp);
                    tb.MinWidth -= 64;
                    tb.MaxWidth = tb.MinWidth;
                    innerPanel = RenderImageButtonPanel(tb);
                    imageButton = (ImageButton)innerPanel.Children[1];
                    imageButton.Click += new RoutedEventHandler(delegate
                    {
                        // start the location service
                        GeoCoordinateWatcher watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
                        watcher.MovementThreshold = 20; // Use MovementThreshold to ignore noise in the signal.
                        watcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>((sender, e) =>
                        {
                            if (e.Status == GeoPositionStatus.Ready)
                            {
                                // Use the Position property of the GeoCoordinateWatcher object to get the current location.
                                GeoCoordinate co = watcher.Position.Location;
                                tb.Text = co.Latitude.ToString("0.000") + "," + co.Longitude.ToString("0.000");
                                // Stop the Location Service to conserve battery power.
                                watcher.Stop();
                                // also store the latlong information in a hidden LatLong FieldValue
                                var latlong = item.GetFieldValue(FieldNames.LatLong, true);
                                if (latlong != null)
                                    latlong.Value = tb.Text;
                            }
                        });
                        watcher.Start();
                    });
                    EditStackPanel.Children.Add(innerPanel);
                    break;
                case DisplayTypes.Priority:
                    ListPicker lp = new ListPicker()
                    {
                        MinWidth = minWidth,
                        FullModeItemTemplate = (DataTemplate)App.Current.Resources["FullListPickerTemplate"],
                        IsTabStop = true
                    };
                    lp.ItemsSource = App.ViewModel.Constants.Priorities;
                    lp.DisplayMemberPath = "Name";
                    int? lpval = (int?)pi.GetValue(container, null);
                    if (lpval != null)
                        lp.SelectedIndex = (int)lpval;
                    else
                        lp.SelectedIndex = 1;  // HACK: hardcode to "Normal" priority.  this should come from a table.
                    lp.SelectionChanged += new SelectionChangedEventHandler(delegate { pi.SetValue(container, lp.SelectedIndex == 1 ? (int?)null : lp.SelectedIndex, null); });
                    lp.TabIndex = tabIndex++;
                    EditStackPanel.Children.Add(lp);
                    break;
                case DisplayTypes.Folders:
                    ListPicker folderPicker = new ListPicker() { MinWidth = minWidth, IsTabStop = true };
                    folderPicker.ItemsSource = App.ViewModel.Folders;
                    folderPicker.DisplayMemberPath = "Name";
                    Folder tl = App.ViewModel.Folders.FirstOrDefault(list => list.ID == folder.ID);
                    folderPicker.SelectedIndex = App.ViewModel.Folders.IndexOf(tl);
                    folderPicker.SelectionChanged += new SelectionChangedEventHandler(delegate { pi.SetValue(container, App.ViewModel.Folders[folderPicker.SelectedIndex].ID, null); });
                    folderPicker.TabIndex = tabIndex++;
                    EditStackPanel.Children.Add(folderPicker);
                    break;
                case DisplayTypes.Lists:
                    ListPicker listPicker = new ListPicker()
                    {
                        MinWidth = minWidth,
                        FullModeItemTemplate = (DataTemplate)App.Current.Resources["FullListPickerTemplate"],
                        ExpansionMode = ExpansionMode.FullScreenOnly,
                        IsTabStop = true
                    };
                    var lists = App.ViewModel.Items.
                        Where(i => i.FolderID == item.FolderID && i.IsList == true && i.ItemTypeID != SystemItemTypes.Reference).
                        OrderBy(i => i.Name).
                        ToObservableCollection();
                    lists.Insert(0, new Item()
                    {
                        ID = Guid.Empty,
                        Name = folder.Name + " (folder)"
                    });
                    listPicker.ItemsSource = lists;
                    listPicker.DisplayMemberPath = "Name";
                    var listGuid = currentValue != null ? (Guid) currentValue : Guid.Empty;
                    Item thisItem = lists.FirstOrDefault(i => i.ID == (Guid) listGuid);
                    // if the list isn't found (e.g. ParentID == null), SelectedIndex will default to the Folder scope (which is correct for that case)
                    listPicker.SelectedIndex = Math.Max(lists.IndexOf(thisItem), 0);
                    listPicker.SelectionChanged += new SelectionChangedEventHandler(delegate { pi.SetValue(container, lists[listPicker.SelectedIndex].ID, null); });
                    listPicker.TabIndex = tabIndex++;
                    EditStackPanel.Children.Add(listPicker);
                    break;
                case DisplayTypes.DatePicker:
                    DatePicker dp = new DatePicker() { DataContext = container, MinWidth = minWidth, IsTabStop = true };
                    DateTime date = Convert.ToDateTime((string)currentValue);
                    if (date.Ticks == 0)
                        date = DateTime.Now.Date;
                    dp.Value = date;
                    dp.ValueChanged += new EventHandler<DateTimeValueChangedEventArgs>(delegate
                    {
                        //pi.SetValue(container, dp.Value, null);
                        pi.SetValue(container, dp.Value == null ? null : ((DateTime)dp.Value).ToString("d"), null);
                        folder.NotifyPropertyChanged("FirstDue");
                        folder.NotifyPropertyChanged("FirstDueColor");
                    });
                    dp.TabIndex = tabIndex++;
                    EditStackPanel.Children.Add(dp);
                    break;
                case DisplayTypes.DateTimePicker:
                    // create date picker
                    DatePicker datePicker = new DatePicker() { DataContext = container, MinWidth = minWidth, IsTabStop = true };
                    // set up two-way data binding so that we don't have to pick up the new value in the event handler
                    datePicker.SetBinding(DatePicker.ValueProperty, new Binding(pi.Name) { Mode = BindingMode.TwoWay });
                    datePicker.ValueChanged += new EventHandler<DateTimeValueChangedEventArgs>(delegate
                    {
                        folder.NotifyPropertyChanged("FirstDue");
                        folder.NotifyPropertyChanged("FirstDueColor");
                    });
                    datePicker.TabIndex = tabIndex++;
                    EditStackPanel.Children.Add(datePicker);
                    // create time picker
                    TimePicker timePicker = new TimePicker() { DataContext = container, MinWidth = minWidth, IsTabStop = true };
                    // set up two-way data binding so that we don't have to pick up the new value in the event handler
                    timePicker.SetBinding(TimePicker.ValueProperty, new Binding(pi.Name) { Mode = BindingMode.TwoWay });
                    timePicker.ValueChanged += new EventHandler<DateTimeValueChangedEventArgs>(delegate
                    {
                        folder.NotifyPropertyChanged("FirstDue");
                        folder.NotifyPropertyChanged("FirstDueColor");
                    });
                    timePicker.TabIndex = tabIndex++;
                    EditStackPanel.Children.Add(timePicker);
                    break;
                case DisplayTypes.Checkbox:
                    CheckBox cb = new CheckBox() { DataContext = container, IsTabStop = true };
                    cb.SetBinding(CheckBox.IsCheckedProperty, new Binding(pi.Name) { Mode = BindingMode.TwoWay });
                    cb.TabIndex = tabIndex++;
                    EditStackPanel.Children.Add(cb);
                    break;
                case DisplayTypes.TagList:
                    TextBox taglist = new TextBox() { MinWidth = minWidth, IsTabStop = true };
                    taglist.KeyUp += new KeyEventHandler(TextBox_KeyUp);
                    taglist.TabIndex = tabIndex++;
                    RenderEditItemTagList(taglist, (Item) container, pi);
                    EditStackPanel.Children.Add(taglist);
                    break;
                case DisplayTypes.ImageUrl:
                    // TODO: wire up to picture picker, and upload to an image service
                    break;
                case DisplayTypes.LinkArray:
                    tb.InputScope = new InputScope() { Names = { new InputScopeName() { NameValue = InputScopeNameValue.Url } } };
                    tb.AcceptsReturn = true;
                    tb.TextWrapping = TextWrapping.Wrap;
                    tb.Height = 150;
                    tb.TabIndex = tabIndex++;
                    tb.ClearValue(TextBox.TextProperty);
                    if (!String.IsNullOrEmpty((string) currentValue))
                    {
                        try
                        {
                            var linkList = JsonConvert.DeserializeObject<List<Link>>((string)currentValue);
                            tb.Text = String.Concat(linkList.Select(l => l.Name != null ? l.Name + "," + l.Url + "\n" : l.Url + "\n").ToList());
                        }
                        catch (Exception)
                        {
                        }
                    }
                    tb.LostFocus += new RoutedEventHandler(delegate
                    {
                        // the expected format is a newline-delimited list of Name, Url pairs
                        var linkArray = tb.Text.Split(new char[] { '\r','\n' }, StringSplitOptions.RemoveEmptyEntries);
                        var linkList = new List<Link>();
                        foreach (var link in linkArray)
                        {
                            var nameval = link.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                            if (nameval.Length == 0)
                                continue;
                            if (nameval.Length == 1)
                                linkList.Add(new Link() { Url = nameval[0].Trim() });
                            else
                                linkList.Add(new Link() { Name = nameval[0].Trim(), Url = nameval[1].Trim() });
                        }
                        var json = JsonConvert.SerializeObject(linkList);
                        pi.SetValue(container, json, null);
                    });
                    EditStackPanel.Children.Add(tb);
                    break;
                case DisplayTypes.Hidden:
                    // skip rendering
                    break;
                case DisplayTypes.ContactList:
                    Item currentContacts = CreateValueList(item, field, currentValue == null ? Guid.Empty : new Guid((string) currentValue));
                    var contactPicker = new ItemRefListPicker(folder, currentContacts, SystemItemTypes.Contact, pi, container)
                    {
                        MinWidth = minWidth,
                        IsTabStop = true
                    };
                    contactPicker.TabIndex = tabIndex++;
                    contactPicker.MinWidth -= 84;
                    contactPicker.MaxWidth = contactPicker.MinWidth;
                    innerPanel = RenderImageButtonPanel(contactPicker, "/Images/button.add.png", "/Images/button.add.pressed.png");
                    imageButton = (ImageButton)innerPanel.Children[1];
                    imageButton.Click += new RoutedEventHandler(delegate
                    {
                        EmailAddressChooserTask chooser = new EmailAddressChooserTask();
                        chooser.Completed += new EventHandler<EmailResult>((s, e) =>
                        {
                            if (e.TaskResult == TaskResult.OK && !String.IsNullOrEmpty(e.Email))
                            {
                                // find the contact using the email address
                                Contacts contacts = new Contacts();
                                contacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>((sen, ev) =>
                                {
                                    // save the contact info as a new contact
                                    var contact = ev.Results.FirstOrDefault();
                                    if (contact == null)
                                        return;
                                    var newContact = ContactPickerHelper.ProcessContact(contact);
                                    if (newContact != null)
                                    {
                                        // if the list doesn't yet exist, create it now
                                        if (currentContacts.ID == Guid.Empty)
                                        {
                                            Guid id = Guid.NewGuid();
                                            currentContacts.ID = id;

                                            // enqueue the Web Request Record
                                            RequestQueue.EnqueueRequestRecord(RequestQueue.UserQueue,
                                                new RequestQueue.RequestRecord()
                                                {
                                                    ReqType = RequestQueue.RequestRecord.RequestType.Insert,
                                                    Body = currentContacts
                                                });

                                            // add the list to the folder
                                            folder.Items.Add(currentContacts);
                                            StorageHelper.WriteFolder(folder);

                                            // store the list's Guid in the item's property
                                            pi.SetValue(container, id.ToString(), null);
                                        }

                                        // rebuild the image panel with a new ItemRefListPicker
                                        HandleAddedContact(currentContacts, newContact);
                                        var oldListPicker = innerPanel.Children[0] as ItemRefListPicker;
                                        contactPicker = new ItemRefListPicker(folder, currentContacts, SystemItemTypes.Contact, pi, container)
                                        {
                                            IsTabStop = true
                                        };
                                        if (oldListPicker != null)
                                        {
                                            contactPicker.TabIndex = oldListPicker.TabIndex;
                                            contactPicker.MinWidth = oldListPicker.MinWidth;
                                            contactPicker.MaxWidth = oldListPicker.MaxWidth;
                                        }
                                        innerPanel.Children[0] = contactPicker;
                                    }
                                });
                                contacts.SearchAsync(e.Email, FilterKind.EmailAddress, null);
                            }
                        });
                        chooser.Show();
                    });
                    EditStackPanel.Children.Add(innerPanel);
                    break;
                case DisplayTypes.LocationList:
                    Item currentLocations = CreateValueList(item, field, currentValue == null ? Guid.Empty : new Guid((string) currentValue));
                    var locationPicker = new ItemRefListPicker(folder, currentLocations, SystemItemTypes.Location, pi, container)
                    {
                        MinWidth = minWidth,
                        IsTabStop = true
                    };
                    locationPicker.TabIndex = tabIndex++;
                    EditStackPanel.Children.Add(locationPicker);
                    break;
                case DisplayTypes.ItemTypes:
                    ListPicker itemTypePicker = new ListPicker()
                    {
                        MinWidth = minWidth,
                        FullModeItemTemplate = (DataTemplate)App.Current.Resources["FullListPickerTemplate"],
                        ExpansionMode = ExpansionMode.FullScreenOnly,
                        IsTabStop = true
                    };
                    var itemTypes = App.ViewModel.ItemTypes.Where(i => i.UserID != SystemUsers.System).OrderBy(i => i.Name).ToList();
                    itemTypePicker.ItemsSource = itemTypes;
                    itemTypePicker.DisplayMemberPath = "Name";
                    ItemType thisItemType = itemTypes.FirstOrDefault(i => i.ID == (Guid) currentValue);
                    itemTypePicker.SelectedIndex = Math.Max(itemTypes.IndexOf(thisItemType), 0);
                    itemTypePicker.SelectionChanged += new SelectionChangedEventHandler(delegate { pi.SetValue(container, itemTypes[itemTypePicker.SelectedIndex].ID, null); });
                    itemTypePicker.TabIndex = tabIndex++;
                    EditStackPanel.Children.Add(itemTypePicker);
                    break;
                default:
                    notMatched = true;
                    break;
            }

            // if wasn't able to match field type by display type, try matching by FieldType
            if (notMatched == true)
            {
                switch (field.FieldType)
                {
                    case FieldTypes.String:
                    default:
                        tb.InputScope = new InputScope() { Names = { new InputScopeName() { NameValue = InputScopeNameValue.Text } } };
                        tb.LostFocus += new RoutedEventHandler(delegate { pi.SetValue(container, tb.Text, null); });
                        tb.TabIndex = tabIndex++;
                        tb.KeyUp += new KeyEventHandler(TextBox_KeyUp);
                        EditStackPanel.Children.Add(tb);
                        break;
                    case FieldTypes.Integer:
                        tb.InputScope = new InputScope() { Names = { new InputScopeName() { NameValue = InputScopeNameValue.Digits } } };
                        tb.LostFocus += new RoutedEventHandler(delegate { pi.SetValue(container, Convert.ToInt32(tb.Text), null); });
                        tb.TabIndex = tabIndex++;
                        tb.KeyUp += new KeyEventHandler(TextBox_KeyUp);
                        EditStackPanel.Children.Add(tb);
                        break;
                    case FieldTypes.DateTime:
                        DatePicker dp = new DatePicker() { DataContext = container, MinWidth = minWidth, IsTabStop = true };
                        dp.SetBinding(DatePicker.ValueProperty, new Binding(pi.Name) { Mode = BindingMode.TwoWay });
                        dp.ValueChanged += new EventHandler<DateTimeValueChangedEventArgs>(delegate
                        {
                            pi.SetValue(container, dp.Value, null);
                            folder.NotifyPropertyChanged("FirstDue");
                            folder.NotifyPropertyChanged("FirstDueColor");
                        });
                        dp.TabIndex = tabIndex++;
                        EditStackPanel.Children.Add(dp);
                        break;
                    case FieldTypes.Boolean:
                        CheckBox cb = new CheckBox() { DataContext = container, IsTabStop = true };
                        cb.SetBinding(CheckBox.IsEnabledProperty, new Binding(pi.Name) { Mode = BindingMode.TwoWay });
                        cb.TabIndex = tabIndex++;
                        EditStackPanel.Children.Add(cb);
                        break;
                }
            }

            // add the listboxitem to the listbox
            EditListBox.Items.Add(listBoxItem);
        }