I'm trying to use the Three20 TTPhotoViewController with MonoTouch. I've derived FacebookPhoto from TTPhoto and FacebookPhotoSource from TTPhotoSource and am now trying to invoke the TTPhotoViewController but I get the following exception when pushing the view controller:
Objective-C exception thrown. Name: NSInvalidArgumentException Reason: * -[NSPlaceholderString initWithFormat:locale:arguments:]: nil argument
I noticed that the monotouch bindings in this github project: https://github.com/mono/monotouch-bindings/tree/492f68c3c2007f0638452cc8a5a762556db224ba/Three20/binding were missing the photoAtIndex binding, so I added that and recompiled them, but I haven't been able to figure out why I am getting this exception.
Here is how I'm invoking the TTPhotoViewController:
List<Photo> photoList = FacebookGraphApi.Instance.GetAlbumPhotos(album.id);
List<FacebookPhoto> fbPhotoList = photoList.Select(x => new FacebookPhoto(x)).ToList();
var photos = new TTPhotoViewController();
photos.PhotoSource = new FacebookPhotoSource(fbPhotoList);
NavController.PushViewController(photos, true);
Here is the definition of the TTPhotoSource
class FacebookPhotoSource : TTPhotoSource
{
List<FacebookPhoto> _photoList;
public FacebookPhotoSource (List<FacebookPhoto> photoList)
{
_photoList = photoList;
int i = 0;
foreach (FacebookPhoto photo in photoList) {
photo.PhotoSource = this;
photo.Index = i++;
}
}
public override string Title {
get {
return "Facebook Photos";
}
set {
throw new NotImplementedException();
}
}
public override int NumberOfPhotos {
get {
return _photoList.Count;
}
}
public override int MaxPhotoIndex {
get {
return _photoList.Count -1;
}
}
public override TTPhoto PhotoAtIndex(int photoIndex)
{
return _photoList[photoIndex];
}
}
and here is the definition of the FacebookPhoto:
class FacebookPhoto : TTPhoto
{
Photo _photo;
public FacebookPhoto(Photo photo)
{
_photo = photo;
}
public override string Caption {
get {
if(_photo.name == null)
return "";
return _photo.name;
}
set {
throw new NotImplementedException();
}
}
public override TTPhotoSource PhotoSource { get; set; }
public override int Index { get; set; }
public override SizeF Size {
get {
return new SizeF(_photo.width, _photo.height);
}
set {
throw new NotImplementedException();
}
}
public override string URLForVersion (int version)
{
switch (version) {
case 4:
return _photo.picture;
default:
return _photo.source;
}
}
}
Related
I am trying to register a ICustomVirtualPathProvider in one of my modules. This is what I am trying to use:
public class AzureVirtualPathProvider : VirtualPathProvider, ICustomVirtualPathProvider
{
public IStaticDataStorageProvider StaticDataStorageProvider { get; set; }
public VirtualPathProvider Instance
{
get
{
return this;
}
}
public AzureVirtualPathProvider(IStaticDataStorageProvider staticDataStorageProvider)
{
StaticDataStorageProvider = staticDataStorageProvider;
}
public override bool FileExists(string virtualPath)
{
if (!virtualPath.Contains("StaticData")) return base.FileExists(virtualPath);
return true;
}
public override VirtualFile GetFile(string virtualPath)
{
if (!virtualPath.Contains("StaticData") || !StaticDataStorageProvider.IsCloud()) return base.GetFile(virtualPath);
return new CustomVirtualFile(StaticDataStorageProvider, virtualPath);
}
}
so in Module.Load I am setting:
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<AzureVirtualPathProvider>().PropertiesAutowired().As<ICustomVirtualPathProvider>();
}
but this has not been picked up when Orchard calls this line in OrchardStartup.cs (in Orchard.Framework)
if (HostingEnvironment.IsHosted) {
foreach (var vpp in container.Resolve<IEnumerable<ICustomVirtualPathProvider>>()) {
HostingEnvironment.RegisterVirtualPathProvider(vpp.Instance);
}
}
I haver tried calling HostingEnvironment.RegisterVirtualPathProvider directly thus:
HostingEnvironment.RegisterVirtualPathProvider(new AzureVirtualPathProvider());
and tried to inject the dependency using property injecction:
builder.Register(c => new AzureVirtualPathProvider { StaticDataStorageProvider = c.Resolve<IStaticDataStorageProvider>() });
however the value for StaticDataStorageProvider is always null when AzureVirtualPathProvider is run.
I have tried moving AzureVirtualPathProvider to OrchardFramework but then it does not resolve StaticDataStorageProvider.
How do I get Orchard to load my CustomVirtualPathProvider?
In the end I did this:
public class OrchardShellEvents : IOrchardShellEvents
{
readonly ICustomVirtualPathProvider _customVirtualPathProvider;
public OrchardShellEvents(ICustomVirtualPathProvider customVirtualPathProvider)
{
_customVirtualPathProvider = customVirtualPathProvider;
}
public void Activated()
{
HostingEnvironment.RegisterVirtualPathProvider(_customVirtualPathProvider.Instance);
}
public void Terminating()
{
}
}
I don't know if this is the best solution but it worked and might help someone else.
I'm having trouble getting my custom tokens to work with my ContentPart. My problem is the same as what is described here:
Is it possible to create an orchard autoroute using contents of a custom type property?
I have created my tokens:
namespace MyNS.Types.Providers
{
public class BioPartTokens : ITokenProvider
{
public BioPartTokens() {
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public void Describe(dynamic context) {
context.For("Bio", T("Bio"), T("Tokens for the Bio content type"))
.Token("FirstName", T("FirstName"), T("First name of person."))
.Token("LastName", T("LastName"), T("Last name of person."));
}
public void Evaluate(dynamic context) {
context.For<BioPart>("Bio")
.Token("FirstName", (Func<BioPart, object>) (f => f.ContentItem.Parts.OfType<BioPart>().First().FirstName.ToLower()))
.Chain("FirstName", "FirstName", (Func<BioPart, object>)(f => f.ContentItem.Parts.OfType<BioPart>().First().FirstName.ToLower()))
.Token("LastName", (Func<BioPart, object>)(f => f.ContentItem.Parts.OfType<BioPart>().First().LastName.ToLower()))
.Chain("LastName", "LastName", (Func<BioPart, object>)(f => f.ContentItem.Parts.OfType<BioPart>().First().LastName.ToLower()))
;
}
}
}
My model:
namespace MyNS.Types.Models
{
public class BioPart: ContentPart<BioPartRecord>
{
public string FirstName {
get { return Record.FirstName; }
set { Record.FirstName = value; }
}
public string LastName
{
get { return Record.LastName; }
set { Record.LastName = value; }
}
public RoleInSchool RoleInSchool
{
get { return Record.RoleInSchool; }
set { Record.RoleInSchool = value; }
}
public bool IsBlogger {
get { return Record.IsBlogger; }
set { Record.IsBlogger = value; }
}
}
}
Though I've tried URL patterns with all of the following tokens, I've not been able to get a value back from the form that I've submitted:
{Content.Bio.FirstName}
{Content.BioPart.FirstName}
{Bio.FirstName}
{BioPart.FirstName}
No errors are being logged.
Hi I am trying to upgrade our ios app from mvvmcross v1 to v3. I can't figure out how to make my custom buttonrow work.
My view ViewDidLoad it is the button items that binds to the button row
public override void ViewDidLoad()
{
base.ViewDidLoad();
SortingView.ViewModel = ViewModel;
_shown = false;
// Setup View Animatons
Buttons.OnClick = () => { AnimationTransition = ViewTransitionAnimation.TransitionFade; };
TopRightButton.TouchDown +=
(sender, args) => {
AnimationTransition = ViewTransitionAnimation.TransitionCrossDissolve;
};
// Setup Bindings
this.AddBindings(
new Dictionary<object, string>
{
{this.BackgroundImage, "{'ImageData':{'Path':'BackgroundImage','Converter':'ImageItem'}}"},
{this.TopbarBackground, "{'ImageData':{'Path':'TopBarImage','Converter':'ImageItem'}}"},
{this.TopLogo, "{'ImageData':{'Path':'Logo','Converter':'ImageItem'}}"},
{this.Buttons, "{'ItemsSource':{'Path':'ButtonItems'}}"},
{this.SlideMenu, "{'ItemsSource':{'Path':'VisibleViews'}}"},
{
this.SortingView,
"{'ItemsSource':{'Path':'CategoriesName'},'SelectedGroups':{'Path':'SelectedGroups'},'ForceUbracoUpdateAction':{'Path':'ForceUbracoUpdateAction'}}"
},
{this.SettingsButton, "{'TouchDown':{'Path':'TopRightButtonClick'},'Hide':{'Path':'HideTopbarButton'},'ImageData':{'Path':'TopButtonImage','Converter':'ImageItem'}}" },
{this.TopRightButton, "{'TouchDown':{'Path':'SecondaryButtonButtonPushed'},'Hide':{'Path':'HideTopbarButton2'},'ImageData':{'Path':'SettingsButtonImage','Converter':'ImageItem'}}" }
});
// Perform any additional setup after loading the view, typically from a nib.
NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.DidBecomeActiveNotification, ReEnableSlideMenu);
this.SortingView.Hidden=true;
ViewModel.SettingButtonEvent += HandleSettingButtonPushed;
}
Here is my custom control "ButtonRow
[Register("ButtonRow")]
public class ButtonRow : CustomListViewController
{
private int _width = 0;
private UIImage _backgroundImage = null;
public ButtonRow(IntPtr handle)
: base(handle)
{
_width = (int)this.Frame.Width;
UseImageAsIcon = false;
FontSize=0;
}
public bool UseImageAsIcon { get; set; }
public UIImage BackgroundImage
{
get { return _backgroundImage; }
set { _backgroundImage = value; }
}
public int FontSize
{
get;set;
}
private Action _onClickAction;
private int _spacing = 0;
public Action OnClick
{
get
{
return _onClickAction;
}
set
{
_onClickAction = value;
}
}
/// <summary>
/// The add views.
/// </summary>
/// custom implementation for adding views to the button row
protected override void AddViews()
{
if (ItemsSource == null)
{
Hidden = true;
return;
}
base.AddViews();
foreach (UIView uiView in Subviews)
{
uiView.RemoveFromSuperview();
}
if (ItemsSource.Count == 0)
{
Hidden = true;
return;
}
if (_backgroundImage != null)
{
var frame = new RectangleF(0, 0, Frame.Width, Frame.Height);
var background = new UIImageView(frame) { Image = _backgroundImage };
AddSubview(background);
}
_width = _width - ((ItemsSource.Count - 1) * Spacing);
var buttonWidth = (int)Math.Ceiling (((double)_width) / ItemsSource.Count);
int index = 0;
foreach (ViewItemModel item in ItemsSource)
{
// creating custom button with needed viewmodel, nib etc is loaded in the class constructor
var button = new ButtonWithLabel(item, OnClick);
if (FontSize > 0)
{
button.FontSize(FontSize);
}
if (UseImageAsIcon)
{
button.AddBindings(
new Dictionary<object, string>
{
{ button, "{'IconLabel':{'Path':'Title'},'TitleFontColor':{'Path':'TitleFontColor'}}" },
{ button.icon, "{'ImageData':{'Path':'ImageIcon','Converter':'ImageItem'}}" }
});
}
else
{
// bindings created between the button and its viewmodel
button.AddBindings(
new Dictionary<object, string>
{
{button, "{'Label':{'Path':'Title'},'TitleFontColor':{'Path':'TitleFontColor'},'BackgroundColor':{'Path':'BackgroundColor'}}" },
{button.Background, "{'ImageData':{'Path':'ImageIcon','Converter':'ImageItem'}}" }
});
button.icon.Hidden = true;
}
// new frame of button is set, as the number of buttons is dynamic
int x = index == 0 ? 0 : index * (buttonWidth + Spacing);
button.SetFrame(new RectangleF(x, 0, buttonWidth, Frame.Height));
// the view of the button is added to the buttonrow view
AddSubview(button.View);
index++;
}
}
public int Spacing
{
get
{
return this._spacing;
}
set
{
this._spacing = value;
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
public override void Cleanup()
{
if(Subviews!=null)
{
foreach (var view in Subviews)
{
view.RemoveFromSuperview();
}
}
if(_backgroundImage!=null)
{
_backgroundImage.Dispose();
}
ItemsSource = null;
}
}
Here is my CustomListViewController
public class CustomListViewController: UIView
{
private IList _itemsSource;
private CustomViewSource _viewSource;
public CustomListViewController(MvxShowViewModelRequest showRequest)
{
ShowRequest = showRequest;
}
protected CustomListViewController()
{
}
public CustomListViewController(IntPtr handle)
: base(handle)
{
}
public bool IsVisible
{
get
{
return this.IsVisible;
}
}
public IList ItemsSource
{
get { return _itemsSource; }
set { _itemsSource = value; if(value!=null){CreateViewSource(_itemsSource); }}
}
public virtual void CreateViewSource(IList items)
{
if (_viewSource == null)
{
_viewSource = new CustomViewSource();
_viewSource.OnNewViewsReady += FillViews;
}
_viewSource.ItemsSource = items;
}
private void FillViews(object sender, EventArgs e)
{
AddViews();
}
protected virtual void AddViews()
{
// get views from source and do custom allignment
}
public virtual void Cleanup()
{
if(_viewSource!=null)
{
_itemsSource.Clear();
_itemsSource=null;
_viewSource.OnNewViewsReady -= FillViews;
_viewSource.ItemsSource.Clear();
_viewSource.ItemsSource = null;
_viewSource=null;
}
}
public MvxShowViewModelRequest ShowRequest { get;
private set;
}
}
And My CustomViewSource
public class CustomViewSource
{
private IList _itemsSource;
private List<UIView> _views=new List<UIView>();
public event EventHandler<EventArgs> OnNewViewsReady;
public CustomViewSource ()
{
}
public List<UIView> Views { get { return _views; } }
public virtual IList ItemsSource
{
get { return _itemsSource; }
set
{
// if (_itemsSource == value)
// return;
var collectionChanged = _itemsSource as INotifyCollectionChanged;
if (collectionChanged != null)
collectionChanged.CollectionChanged -= CollectionChangedOnCollectionChanged;
_itemsSource = value;
collectionChanged = _itemsSource as INotifyCollectionChanged;
if (collectionChanged != null)
collectionChanged.CollectionChanged += CollectionChangedOnCollectionChanged;
ReloadViewData();
}
}
protected object GetItemAt(int position)
{
if (ItemsSource == null)
return null;
return ItemsSource[position];
}
protected void CollectionChangedOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
ReloadViewData();
}
protected virtual void ReloadViewData()
{
if(ItemsSource==null){return;}
foreach (var VARIABLE in ItemsSource)
{
//call create view and add it to Views
}
//event new views created
if(OnNewViewsReady!=null)
OnNewViewsReady(this,new EventArgs());
}
protected virtual UIView CreateUIView(int position)
{
UIView view = null;
/*
//create view from nib
UIView newView=null;
return newView;
* */
return view;
}
}
Any one have any clues on how to make this work in mvvmcross v3 ?
I would like to make it so i can add x number of buttons and load the buttons from nib files. Have looked at the Kittens collection view example, but have not figured out how to make it work for my buttonRow, not sure if the collectionView is the right one to use as base.
The most common way to show a list is to use a UITableView.
There are quite a few samples around that show how to load these in MvvmCross:
n=2 and n=2.5 in http://mvvmcross.wordpress.com/
n=6 and n=6.5 in http://mvvmcross.wordpress.com/
Working with Collections in https://github.com/MvvmCross/MvvmCross-Tutorials/tree/master/Working%20With%20Collections
In your case, it looks like the previous coder has implemented some sort of custom control with custom layout. Adapting this to v3 shouldn't be particularly difficult, but you are going to need to read through and understand the previous code and how it works (this is nothing to do with MvvmCross - it's just app UI code).
One sample on dealing with custom views like this in iOS is the N=32 tutorial - http://slodge.blogspot.co.uk/2013/06/n32-truth-about-viewmodels-starring.html
I want to create the following Alias from the recipe.
How is this achieved?
I have created this class to add a command for Orchard to create a new alias from command line or recipe:
using Orchard;
using Orchard.Alias;
using Orchard.Commands;
using System;
using Orchard.Environment;
using System.Linq;
namespace Contrib.Foundation.Common.Commands
{
public class AliasCommands : DefaultOrchardCommandHandler
{
private readonly Work<WorkContext> _workContext;
private readonly IAliasService _aliasService;
public AliasCommands(Work<WorkContext> workContext, IAliasService aliasService,
IOrchardServices orchardServices)
{
_workContext = workContext;
_aliasService = aliasService;
Services = orchardServices;
}
public IOrchardServices Services { get; private set; }
[OrchardSwitch]
public string AliasPath { get; set; }
[OrchardSwitch]
public string RoutePath { get; set; }
[CommandName("alias add")]
[CommandHelp("alias add /AliasPath:<alias-path> /RoutePath:<route-path>\r\n\t" + "Add a new alias")]
[OrchardSwitches("AliasPath,RoutePath")]
public void Add()
{
AliasPath = AliasPath.TrimStart('/', '\\');
if (String.IsNullOrWhiteSpace(AliasPath))
{
AliasPath = "/";
}
if (String.IsNullOrWhiteSpace(RoutePath))
{
Context.Output.WriteLine(T("Route can't be empty"));
return;
}
if (CheckAndWarnIfAliasExists(AliasPath))
{
Context.Output.WriteLine(T("Alias already exist"));
return;
}
try
{
_aliasService.Set(AliasPath, RoutePath, "Custom");
}
catch (Exception ex)
{
Services.TransactionManager.Cancel();
Context.Output.WriteLine(T("An error occured while creating the alias {0}: {1}. Please check the values are correct.", AliasPath, ex.Message));
return;
}
Context.Output.WriteLine(T("Alias {0} created.", AliasPath));
}
private string GetExistingPathForAlias(string aliasPath)
{
var routeValues = _aliasService.Get(aliasPath.TrimStart('/', '\\'));
if (routeValues == null) return null;
return _aliasService.LookupVirtualPaths(routeValues, _workContext.Value.HttpContext)
.Select(vpd => vpd.VirtualPath)
.FirstOrDefault();
}
private bool CheckAndWarnIfAliasExists(string aliasPath)
{
var routePath = GetExistingPathForAlias(aliasPath);
if (routePath == null) return false;
return true;
}
}
}
You can use it in a recipe like this:
<Command>
alias add /AliasPath:"/" /RoutePath:"mycontroller"
</Command>
Put the class inside your module and reference Orchard.Alias
I would like to use nested list in my web.config. I see pieces of code that work, but not mine, i have an error when i try to read the configuration section :
System.Configuration.ConfigurationErrorsException : Élément non
reconnu IsochroneServicesTestImbrique'.
My config :
<IsochroneServicesTest>
<add name="TC">
<IsochroneServicesTestImbrique>
<add name="DEFAULT" path="success"/>
</IsochroneServicesTestImbrique>
</add>
</IsochroneServicesTest>
and the code :
[ConfigurationProperty("IsochroneServicesTest")]
[ConfigurationCollection(typeof(CollectionIsochrone), AddItemName = "add")]
public CollectionIsochroneType IsochroneServicesTest
{
get { return (CollectionIsochroneType)this["IsochroneServicesTest"]; }
set { this["IsochroneServicesTest"] = value; }
}
public class CollectionIsochroneType : ConfigurationElementCollection
{
public void Add(ProviderElement element)
{
base.BaseAdd(element);
}
protected override ConfigurationElement CreateNewElement()
{
return new IsochroneElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((IsochroneTypeElement)element).Name;
}
public IsochroneTypeElement this[int index]
{
get { return (IsochroneTypeElement)BaseGet(index); }
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
public new IsochroneTypeElement this[string Name]
{
get { return (IsochroneTypeElement)BaseGet(Name); }
}
public int IndexOf(IsochroneTypeElement element)
{
return BaseIndexOf(element);
}
protected override void BaseAdd(ConfigurationElement element)
{
BaseAdd(element, false);
}
}
public class IsochroneTypeElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
[ConfigurationProperty("IsochroneServicesTestImbrique", IsRequired = true)]
[ConfigurationCollection(typeof(CollectionIsochrone), AddItemName = "add")]
public CollectionIsochrone IsochroneServicesImbrique
{
get { return (CollectionIsochrone)this["IsochroneServicesTestImbrique"]; }
set { this["IsochroneServicesTestImbrique"] = value; }
}
}
public class CollectionIsochrone : ConfigurationElementCollection
{
public void Add(ProviderElement element)
{
base.BaseAdd(element);
}
protected override ConfigurationElement CreateNewElement()
{
return new IsochroneElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((IsochroneElement)element).Name;
}
public IsochroneElement this[int index]
{
get { return (IsochroneElement)BaseGet(index); }
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
public new IsochroneElement this[string Name]
{
get { return (IsochroneElement)BaseGet(Name); }
}
public int IndexOf(IsochroneElement element)
{
return BaseIndexOf(element);
}
protected override void BaseAdd(ConfigurationElement element)
{
BaseAdd(element, false);
}
}
public class IsochroneElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
[ConfigurationProperty("path", IsRequired = true)]
public string Path
{
get { return (string)this["path"]; }
set { this["path"] = value; }
}
}
Thanks for your help
You are getting that error because you are missing the declaration for IsochroneServicesTestImbrique in your <configSections> section. Also, at least one class must inherit from ConfigurationSection. See the example here.