主要说说MonoCross中重要的几个基类和接口,主要是摘抄,给自己忘记的时候看看,因为几个类很重要,所以我觉得很有必要摘抄
书上是以一个demo展开讲这几个基类的,所以我也按照这个顺序来写,思路才不会奇奇怪怪的。
Using MVC pattern
Model
像前面的文章说到的,build一个monocross应用,第一步是先定义好Model,下面是一个customer managment system的Customer的Model定义
public class Customer { public Customer() { ID = “0”; Name = string.Empty; Website = string.Empty; PrimaryAddress = new Address(); Addresses = new List<Address>(); Contacts = new List<Contact>(); Orders = new List<Order>(); } public string ID { get; set; } public string Name { get; set; } public string Website { get; set; } public string PrimaryPhone { get; set; } public Address PrimaryAddress { get; set; } public List<Address> Addresses { get; set; } public List<Contact> Contacts { get; set; } public List<Order> Orders { get; set; } }
Controller
are inherited from the abstract MXController<T> class.
建好Model之后,就要建Controller了,所有Monocross中的Controller都是继承于抽象类MXController<T>的
using System; using System.Collections.Generic; namespace MonoCross.Navigation { public interface IMXController{ Dictionary<string, string> Parameters { get; set; } String Uri { get; set; } IMXView View { get; set; } Type ModelType { get; } object GetModel(); string Load(Dictionary<string, string> parameters); void RenderView(); } public abstract class MXController<T> : IMXController { public string Uri { get; set; } public Dictionary<string, string> Parameters { get; set; } public T Model { get; set; } public Type ModelType { get { return typeof(T); } } public virtual IMXView View { get; set; } public object GetModel() { return Model; } public abstract string Load(Dictionary<string, string> parameters); public virtual void RenderView() { if (View != null) View.Render(); } } }
上面是接口IMXController和抽象类MXController<T>的定义。
下面是Customer类的Controller实现的一部分代码
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Net; using System.Xml.Serialization; using MonoCross.Navigation; using CustomerManagement.Shared; using CustomerManagement.Shared.Model; namespace CustomerManagement.Controllers { public class CustomerController : MXController<Customer> { public override string Load(Dictionary<string, string> parameters) { string perspective = ViewPerspective.Default; string customerId = null; parameters.TryGetValue(“CustomerId”, out customerId); // get the action, assumes string action; if (!parameters.TryGetValue(“Action”, out action)) { // set default action if none specified action = “GET”; } switch (action) { case “EDIT”: case “GET”: // populate the customer model if (customerId == null) throw new Exception(“No Customer Id found”); if (string.Equals(customerId.ToUpper(), “NEW”)) { // assign Model a new customer for editing Model = new Customer(); perspective = ViewPerspective.Update; } else { Model = GetCustomer(customerId); if (String.Equals(action, “EDIT”)) perspective = ViewPerspective.Update; else perspective = ViewPerspective.Default; } break; case “DELETE”: if (customerId == null) customerId = Model.ID; // post delete request to the server DeleteCustomer(customerId); // return and let redirected controller execute, // remaining navigation is ignored MXContainer.Instance.Redirect(“Customers”); return ViewPerspective.Delete; case “CREATE”: // process addition of new model if(AddNewCustomer(Model)) MXContainer.Instance.Redirect(“Customers”); break; case “UPDATE”: if(UpdateCustomer(Model)) MXContainer.Instance.Redirect(“Customers”); break; } return perspective; }
The Load() method of the CustomerController is where you write any logic necessary to initialize and prepare your Company object for presentation. The Load() method receives a dictionary of parameters on the argument that contains any model-specifi
c information passed in the navigation call that loads the controller. In this case, you pass the unique “Customer” identifi er for the company you want to load, make a call to the data service to retrieve the company data, and then set the model property
of the CustomerController to a new Company object instance.
View
public interface IMXView { Type ModelType { get; } void SetModel(object model); void Render(); }
The IMXView interface defi nes the implementation contract for all MonoCross views. The interface consists of the properties, methods, and events necessary for the MonoCross navigation framework to initialize the rendering of a view from
a controller in the shared application. This approach offers great fl exibility in defi ning views across multiple mobile platforms. You can implement your views using whatever approach is required by the target platform, provided you implement the IMXView
interface.
using System; namespace MonoCross.Navigation { public delegate void ModelEventHandler(object model); public interface IMXView { Type ModelType { get; } void SetModel(object model); void Render(); } public abstract class MXView<T> : IMXView { public Type ModelType { get { return typeof(T); } } public virtual void SetModel(object model) { Model = (T)model; } public virtual void Render() { } public virtual T Model { get; set; } } }
Use the MXView<T> class to create a view for your Customer model class. For simplicity in illustrating the concept, the view in Listing 4-6 has been created for the Windows Console target.
下面是控制台实现Customer view的代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using MonoCross.Navigation; using MonoCross.Console; using CustomerManagement.Shared.Model; namespace CustomerManagement.Console.Views { class CustomerView : MXConsoleView<Customer> { public override void Render() { System.Console.Clear(); System.Console.WriteLine(“Customer Details”); System.Console.WriteLine(); System.Console.WriteLine(Model.Name); System.Console .WriteLine(string .Format(“{0} {1}”, Model.PrimaryAddress.Street1, Model.PrimaryAddress.Street2)); System.Console .WriteLine(string .Format(“{0}, {1} {2}”, Model.PrimaryAddress.City, Model.PrimaryAddress.State, Model.PrimaryAddress.Zip)); System.Console .WriteLine(“Previous Orders: “ + Model.Orders.Count.ToString()); System.Console .WriteLine(“Addresses:” + Model.Addresses.Count.ToString()); System.Console .WriteLine(“Contacts: “ + Model.Contacts.Count.ToString()); System.Console.WriteLine(); System.Console.WriteLine(“Web: “ + Model.Website); System.Console.WriteLine(“Phone: “ + Model.PrimaryPhone); System.Console.WriteLine(); System.Console.WriteLine(“Enter to Continue, (D)elete or (E)dit”); while (true) { string input = System.Console.ReadLine().Trim(); if (input.Length == 0) { this.Back(); return; } else if (input.StartsWith(“E”)) { this.Navigate(“Customers/” + Model.ID + “/EDIT”); } else if (input.StartsWith(“D”)) { this.Navigate(“Customers/” + Model.ID + “/DELETE”); } } } } }
The Render() method of the CustomerView class is where you define the logic to display your model customer instance to the user. In this case the model property values are simply displayed in the appropriate format using the Console.WriteLine()
method.
看完上面几个基类后,下面需要看看将他们联系起来的几个类以及方法
Using URI-Based Navigation
To turn your MVC combinations into a cohesive application, you need a mechanism to bind them together. The MonoCross navigation framework provides that
mechanism using a URI-based navigation structure. Each controller in your application is associ-ated with one or more URI endpoints that uniquely identify its place or places in the application workflow. Each action in your application, whether selecting an
item from a list or saving a form, is represented as a navigation to one of these controller endpoints. By using this technique, MonoCross enables the defi nition of any number of complex workfl ows, while at the same time encapsulating
and reusing as much controller business logic as possible. MonoCross navigation also builds upon the loosely coupled MVC pattern described previously with the addition of two important concepts: a shared application and a platform container.
Building the Shared Application
in this same structure.
using System; using System.Collections.Generic; using System.Linq; namespace MonoCross.Navigation { public abstract class MXApplication { public string NavigateOnLoad { get; set; } public string Title { get; set; } public NavigationList NavigationMap = new NavigationList(); protected MXApplication() { NavigateOnLoad = string.Empty; OnAppLoad(); } public virtual void OnAppLoad() { } public virtual void OnAppLoadComplete() { } public class NavigationList : List<MXNavigation> { public void Add(string pattern, IMXController controller) { this.Add(pattern, controller, new Dictionary<string, string>()); } public IMXController GetControllerForPattern(string pattern) { return this.Contains(pattern) ? this.Where(m => m.Pattern == pattern) .First().Controller : null; } public String GetPatternForModelType(Type modelType) { return this.Where(m => m.Controller.ModelType == modelType) .First().Pattern; } public bool Contains(string pattern) { return this.Where(m => m.Pattern == pattern).Count() > 0; } public void Add(string pattern, IMXController controller, Dictionary<string, string> parameters) { #if DROID Android.Util.Log.Debug(“NavigationList”, “Adding: ‘” + pattern + “’”); #endif // Enforce uniqueness MXNavigation currentMatch = this .Where(m => m.Pattern == pattern) .FirstOrDefault(); if (currentMatch != null) { #if DEBUG string text = string .Format(“MapUri \”{0}\” is already matched to Controller type {1}”, pattern, currentMatch.Controller); throw new Exception(text); #else return; #endif } this.Add(new MXNavigation(pattern, controller, parameters)); } } } }
You inherit from the MXApplication class to create your MonoCross application and register your workflow endpoints for navigation.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using MonoCross.Navigation; using CustomerManagement.Controllers; namespace CustomerManagement { public class App : MXApplication { public override void OnAppLoad() { // Set the application title Title = “Customer Management”; // Add navigation mappings NavigationMap.Add(“Customers”, new CustomerListController()); CustomerController customerController = new CustomerController(); NavigationMap.Add(“Customers/{CustomerId}”, customerController); NavigationMap.Add(“Customers/{CustomerId}/{Action}”, customerController); // Set default navigation URI NavigateOnLoad = “Customers”; } } }
Adding Controllers to the Navigation Map
of the model. Navigation endpoints are registered in the NavigationMap found in the MXApplication class.
NavigationMap.Add(“Customers/{CustomerId}”, customerController);
This establishes the default entry point for an individual order. The URI template shown uses the squiggly bracket syntax ( { } ) to indicate a substituted value, (in this case “CustomerId”) in the navigation URI. So a URI of Customers/1234 loads the CustomerController
from your NavigationMap . At runtime, the navigation framework extracts the individual customer identifier, and places it in the controller parameters argument passed to the CustomerController.Load() method as a name/value pair, (CustomerId=1234).
NavigationMap.Add(“Customers/{CustomerId}/{Action}”, customerController);
是在customController中选择一个Action操作后,再次去到另一个customController中,看会前面的customer的controller就可以看到在load方法中,有取了action参数出来,然后根据参数不同的取值,进行不同的操作。这样就把一个controller多次使用了。
Adding a Platform Container
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using MonoCross.Navigation; namespace MonoCross.Console { public static class MXConsoleNavigationExtensions { public static void Back(this IMXView view) { MXConsoleContainer.Back(view); } } public class MXConsoleContainer : MXContainer { public MXConsoleContainer(MXApplication theApp) : base(theApp) { } private class NavDetail { public string Path { get; set; } public Dictionary<string, string> Parameters { get; set; } public NavDetail(string path, Dictionary<string, string> parameters) { Path = path; Parameters = parameters; } } static Stack<NavDetail> NavHistory = new Stack<NavDetail>(); public static void Initialize(MXApplication theApp) { MXContainer.InitializeContainer(new MXConsoleContainer(theApp)); // non-threaded container, not needed as all input is blocking (old-school) MXContainer.Instance.ThreadedLoad = false; } public static void Back(IMXView view) { // exit if we try to go back too far if (!CanGoBack()) { Environment.Exit(0); } else { // pop off the current view NavHistory.Pop(); // prepare to re-push the current view NavDetail backTo = NavHistory.Pop(); // re-display the view Navigate(view, backTo.Path, backTo.Parameters); } } public static bool CanGoBack() { if (NavHistory.Count > 1) return true; else return false; } protected override void OnControllerLoadComplete( IMXView fromView, IMXController controller, MXViewPerspective perspective) { // store of the stack for later NavHistory.Push(new NavDetail(controller.Uri, controller.Parameters)); // render the view MXConsoleContainer.RenderViewFromPerspective(controller, perspective); } protected override void OnControllerLoadFailed(IMXController controller, Exception ex) { System.Console.WriteLine(“Failed to load controller: “ + ex.Message); System.Console.WriteLine(“Stack Dump”); System.Console.WriteLine(ex.StackTrace.ToString()); System.Diagnostics.Debug.WriteLine(“Failed to load controller: “ + ex.Message); System.Diagnostics.Debug.WriteLine(“Stack Dump”); System.Diagnostics.Debug.WriteLine(ex.StackTrace.ToString()); } public override void Redirect(string url) { Navigate(null, url); CancelLoad = true; } } }
next shows the code for initializing your container. You need to start by providing it an instance of your shared app. You also register the views for your application by calling the AddView() method on the container and passing a new instance of each view
to be placed in the view map. In this case, you register two views: The first displays a list of customers in the system and the second displays the details of a single customer, which is an instance of the CustomerView class discussed earlier in this chapter.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using MonoCross.Navigation; using MonoCross.Console; using CustomerManagement; using CustomerManagement.Controllers; using CustomerManagement.Shared; using CustomerManagement.Shared.Model; namespace CustomerManagement.Console { class Program { static void Main(string[] args) { // initialize container MXConsoleContainer.Initialize(new CustomerManagement.App()); // customer list view MXConsoleContainer.AddView<List<Customer>>(new Views.CustomerListView(), ViewPerspective.Default); // customer view and customer edit/new MXConsoleContainer.AddView<Customer>(new Views.CustomerView(), ViewPerspective.Default); MXConsoleContainer.AddView<Customer>(new Views.CustomerEdit(), ViewPerspective.Update); // navigate to first view MXConsoleContainer.Navigate(MXContainer.Instance.App.NavigateOnLoad); } } }
一个controller会对应一个view 所以这里初始化view的数目要和navigationMap中德controller的一样,而且名字最好对应上,增加可读性。注意到AddView函数里面的第二个参数是ViewPerspective的,下面就说说这个参数
Working with View Perspectives
A view perspective is simply a string that describes the intended purpose of your view.
public static class ViewPerspective { public const string Default = “”; public const string Read = “GET”; public const string Create = “POST”; public const string Update = “PUT”; public const string Delete = “DELETE”; }
View perspectives are used to uniquely register views in your container and are also used by your controllers to indicate to the container which view to render at a given point in your application.
view perspective value to your container, and the desired view renders.