Cocoa app without MainMenu.xib? - objective-c

It's been asked before, but no clear (current) answer seems to be out there.. Is it actually possible to build a Cocoa app, WITH a Main Menu, exclusively from code (no MainMenu.xib, etc.) in such a way that it will still pass scrutiny when submitted to the app store? If so, what is the magic incantation(s) to get this to work?
Extra credit if someone can point me to a tutorial or document that's not from 2002 and works on mavericks..

Well, it certainly used to be possible.
I’d start by looking in main.m, and replace NSApplicationMain() with NSApplicationLoad(), so you get an NSApplication object.
Then you can create an NSMenu using standard NSMenu methods (-addItem:, etc), and then call
[NSApplication sharedApplication].mainMenu = myMenu;
Then, you know, make an NSWindow, show that, etc.
Of course, there are a lot of menu items that Apple sets up for you when you launch, that you might not get when you do this. You’d have to add them by hand if so.
This really isn’t something I recommend, but there you go.

http://blog.moostep.com/xamarin-mac-helloworld-application-without-xib/
http://blog.moostep.com/creating-menu-for-xamarin-mac-app-using-csharp/
I don't know enough about Mac App Store to know if this will pass muster. I assume you can translate it from C# to Objective-C. It doesn't use any special Mono or Xamarin features.
Read the blog articles for some background. Meanwhile, I'll paste my versions of main.cs and AppDelegate.cs here:
main.cs:
using System;
using MonoMac.AppKit;
namespace XibFreeCocoaApp
{
public class XibFreeCocoaApp
{
static void Main (string[] args)
{
NSApplication.Init ();
var application = NSApplication.SharedApplication;
application.Delegate = new AppDelegate();
application.Run ();
}
}
}
AppDelegate.cs:
using System;
using System.Drawing;
using MonoMac.AppKit;
using MonoMac.Foundation;
namespace XibFreeCocoaApp
{
public class AppDelegate : NSApplicationDelegate
{
public override void FinishedLaunching (MonoMac.Foundation.NSObject notification)
{
CreateMenu (NSProcessInfo.ProcessInfo.ProcessName);
var window = new NSWindow (
new RectangleF (0, 0, 400, 300),
NSWindowStyle.Titled,
NSBackingStore.Buffered,
false) {
Title = "Xib Free Cocoa App with Menu"
};
window.CascadeTopLeftFromPoint (new PointF (20, 20));
window.MakeKeyAndOrderFront(null);
}
void CreateMenu (string appName)
{
var mainMenu = new NSMenu();
var appMenuItem = new NSMenuItem ();
mainMenu.AddItem (appMenuItem);
var appMenu = new NSMenu ();
var quitMenuItem = new NSMenuItem (String.Format ("Quit {0}", appName), "q", delegate {
NSApplication.SharedApplication.Terminate(mainMenu);
});
appMenu.AddItem (quitMenuItem);
appMenuItem.Submenu = appMenu;
NSApplication.SharedApplication.MainMenu = mainMenu;
}
}
}

Be aware that the code above will not change the title of menu items that have a submenu - to do this you need to assign title to the Submenu as well like so:
NSMenuItem someMenuItem = new NSMenuItem("Title 1");
NSMenu someMenuItemSubMenu = new NSMenu();
someMenuItemSubMenu.Title = "Title 2";
someMenuItem.Submenu = someMenuItemSubMenu;
The displayed tile in this case will be "Title 2", not "Title 1" (actually "Title 1" will be completely ignored).

Related

MvvmCross - How to get iOS Views to load from a different assembly?

I am trying to get the Views in my xamarin-forms + mvvmcross project to load correctly with no luck.
Project structure breakdown:
Project: Shared.Core - 100% cross platform code, view models, models,
etc..
Project: Shared.Mobile - Xamarin-forms views
Project: iOS - uses shared views
Project: Android - uses shared views
Project: UWP - uses shared views
Project: WPF - uses WPF native views
I have a working WPF project using mvvmcross and am trying to get the mobile going starting with iOS.
The iOS project is only loading the views when the views and viewmodels are in the same assembly. Otherwise I am getting:
Foundation.MonoTouchException: Objective-C exception thrown. Name:
NSInternalInconsistencyException Reason: Application windows are
expected to have a root view controller at the end of application
launch
The same can be seen from this sample project by taking the PCL Views folder and moving it to the iOS project.
https://github.com/MvvmCross/MvvmCross-Forms/tree/master/Samples/Example001XAML
I have also tried the following to no avail:
Setup.cs
protected override IEnumerable<Assembly> GetViewModelAssemblies()
{
var result = base.GetViewModelAssemblies();
var assemblyList = result.ToList();
assemblyList.Add(typeof(FirstViewModel).Assembly);
return assemblyList.ToArray();
}
protected override IEnumerable<Assembly> GetViewAssemblies()
{
var result = base.GetViewAssemblies();
var assemblyList = result.ToList();
assemblyList.Add(typeof(FirstPage).Assembly);
return assemblyList.ToArray();
}
protected override void InitializeViewLookup()
{
base.InitializeViewLookup();
var vmLookup = new Dictionary<Type, Type> {
{typeof (FirstViewModel), typeof (FirstPage)},
{typeof (AboutViewModel), typeof (AboutPage)}
};
var container = Mvx.Resolve<IMvxViewsContainer>();
container.AddAll(vmLookup);
}
I have just fixed this in the Forms presenter core so it now works! You were on the right track with overriding GetViewsAssemblies or InitializeViewLookup. That is how it should work if the presenter had been implemented correctly to begin with.
Anyways, with the new changes in this Pull Request the way it works is:
Either override GetViewsAssemblies to let InitializeViewLookup internally map Views to ViewModels, from the found views in where you tell MvvmCross to look for them. The code in Setup.cs will look something like:
protected override IEnumerable<Assembly> GetViewAssemblies()
{
var result = base.GetViewAssemblies();
var assemblyList = result.ToList();
assemblyList.Add(typeof(FirstPage).Assembly);
return assemblyList;
}
Where FirstPage is one of the pages in an Assembly containing views.
Or you can explicitly tell MvvmCross how to map Views to ViewModels in InitializeViewLookup:
protected override void InitializeViewLookup()
{
base.InitializeViewLookup();
var vmLookup = new Dictionary<Type, Type> {
{typeof (FirstViewModel), typeof (FirstPage)}
};
var container = Mvx.Resolve<IMvxViewsContainer>();
container.AddAll(vmLookup);
}

Xamarin Forms Dynamically Load Content in a Page

My current set up:
Xamarin Forms, consisting of iOS, Android, WP app and shared PCL.
Using MVVM Light to keep a nice separation of concerns.
Brief intro into what I want to achieve. I want to have a Base page that has a Cancel and Next button. On pressing the Next button Content is loaded dynamically within that base page.
Xaml View:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="LogInPresenterView">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Cancel" Priority="0" Order="Primary" Command="{Binding Cancel}"></ToolbarItem>
<ToolbarItem Text="Next" Priority="1" Order="Primary" Command="{Binding Next}"></ToolbarItem>
</ContentPage.ToolbarItems>
</ContentPage>
ViewModel Code:
public class LogInPresenterViewModel : ViewModelBase
{
public LogInPresenterViewModel() {}
private RelayCommand _next;
public RelayCommand Next
{
get
{
return _next ?? (_next = new RelayCommand(async () => await DoNext()));
}
}
private async Task DoNext()
{
// IN HERE I WOULD LIKE TO DYNCAMICALLY LOAD CONTENT / VIEWS
}
}
Usually you would have a StackLayout etc before the element. However, on clicking the Next Toolbar Item I want to dynamically load content (that has a viewmodel).
So maybe my ICommand for my next button checked to see what the current content type was, and depending on that I would load another bit of content.
The scenario would be, the base page would load along with the first bit of content - Enter Email and Password. User enters that then clicks on next, if all ok, the content is replaced with the option to enter a security code, keeping the base Close and Next buttons at the top.
Hopefully this makes sense. I know what I want to do in my head, I just don't know how to translate that into Xamarin Forms...
Ok,
So first job is to create your region service in your PCL. This will look something like this:
using System;
using System.Collections.Generic;
namespace xxx
{
public class RegionService : IRegionService
{
private Dictionary<string, object> _regionDictionary;
public RegionService ()
{
_regionDictionary = new Dictionary<string, object> ();
}
#region IRegionService implementation
public bool RegisterRegion (string regionName, object regionObject)
{
object region = null;
_regionDictionary.TryGetValue (regionName, out region);
if (region != null)
_regionDictionary [regionName] = regionObject;
else
_regionDictionary.Add (regionName, regionObject);
return true;
}
public object ResolveRegion (string regionName)
{
object region = null;
_regionDictionary.TryGetValue (regionName, out region);
if (region == null)
throw new RegionServiceException ("Unable to resolve region with given name");
return region;
}
#endregion
}
}
This when you create your page with the dynamic content register your dynamic contentview in your code behind:
ContentView contentView = this.FindById<ContentView>("myContentView");
regionService.RegisterRegion("DynamicView", contentView);
You'll need to create an interface for your views and pages to use to indicate which region they wish to be presented in:
using System;
namespace xxx
{
public interface IRegionView
{
string GetRegionName ();
}
}
Then in your code behind for your view implement this interface to return the name of the region to display in.
You now need a custom presenter to use this region code. I use MVVMCross, so the details will vary for the MVVM implementation you are using, but essentially something like this is what you need:
public async static Task PresentPage(Page page)
{
if (typeof(IRegionView).GetTypeInfo().IsAssignableFrom(page.GetType().GetTypeInfo()))
{
IRegionService regionService = Mvx.Resolve<IRegionService>();
string regionName = (page as IRegionView).GetRegionName();
Page region = regionService.ResolveRegion(regionName) as Page;
if (typeof(IModalPage).GetTypeInfo().IsAssignableFrom(page.GetType().GetTypeInfo()))
await region.Navigation.PushModalAsync(page);
else if (typeof(IPopupPage).GetTypeInfo().IsAssignableFrom(page.GetType().GetTypeInfo()))
region.PushOverlayPage(page);
else if (typeof(NavigationPage).GetTypeInfo().IsAssignableFrom(region.GetType().GetTypeInfo()))
await (region as NavigationPage).PushAsync(page);
}
}
I hope this is useful for you :)
So if this was me. I would create a region service where the contentview registers a unique region name.
Content would then be marked to use that region, and a custom presenter can be used to show the view model's content in the appropriate region.
I'm on my phone whilst travelling at the moment but I can post some code later on if that helps :)
Tristan
You can dynamically load Xamarin Forms UI with XAML.
Old Answer:
This can be achieved with the use of the LoadFromXaml method. It works in the same was as XamlReader.Load in Silverlight/WPF. It is a hidden method that can be only accessed through reflection. There is an article on how to do it here:
http://www.cazzulino.com/dynamic-forms.html
But, I would like to ask to you go to this feature request at Xamarin and ask that the method be made public so that it becomes a fully supported feature:
https://forums.xamarin.com/discussion/comment/252626

Loading Xaml via XamlReader only for Preview

just got some Problems with Loading Xaml Files by Runtime.
For your Information my Code-Snippet to Load the File as Content of a Usercontrol:
public UserControl LoadXaml(FileInfo paramFile)
{
FileInfo _XamlFile = paramFile;
UIElement rootElement;
FileStream s = new FileStream(_XamlFile.FullName, FileMode.Open);
rootElement = (UIElement)XamlReader.Load(s);
s.Close();
UserControl uc = new UserControl();
if (rootElement.GetType() == typeof(Window))
{
uc.Content = (rootElement as Window).Content;
}
else
{
uc = rootElement as UserControl;
}
return uc;
}
private void lstPDFDokumente_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var _XamlFile = ((System.Windows.Controls.ListBox)sender).SelectedItem as FileInfo;
if (_XamlFile != null)
{
layoutGrid.Children.Clear();
System.Windows.Controls.UserControl rootElement;
rootElement = XamlController.LoadXaml(_XamlFile);
layoutGrid.Children.Add(rootElement);
}
}
This works fine while Events and x:Class="..." are deleted by hand.
The Problems I try to solve are:
If there is a x:Class="..." at the root element the XamlReader throws the first exception.
When the XamlReader reaches a Control which contains an event, for Example Click or TextChanged, it throws another Exception.
What i try to figure out is how to load a XamlFile, show it inside of a Control in the main Window and to show some of the attributes like Name,Height,Width and so on.
Just read dozens of Websites but never found a topic about to make a preview or things like that.
One of the solutions i tried is to read the Xaml File as XML and delete that code.
The Problem was to get a list of all possible Events in C#.
If there are some Questions to that Code, feel free to ask :)
Greetings
Daniel

How to give the option to change the Textbloack foreground-color,size in Windowsphone7

I am completely new in Windowsphone7.i have develop sample application in that i want give the option to change the font-color,Size,style(Italic/Bold) as Dynamically(like RadEditor) .please help me how to resolve this option.
If you Develop your Application MVVM style then it is not so hard to do this. You just need a property for every setting you want to set dynamically and then bind to this properties. And you create a Settings View where you can set the properties and if you change them you use INotifyPropertyChanged to broadcast that your properties value changed and so every control which is bound to that property will change and redraw.
GalaSoft MVVM
MVVM Codeplex
Easy MVVM sample Application for Windows Phone 7
The link you found to save an image looks ok, but i did it a bit different, actually from the CameraCaptureTask you already get a WritableBitmap Image and you can save it like this.
To save the Image:
private void SaveToIsolatedStorage(WriteableBitmap image, string fileName)
{
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (myIsolatedStorage.FileExists(fileName))
{
myIsolatedStorage.DeleteFile(fileName);
}
using (var stream = myIsolatedStorage.OpenFile(fileName, FileMode.Create))
{
Extensions.SaveJpeg(image, stream, image.PixelWidth, image.PixelHeight,0, 100);
}
}
}
To read the Image:
private WritableBitmap ReadFromIsolatedStorage(string fileName)
{
WriteableBitmap bitmap = new WriteableBitmap(200,200);
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (store.FileExists(fileName))
{
using (var stream = store.OpenFile(fileName,FileMode.Open))
{
bitmap.SetSource(stream);
}}
}
return bitmap;
}
I hope this will work because i wrote it from scratch. :)
And in your ViewModel you should have a WritableBitmap Property which is bound to your Image Control on your View.
To use a lot of images and work with them you should read a bit more about this, because somehow SL Images use a lot of memory so you will need to address this problem somehow in the future.

Serialize an Activity to xaml

I have Googled a bit, and cannot seem to find any examples of Xaml-fying Activities - good, bad, or otherwise!
public static string ToXaml (this Activity activity)
{
// i would use ActivityXamlServices to go from Xaml
// to activity, but how to go other way? documentation
// is slim, and cannot infer proper usage of
// ActivityXamlServices from Xml remarks :S
string xaml = string.Empty;
return xaml;
}
Hints, tips, pointers would be welcome :)
NOTE: so found this. Will work through and update once working. Anyone wanna beat me to the punch, by all means. Better yet, if you can find a way to be rid of WorkflowDesigner, seems odd it is required.
Alright, so worked through this forum posting.
You may Xaml-fy [ie transform an instance to declarative Xaml] a well-known Activity via
public static string ToXaml (this Activity activity)
{
StringBuilder xaml = new StringBuilder ();
using (XmlWriter xmlWriter = XmlWriter.Create (
xaml,
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true, }))
using (XamlWriter xamlWriter = new XamlXmlWriter (
xmlWriter,
new XamlSchemaContext ()))
using (XamlWriter xamlServicesWriter =
ActivityXamlServices.CreateBuilderWriter (xamlWriter))
{
ActivityBuilder activityBuilder = new ActivityBuilder
{
Implementation = activity
};
XamlServices.Save (xamlServicesWriter, activityBuilder);
}
return xaml.ToString ();
}
Your Xaml may contain certain artifacts, such as references to System.Activities.Presentation namespace appearing as xmlns:sap="...". If this presents an issue in your solution, read the source link above - there is a means to inject directives to ignore unrecognized namespaces.
Will leave this open for a while. If anyone can find a better solution, or improve upon this, please by all means :)
How about XamlServices.Save(filename, activity)?
Based on the other solution (for VS2010B2) and some Reflectoring, I found a solution for VS2010RC. Since XamlWriter is abstract in the RC, the new way to serialize an activity tree is this:
public static string ToXaml (this Activity activity)
{
var xamlBuilder = new StringBuilder();
var xmlWriter = XmlWriter.Create(xamlBuilder,
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true });
using (xmlWriter)
{
var xamlXmlWriter =
new XamlXmlWriter(xmlWriter, new XamlSchemaContext());
using (xamlXmlWriter)
{
XamlWriter xamlWriter =
ActivityXamlServices.CreateBuilderWriter(xamlXmlWriter);
using (xamlWriter)
{
var activityBuilder =
new ActivityBuilder { Implementation = sequence };
XamlServices.Save(xamlWriter, activityBuilder);
}
}
}
return xamlBuilder.ToString();
}