I'm new to Mvvmcross framework and currently exploring the iOS part of it (ohh and also new to iOS development to draw a beautiful picture of my current situation ^^). I'm using the vNext version.
I've found references to implementation of UICollectionViewController (MvxTouchCollectionViewController and MvxBindableCollectionViewSource), but these classes seem to be only a skeleton for a future implementation (abstract class, missing a kind of MvxSimpleBindableCollectionViewSource at least). I haven't found a sample using this feature.
I've also found a blog post from Stuart which lets presume he's working on this part (Work In Progress - MvvmCross lists sample).
Does anybody already play with this part and know about an implementation or usage example?
I've took a look to the 10 first minutes of the xaminar mentioned by Stuart in its article and seems pretty interesting, a good starting point for me.
I've used the collection view controller in several customer apps, but don't think I've published any open source samples that use it.
In essence, the use of the collectionview is very similar to the use of the tableview and cell - which is shown in detail in: http://slodge.blogspot.co.uk/2013/01/uitableviewcell-using-xib-editor.html
In vNext, a sample controller might look like:
public class MyCollectionView : BaseCollectionView<MyCollectionViewModel>
{
private bool _needToCallViewDidLoadManually;
public HubView (MvxShowViewModelRequest request)
: base(request, new UICollectionViewFlowLayout (){
ItemSize= new System.Drawing.SizeF (100, 100),
MinimumInteritemSpacing = 20.0f,
SectionInset = new UIEdgeInsets (10,50,20,50),
ScrollDirection = UICollectionViewScrollDirection.Vertical,
})
{
if (_needToCallViewDidLoadManually) {
ViewDidLoad();
}
}
public override void ViewDidLoad ()
{
if (ShowRequest == null) {
_needToCallViewDidLoadManually = true;
return;
}
base.ViewDidLoad ();
_needToCallViewDidLoadManually = false;
var source = new CollectionViewSource(CollectionView);
this.AddBindings(
new Dictionary<object, string>()
{
{ source, "ItemsSource TheItems" }
});
CollectionView.Source = source;
CollectionView.ReloadData();
}
public class CollectionViewSource : MvxBindableCollectionViewSource
{
public CollectionViewSource (UICollectionView collectionView)
: base(collectionView, MyViewCell.Identifier)
{
collectionView.RegisterNibForCell(UINib.FromName(MyViewCell.Identifier, NSBundle.MainBundle), MyViewCell.Identifier);
}
}
}
If you are starting development now, then you might also benefit from considering the v3 branch which is just entering Beta.
Related
I am toying with Swashbuckle.Examples package (3.10.0) in an ASP.NET MVC project. However, I cannot make request examples appear within the UI.
Configuration (SwaggerConfig.cs)
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c => {
c.SingleApiVersion("v1", "TestApp.Web");
c.IncludeXmlComments(string.Format(#"{0}\bin\TestApp.Web.xml", System.AppDomain.CurrentDomain.BaseDirectory));
c.OperationFilter<ExamplesOperationFilter>();
c.OperationFilter<DescriptionOperationFilter>();
c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
})
.EnableSwaggerUi(c => { });
}
Request example classes
public class EchoRequestExample : IExamplesProvider
{
public object GetExamples()
{
return new EchoInput { Value = 7 } ;
}
}
public class EchoInput
{
public int Value { get; set; }
}
Action
[HttpGet]
[Route("Echo")]
[CustomApiAuthorize]
[SwaggerRequestExample(typeof(EchoInput), typeof(EchoRequestExample))]
[ResponseType(typeof(EchoServiceModel))]
public HttpResponseMessage Echo([FromUri] EchoInput model)
{
var ret = new EchoServiceModel
{
Username = RequestContext.Principal.Identity.Name,
Value = value
};
return Request.CreateResponse(HttpStatusCode.OK, ret);
}
Swagger UI shows xml comments and output metadata (model and an example containing default values), but shows no request example. I attached to process and EchoRequestExample.GetExamples is not hit.
Question: How to make SwaggerRequestExample attribute work in ASP.NET MVC 5?
Note: Windows identity is used for authorization.
I received an answer from library owner here:
Swagger request examples can only set on [HttpPost] actions
It is not clear if this is a design choice or just a limitation, as I find [HttpGet] examples also relevant.
I know the feeling, lot's of overhead just for an example, I struggle with this for a while, so I created my own fork of swashbuckle, and after unsuccessful attempts to merge my ideas I ended up detaching and renaming my project and pushed to nuget, here it is: Swagger-Net
An example like that will be:
[SwaggerExample("id", "123456")]
public IHttpActionResult GetById(int id)
{
Here the full code for that: Swagger_Test/Controllers/IHttpActionResultController.cs#L26
Wondering how that looks like on the Swagger-UI, here it is:
http://swagger-net-test.azurewebsites.net/swagger/ui/index?filter=IHttpActionResult#/IHttpActionResult/IHttpActionResult_GetById
Im new to dependency injection and Ive decided to use autofac as it seems to have the best 'out of the box' support for MVC5 (others might be better but im a newbie to this)
Im creating simple use scenarios and from the wiki ive got the following code in application_start in global.asax
protected void Application_Start()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterType<ArtistController>().InstancePerHttpRequest();
builder.RegisterType<ArtistService>().As<IArtistService>().SingleInstance();
builder.RegisterType<ArtistRepository>().As<IArtistRepository>().SingleInstance();
builder.RegisterType<BandMemberRepository>().As<IBandMemberRepository>).SingleInstance();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
and in my ArtistController I have this
private IArtistService _artistService;
I then have some code the retrieves and updates data, all very simple. This works ok and Im starting to get my head around the whole concept.
My question is this, do I have to register all the concrete classes Im using manually ? My app could eventually grow and I would have many, many classes so this will be a pain to manage. I did come across this
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
which as far as Im aware should register everything for me but it didnt work. Am I doing something wrong ?
ok, thanks for the advice.
the autofac website shows an example using lambdas, so I added this in global.asax
builder.RegisterAssemblyTypes(typeof(MvcApplication).Assembly)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces();
builder.RegisterAssemblyTypes(typeof(MvcApplication).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces();
but that didnt work, any idea why ?
I do most (90+%) of my registrations by tagging them with this attribute:
[AttributeUsage(AttributeTargets.Class)]
[JetBrains.Annotations.MeansImplicitUse]
public class AutoRegisterAttribute : Attribute {}
Then I use this module to register those classes:
public class AutoRegisterModule : Module
{
private readonly Assembly[] _assembliesToScan;
public AutoRegisterModule(params Assembly[] assembliesToScan)
{
_assembliesToScan = assembliesToScan;
}
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(_assembliesToScan)
.Where(t => t.GetCustomAttribute<AutoRegisterAttribute>(false) != null)
.AsSelf()
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
}
public static AutoRegisterModule ForCallingAssembly()
{
return new AutoRegisterModule(Assembly.GetCallingAssembly());
}
}
So when I'm building my container, I typically just do:
builder.RegisterModule(AutoRegisterModule.ForCallingAssembly());
In a cross platform Xamarin app built with the MvvmCross framework I'm using a ToggleButton Widget in an Android .axml layout. I've bound the Checked property to a View Model property using a converter using the following binding syntax:
Checked MarketBuySellViewModel.Direction, Converter=DirectionBool, ConverterParameter='Sell'
Everything works well. On the iOS side, it appears you can use UIButton as a ToggleButton by using the Selected property. This implies that the following binding should achieve what I want on iOS:
set.Bind (SellButton).For(b => b.Selected).To (vm => vm.MarketBuySellViewModel.Direction).WithConversion("DirectionBool", "Sell");
I don't get any binding errors in the application output but the binding itself doesn't seem to work. Clicking the button doesn't set the Direction property and setting the direction to a different value does not set the Selected property on the UIButton.
Do I need to create a Custom Binding or am I simply setting up the binding incorrectly?
I also tried using a UISegmentedControl to achieve the same effect. Is binding to this control supported at all in MvvmCross? I don't see any reference to it in the source code. Does this mean I need to create custom bindings for it too?
For the UIButton, I don't believe there's any included Selected binding built into MvvmCross. Because of this - and because Selected doesn't have a simple paired event SelectedChanged, then I believe Selected binding should work one-way (from ViewModel to View) but not two-way.
There is a binding for the On of a UISwitch control and that's the control I've seen used most in these situations.
If you wanted to add a custom 2-way binding for Selected then I guess you'd have to do this using the ValueChanged event (but would need to check that is correct).
To do so, you'd just build a target binding something like:
public class MvxUIButtonSelectedTargetBinding : MvxPropertyInfoTargetBinding<UIButton>
{
public MvxUIButtonSelectedTargetBinding(object target, PropertyInfo targetPropertyInfo)
: base(target, targetPropertyInfo)
{
var view = View;
view.ValueChanged += HandleValueChanged;
}
private void HandleValueChanged(object sender, System.EventArgs e)
{
var view = View;
if (view == null)
return;
FireValueChanged(view.Selected);
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.TwoWay; }
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (isDisposing)
{
var view = View;
if (view != null)
{
view.ValueChanged -= HandleValueChanged;
}
}
}
}
and this could be registered in Setup in protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry) using something like:
registry.RegisterPropertyInfoBindingFactory(typeof(MvxUIButtonSelectedTargetBinding), typeof(UIButton),
"Selected");
Similarly, I don't believe anyone has added a two way UISegmentedControl binding yet - but would happily see one added.
Building a two way UISegmentedControl binding would be quite straight-forward - you'd just have to bind to the pair SelectedSegment and ValueChanged - with code similar to above.
Alternatively, you could switch to using a custom MySegmentedControl which had a nicer Value`ValueChanged` pair which would automatically work without a custom binding - e.g.:
public class MySegmentedControl : UISegmentedControl
{
// add more constructors if required
public int Value
{
get { return base.SelectedSegment; }
set { base.SelectedSegment = value; }
}
}
If any or all of these custom bindings are needed, then the Mvx project is happy to get these bindings added as issues or pull requests along with test/demo UIs in the https://github.com/slodge/MvvmCross-Tutorials/blob/master/ApiExamples/ApiExamples.Touch/Views/FirstView.cs project
Could be helpful to someone else, so i'm sharing my experience. I needed a two way binding for UISegmentedControl.SelectedSegment property to a ViewModel. The one way biding (ViewModel => View) works by default. I couldn't able to properly utilize the solution proposed by Stuart - to subclass the UISegmentedControl. I tried to ensure that the linker does not rip off the new custom control code, but this didn't help me a bit. So a perfectly viable solution is the one with MvxPropertyInfoTargetBinding. Here is the code working ok for me:
public class MvxUISegmentedControlSelectedSegmentTargetBinding : MvxPropertyInfoTargetBinding<UISegmentedControl>
{
public MvxUISegmentedControlSelectedSegmentTargetBinding(object target, PropertyInfo targetPropertyInfo)
: base(target, targetPropertyInfo)
{
this.View.ValueChanged += HandleValueChanged;
}
private void HandleValueChanged(object sender, System.EventArgs e)
{
var view = this.View;
if (view == null)
{
return;
}
FireValueChanged(view.SelectedSegment);
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.TwoWay; }
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (isDisposing)
{
var view = this.View;
if (view != null)
{
view.ValueChanged -= HandleValueChanged;
}
}
}
}
public class Setup : MvxTouchSetup
{
...
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
registry.RegisterPropertyInfoBindingFactory(typeof(MvxUISegmentedControlSelectedSegmentTargetBinding), typeof(UISegmentedControl), "SelectedSegment");
}
}
I want to use a third party view controller that already inherits from UIViewController (https://bitbucket.org/thedillonb/monotouch.slideoutnavigation/src/f4e51488598b/MonoTouch.SlideoutNavigation?at=master), how would I integrate that with MVVMCross?
I could just take the source and change it to inherit from MvxViewController, but guessing I will run into this with other libraries.
Do I need to implement all the interfaces MvxViewController does? IMvxTouchView? IMvxEventSourceViewController?
For this particular case, where you don't actually want to do any data-binding so you can just use a custom presenter - e.g. see #Blounty's answer, or see this project demo - https://github.com/fcaico/MvxSlidingPanels.Touch
If you ever do need to convert third party ViewController base classes so that they support data-binding, then the easiest way is exactly what you guessed:
inherit from them to provide an EventSource-ViewController
inherit from the EventSource-ViewController to add the Mvx BindingContext
This technique is exactly how MvvmCross itself extends each of UIViewController, UITableViewController, UITabBarController, etc in order to provide data-binding.
For example, see:
extending UIViewController to provide an eventsource - MvxEventSourceViewController.cs
extending the event source ViewController to provide a binding context - MvxViewController.cs
Note that because C# doesn't have any Multiple-Inhertiance or any true Mixin support, this adaption of ViewControllers does involve a little cut-and-paste, but we have tried to minimise this through the use of event hooks and extension methods.
If it helps, this iOS technique for a previous MvvmCross version was discussed in Integrating Google Mobile Analytics with MVVMCross (obviously this is out of date now - but the general principles kind of remain the same - we adapt an existing viewcontroller via inheritance)
In Android, a similar process is also followed for Activity base classes - see ActionBarSherlock with latest MVVMCross
You can use a custom view presenter like below, This is pretty much straight out of my app using the SlideOutNavigation.
public class Presenter
: IMvxTouchViewPresenter
{
private readonly MvxApplicationDelegate applicationDelegate;
private readonly UIWindow window;
private SlideoutNavigationController slideNavigationController;
private IMvxTouchViewCreator viewCreator;
public Presenter(MvxApplicationDelegate applicationDelegate, UIWindow window)
{
this.applicationDelegate = applicationDelegate;
this.window = window;
this.slideNavigationController = new SlideoutNavigationController();
this.slideNavigationController.SlideWidth = 200f;
this.window.RootViewController = this.slideNavigationController;
}
public async void Show(MvxViewModelRequest request)
{
var creator = Mvx.Resolve<IMvxTouchViewCreator>();
if (this.slideNavigationController.MenuView == null)
{
// TODO: MAke this not be sucky
this.slideNavigationController.MenuView = (MenuView)creator.CreateView(new MenuViewModel());
((MenuView) this.slideNavigationController.MenuView).MenuItemSelectedAction = this.MenuItemSelected;
}
var view = creator.CreateView(request);
this.slideNavigationController.TopView = (UIViewController)view;
}
public void ChangePresentation(MvxPresentationHint hint)
{
Console.WriteLine("Change Presentation Requested");
}
public bool PresentModalViewController(UIViewController controller, bool animated)
{
Console.WriteLine("Present View Controller Requested");
return true;
}
public void NativeModalViewControllerDisappearedOnItsOwn()
{
Console.WriteLine("NativeModalViewControllerDisappearedOnItsOwn");
}
private void MenuItemSelected(string targetType, string objectId)
{
var type = Type.GetType(string.Format("App.Core.ViewModels.{0}ViewModel, AppCore", targetType));
var parameters = new Dictionary<string, string>();
parameters.Add("objectId", objectId);
this.Show(new MvxViewModelRequest { ViewModelType = type, ParameterValues = parameters });
}
}
I saw 2 different way to create web parts for sharepoint. Which one is preferred by most?
http://msdn.microsoft.com/en-us/library/aa973249%28office.12%29.aspx
Anything involving VSeWSS is just going to end in pain, so method 1 is definitely out. Method 2 isn't ideal either, as setting up html elements as controls becomes unmanageable at a level just beyond what you see in that demo. I use a fairly simple generic base class that takes a user control as a type parameter and lets me keep all the layout nicely seperated from the sharepoint infrastructure. If you are creating pages/web parts programatically most of the web part xml turns out to be optional also.
public abstract class UserControlWebPart<T> : Microsoft.SharePoint.WebPartPages.WebPart where T:UserControl
{
protected UserControlWebPart()
{
this.ExportMode = WebPartExportMode.All;
}
protected virtual void TransferProperties(T ctrl)
{
var tc = typeof(T);
var tt = this.GetType();
foreach (var p in tt.GetProperties()) {
if (p.IsDefined(typeof(ControlPropertyAttribute), true)) {
foreach (var p2 in tc.GetProperties()) {
if (p2.Name == p.Name) {
p2.SetValue(ctrl, p.GetValue(this, null), null);
}
}
}
}
}
protected override void CreateChildControls()
{
string controlURL = ControlFolder+typeof(T).Name+".ascx";
var ctrl = Page.LoadControl(controlURL) as T;
TransferProperties(ctrl);
this.Controls.Add(ctrl);
}
protected virtual string ControlFolder
{
get {
return "~/_layouts/UserControlWebParts/";
}
}
}
For the few web parts I've written, I guess I've gone more with method #2 than method #1. Seems more straightforward and has the potential to be reused outside of the SharePoint environment (depending on the depth of your business logic).