前言:
该技术前提是 已经掌握一定程度的mvvmlight for win8 技巧
实现思路:
通过Messenger 机制 动态发送导航参数 到 需要导航的page 完成导航
导航参数:
public class NavigateParameter { private string pageType; public string PageType { get { return pageType; } set { pageType = value; } } private object parameter; public object Parameter { get { return parameter; } set { parameter = value; } } }
在 OnNavigated 到首页的地方 加入以下方法 注册 该处理通道
protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); this.prContent.IsIndeterminate = true; this.ContentFrame.Navigate(typeof(MainPage)); this.TopFrame.Navigate(typeof(VideosPage)); #region Messengers 消息处理中心 Messenger.Default.Register<NavigateParameter>(this, "MenuNavigate", async s => { var type = await Task.Run<Type>(() => { var fullName = "IntTourism.View." + s.PageType; return Type.GetType(fullName); }); if (null != s.Parameter) await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () => this.ContentFrame.Navigate(type, s.Parameter)); else await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () => this.ContentFrame.Navigate(type)); }); Messenger.Default.Register<NavigateParameter>(this, "TopFrameNavigate", async s => { var type = await Task.Run<Type>(() => { var fullName = "IntTourism.View." + s.PageType; return Type.GetType(fullName); }); await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () => { if (!TopFrame.CurrentSourcePageType.Equals(type)) if (null != s.Parameter) this.TopFrame.Navigate(type, s.Parameter); else this.TopFrame.Navigate(type); }); }); #endregion }
Unloaded 注销
private void LayoutAwarePage_Unloaded(object sender, RoutedEventArgs e) { Messenger.Default.Unregister<NavigateParameter>(this, "MenuNavigate"); Messenger.Default.Unregister<LogBean>(this, "LogError"); Messenger.Default.Unregister<LogBean>(this, "TopBusy"); Messenger.Default.Unregister<LogBean>(this, "ContentBusy"); Messenger.Default.Unregister<LogBean>(this, "TopFrame"); this.btnCancel.PointerPressed -= SendError; this.btnSend.PointerPressed -= SendError; }
以下为xaml 里 gridview 菜单的binding方式
注意:ItemClick 此处通过eventTocommand 将事件与 viewmodel中相应的命令进行了binding处理 请结合我上一篇文章 对EventTocomand这一概念进行理解
<common:LayoutAwarePage x: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:i="using:IntTourism.Com.Transvalue.Tools" xmlns:ignore="http://www.ignore.com" xmlns:common="using:IntTourism.Common" xmlns:cmd="using:IntTourism" mc:Ignorable="d ignore" d:DesignHeight="1080" d:DesignWidth="1080" DataContext="{Binding Main, Source={StaticResource Locator}}" NavigationCacheMode="Required"> <Viewbox> <Grid Width="1080" Height="1080" x:Name="root"> <GridView HorizontalAlignment="Left" VerticalAlignment="Top" Width="1080" Height="1080" CanDragItems="false" IsRightTapEnabled="False" ScrollViewer.HorizontalScrollBarVisibility="Hidden" IsSwipeEnabled="False" x:Name="gv" ItemsSource="{Binding MenuItems}" SelectionMode="None" > <GridView.Transitions> <TransitionCollection> <EntranceThemeTransition/> </TransitionCollection> </GridView.Transitions> <GridView.ItemContainerTransitions> <TransitionCollection> <EntranceThemeTransition/> </TransitionCollection> </GridView.ItemContainerTransitions> <GridView.HeaderTransitions> <TransitionCollection> <EntranceThemeTransition/> </TransitionCollection> </GridView.HeaderTransitions> <GridView.ItemTemplate> <DataTemplate> <GridViewItem> <Image Height="280" Width="280" Source="{Binding IconUrl}"/> <i:EventToCommandCollection.Items> <i:EventToCommand Command="{Binding MenuCMD}" CommandParameter="{Binding NavigateParameter}" NavigateUrl="{Binding NavigateUrl}" Event="ItemClick"/> </i:EventToCommandCollection.Items> </GridViewItem> </DataTemplate> </GridView.ItemTemplate> <GridView.ItemsPanel> <ItemsPanelTemplate> <VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,80,0" ItemWidth="300" ItemHeight="300" MaximumRowsOrColumns="10"/> </ItemsPanelTemplate> </GridView.ItemsPanel> </GridView> </Grid> </Viewbox> </common:LayoutAwarePage>
下面这一段是ViewModel中对命令的处理,以及菜单的动态加载
using GalaSoft.MvvmLight; using IntTourism.Com.Transvalue.Service; using GalaSoft.MvvmLight.Command; using System.Diagnostics; using Windows.UI.Xaml.Controls; using System.Collections; using System.Collections.ObjectModel; using IntTourism.Com.Transvalue.Model; using GalaSoft.MvvmLight.Messaging; using System; using System.ComponentModel; using System.Collections.Generic; using IntTourism.Com.Transvalue.Bean; using IntTourism.Com.Transvalue.Tools; using IntTourism.Com.Transvalue.Const; using System.Threading.Tasks; namespace IntTourism.ViewModel { /// <summary> /// This class contains properties that the main View can data bind to. /// <para> /// See http://www.galasoft.ch/mvvm /// </para> /// </summary> public class MainViewModel : ViewModelBase { private readonly IMainPageSerivce _dataService; #region Command private RelayCommand<ExCommandParameter> _menuCMD; /// <summary> /// Gets the MenuCMD. /// </summary> public RelayCommand<ExCommandParameter> MenuCMD { get { return _menuCMD ?? (_menuCMD = new RelayCommand<ExCommandParameter>( ExecuteMenuCMD, CanExecuteMenuCMD)); } } private async void ExecuteMenuCMD(ExCommandParameter url) { await Task.Run(() => { var nav = new NavigateParameter(); nav.Parameter = url.Parameter; nav.PageType = url.NavigateUrl; if (null != url.NavigateUrl) { Messenger.Default.Send<bool>(true, "ContentBusy"); Messenger.Default.Send<NavigateParameter>(nav, "MenuNavigate"); } }); } private bool CanExecuteMenuCMD(ExCommandParameter url) { if (null != url && !"".Equals(url)) return true; else return false; } #endregion #region DataProperties #region MenuItems /// <summary> /// The <see cref="MenuItems" /> property's name. /// </summary> public const string MenuItemsPropertyName = "MenuItems"; private ObservableCollection<MenuItem> _menuItems; /// <summary> /// Sets and gets the MenuItems property. /// Changes to that property's value raise the PropertyChanged event. /// This property's value is broadcasted by the MessengerInstance when it changes. /// </summary> public ObservableCollection<MenuItem> MenuItems { get { return _menuItems; } set { if (_menuItems == value) { return; } RaisePropertyChanging(() => MenuItems); var oldValue = _menuItems; _menuItems = value; RaisePropertyChanged(() => MenuItems, oldValue, value, true); } } #endregion /// <summary> /// The <see cref="WelcomeTitle" /> property's name. /// </summary> public const string WelcomeTitlePropertyName = "WelcomeTitle"; private string _welcomeTitle; /// <summary> /// Sets and gets the WelcomeTitle property. /// Changes to that property's value raise the PropertyChanged event. /// This property's value is broadcasted by the MessengerInstance when it changes. /// </summary> public string WelcomeTitle { get { return _welcomeTitle; } set { if (_welcomeTitle == value) { return; } RaisePropertyChanging(WelcomeTitlePropertyName); var oldValue = _welcomeTitle; _welcomeTitle = value; RaisePropertyChanged(WelcomeTitlePropertyName, oldValue, value, true); } } #endregion /// <summary> /// Initializes a new instance of the MainViewModel class. /// </summary> public MainViewModel(IMainPageSerivce dataService) { _dataService = dataService; InitPropertyChanged(); _dataService.GetItemData( async (items, error) => { if (await Pollute.ReportError(error, "GetItemData", ErrorDesc._E01)) { Messenger.Default.Send<bool>(false, "ContentBusy"); return; } var data = await items; foreach (var item in data) item.MenuCMD = MenuCMD; MenuItems = data; Messenger.Default.Send<bool>(false, "ContentBusy"); }); } #region PropertyChangeMethods /// <summary> /// 初始化相关方法 /// </summary> private void InitPropertyChanged() { PropertyChanging += PropertyChangingMethod; PropertyChanged += PropertyChangedMethod; } //private static Dictionary<Predicate<string>, Action> PropertyChangingActions; //private static Dictionary<Predicate<string>, Action> PropertyChangedActions; private void PropertyChangedMethod(object sender, PropertyChangedEventArgs e) { } private void PropertyChangingMethod(object sender, PropertyChangingEventArgs e) { } #endregion public override void Cleanup() { // Clean up if needed base.Cleanup(); } } }
菜单实例数据:
using System; using System.Collections.ObjectModel; using System.Threading.Tasks; using IntTourism.Com.Transvalue.Bean; using IntTourism.Com.Transvalue.Model; using IntTourism.Com.Transvalue.Service; namespace IntTourism.Com.Transvalue.Service.Impl { public class MainPageService : IMainPageSerivce { public void GetData(Action<DataItem, Exception> callback) { var item = new DataItem("Welcome to MVVM Light"); callback(item, null); } public void GetItemData(Action<Task<ObservableCollection<MenuItem>>, Exception> callback) { var result = Task.Run(() => { var items = new ObservableCollection<MenuItem>(); var menu1 = new MenuItem(); menu1.IconUrl = "../Assets/ICON/交通.png"; menu1.NavigateUrl = "IntMaps"; items.Add(menu1); var menu2 = new MenuItem(); menu2.IconUrl = "../Assets/ICON/娱乐.png"; menu2.NavigateUrl = "IntMaps"; menu2.NavigateParameter = "Play"; items.Add(menu2); var menu3 = new MenuItem(); menu3.IconUrl = "../Assets/ICON/景点.png"; menu3.NavigateUrl = "SceneViewGroup"; items.Add(menu3); var menu4 = new MenuItem(); menu4.IconUrl = "../Assets/ICON/购物.png"; menu4.NavigateUrl = "IntMaps"; menu4.NavigateParameter = "Buy"; items.Add(menu4); var menu5 = new MenuItem(); menu5.IconUrl = "../Assets/ICON/专题旅游.png"; menu5.NavigateUrl = "SceneViewGroup"; menu5.NavigateParameter = "Travel"; items.Add(menu5); var menu6 = new MenuItem(); menu6.IconUrl = "../Assets/ICON/实用信息.png"; items.Add(menu6); var menu7 = new MenuItem(); menu7.IconUrl = "../Assets/ICON/武汉之最.png"; items.Add(menu7); var menu8 = new MenuItem(); menu8.IconUrl = "../Assets/ICON/住宿.png"; menu8.NavigateUrl = "IntMaps"; menu8.NavigateParameter = "Rest"; items.Add(menu8); var menu9 = new MenuItem(); menu9.IconUrl = "../Assets/ICON/推荐线路.png"; items.Add(menu9); //var menu10 = new MenuItem(); //menu10.IconUrl = "../Assets/ICON/武汉介绍.png"; //items.Add(menu10); var menu11 = new MenuItem(); menu11.IconUrl = "../Assets/ICON/周边旅游.png"; menu11.NavigateUrl = "IntMaps"; menu11.NavigateParameter = "AllTravel"; items.Add(menu11); var menu12 = new MenuItem(); menu12.IconUrl = "../Assets/ICON/旅游动态.png"; items.Add(menu12); var menu13 = new MenuItem(); menu13.IconUrl = "../Assets/ICON/美图欣赏.png"; items.Add(menu13); var menu14 = new MenuItem(); menu14.IconUrl = "../Assets/ICON/天气预报.png"; items.Add(menu14); var menu15 = new MenuItem(); menu15.IconUrl = "../Assets/ICON/旅游咨询.png"; items.Add(menu15); var menu16 = new MenuItem(); menu16.IconUrl = "../Assets/ICON/美食.png"; menu16.NavigateUrl = "IntMaps"; menu16.NavigateParameter = "Eat"; items.Add(menu16); return items; }); callback(result, null); } } }
以上sample 数据 可以存入数据库中 进行动态配置,完成菜单的动态显示,降低了程序的耦合,增加了可配置性。
一、安装Wax
当前,我们推荐你使用Xcode3创建Wax项目。Xcode4的项目模板仍然有些问题,因此请使用Xcode3。
1. 从http://github.com/probablycorey/wax/下载Wax或者用git命令将它复制到本地
2. 在shell中,转到wax文件夹,输入rake install命令。这将安装xcode项目模板。
3. 你知道Lua吗?如果你熟悉Javascript、Python、Runby之类的动态语言,你通过5分钟的学习就能入门。你可以阅读这些免费教程或者买本《Programingin Lua》。
二、创建新项目
三、开始使用Lua
- 并不需要在Xcode中编写Lua代码,它们全都在APP_ROOT/scripts目录下,直接用你喜欢的文本编辑工具编辑它们好了。Wax标准库文件位于APP_ROOT/wax/lib/stdlib目录下。这些全是你会用到的代码!
- 如果你使用TextMate,从APP_ROOT目录下执行rake tm,以便从项目打开。还可以安装wax-bundle已支持快捷键。
- 如果你迷恋于Xcode,你可以试试 capgo.com's 的语法加亮插件。
- 如果你使用的是vi或Emacs,你总不会连Lua支持都不会装吧?
- 要创建一个TableView,首先创建一个文件: APP_ROOT/scripts/MyTableViewController.lua
- 要创建新的OC控制器类,使用:
waxClass{"MyTableViewController",UITableViewController}
- 现在实现init函数,在里面设置tableView的内容并调用父类的init方法(正如你在OC中所做的一样)。
function init(self)
self.super:initWithStyle(UITableViewStylePlain)
-- Here are the tableView'scontents
self.things ={"Planes", "Trains", "Automobiles"}
return self
end
- 接下来实现UIDataSource协议方法。最终 MyTableViewController.lua 文件如下所示:
waxClass{"MyTableViewController",UITableViewController}
function init(self)
self.super:initWithStyle(UITableViewStylePlain)
-- Here are thetableView's contents
self.things ={"Planes", "Trains", "Automobiles"}
return self
end
functionnumberOfSectionsInTableView(self, tableView)
return 1
end
functiontableView_numberOfRowsInSection(self, tableView, section)
return #self.things
end
functiontableView_cellForRowAtIndexPath(self, tableView, indexPath)
local identifier ="MyTableViewControllerCell"
local cell =tableView:dequeueReusableCellWithIdentifier(identifier) or
UITableViewCell:initWithStyle_reuseIdentifier(UITableViewCellStyleDefault,identifier)
local thing =self.things[indexPath:row() + 1] -- Must +1 because Lua arrays are 1 based
cell:textLabel():setText(thing)
return cell
end
- 最后就是创建MyTableViewController示例并加到主窗口中。这需要修改APP_ROOT/scripts/AppDelegate.lua。它跟我们在OC程序中的UIApplicationDelegate是一样的。
require"MyTableViewController"
waxClass{"AppDelegate",protocols = {"UIApplicationDelegate"}}
functionapplicationDidFinishLaunching(self, application)
local frame =UIScreen:mainScreen():bounds()
self.window =UIWindow:initWithFrame(frame)
self.controller =MyTableViewController:init()
self.window:addSubview(self.controller:view())
self.window:makeKeyAndVisible()
end
- 运行程序…你已经用Lua创建了一个真正的UITableView!这真是太好了。
- 在这里下载framework:https://github.com/downloads/probablycorey/wax/wax.framework.zip
1. 用Xcode打开项目,将wax.framework拖到Xcode的frameworks组下。确保勾选"Copy items into destination group's folder"。
2. 新建init.lua(确保加到了应用程序束中)。在文件中加入代码:
puts("ZOMG, LUA IS RUNNING")
puts("Here is Lua talking to ObjC%s", tostring(UIApplication:sharedApplication()))
3. 打开AppDelegate文件,导入wax头文件:
#import "wax/wax.h"
4. 在AppDelegate的application:didFinishLaunchingWithOptions:方法中加入:
wax_start("init.lua",nil);
// To add wax with extensions, use thisline instead
// #import "wax/wax_http.h"
// #import "wax/wax_json.h"
// #import"wax/wax_filesystem.h"
// wax_start("init.lua",luaopen_wax_http, luaopen_wax_json, luaopen_wax_filesystem, nil);
最后,build and run,你将在Xcode控制台重看到Lua输出的内容。
package com.testasyntextview; /** * 把获取的线程写到方法中(比较好) */ import android.app.Activity; import android.app.ProgressDialog; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.Html; import android.text.Spanned; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MethodTestAsynTextViewActivity extends Activity { private TextView textView1; private Button button1; private Context context; private ProgressDialog progressDialog; private Spanned html; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); context = this; textView1 = (TextView) findViewById(R.id.textView1); button1 = (Button) findViewById(R.id.button1); button1.setOnClickListener(l); } private OnClickListener l = new OnClickListener() { @Override public void onClick(View v) { progressDialog = ProgressDialog.show(context, "获取数据中", "等待"); getHtmlDate(); } }; private void getHtmlDate() {// 获取数据,把线程写入了其中 new Thread() { public void run() { Message msg = myHandler.obtainMessage(); try { html = HttpUtil.fromHtml(HttpUtil .getHtml("http://wap.sina.com")); msg.what = 0; } catch (Exception e) { e.printStackTrace(); msg.what = 1; } myHandler.sendMessage(msg); } }.start(); } Handler myHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case 0: textView1.setText(html); progressDialog.dismiss(); break; case 1: textView1.setText("当前无数据"); progressDialog.dismiss(); break; } super.handleMessage(msg); } }; }
package com.testasyntextview; /** * 使用AsyncTask类 */ import android.app.Activity; import android.app.ProgressDialog; import android.content.Context; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.Html; import android.text.Spanned; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class TestAsynTextViewActivity extends Activity { private TextView textView1; private Button button1; private Context context; private ProgressDialog progressDialog; private Spanned html; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); context = this; progressDialog = new ProgressDialog(context); progressDialog.setTitle("进度条"); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); textView1 = (TextView) findViewById(R.id.textView1); button1 = (Button) findViewById(R.id.button1); button1.setOnClickListener(l); } private OnClickListener l = new OnClickListener() { @Override public void onClick(View v) { new InitTask().execute("http://wap.sina.com", "http://wap.baidu.com"); } }; private void getHtmlDate(String url) {// 获取数据,把线程写入了其中 try { html = HttpUtil.fromHtml(HttpUtil.getHtml(url)); } catch (Exception e) { e.printStackTrace(); } } /** * When an asynchronous task is executed, the task goes through 4 steps: * * onPreExecute(), * invoked on the UI thread immediately after the task is * executed. This step is normally used to setup the task, for instance by * showing a progress bar in the user interface. * * doInBackground(Params...), * invoked on the background thread immediately after onPreExecute() * finishes executing. This step is used to perform background computation * that can take a long time. The parameters of the asynchronous task are * passed to this step. The result of the computation must be returned by * this step and will be passed back to the last step. This step can also * use * * publishProgress(Progress...) to publish one or more units of * progress. These values are published on the UI thread, in the * * onProgressUpdate(Progress...) step. onProgressUpdate(Progress...), * invoked on the UI thread after a call to publishProgress(Progress...). * The timing of the execution is undefined. This method is used to display * any form of progress in the user interface while the background * computation is still executing. For instance, it can be used to animate a * progress bar or show logs in a text field. onPostExecute(Result), invoked * on the UI thread after the background computation finishes. The result of * the background computation is passed to this step as a parameter. */ class InitTask extends AsyncTask<String, Integer, Long> { protected void onPreExecute() { progressDialog.show(); super.onPreExecute(); } protected Long doInBackground(String... params) {// Long是结果 String 是入口参数 getHtmlDate(params[0]);// 可以运行两个任务 publishProgress(50);// 发送进度50% getHtmlDate(params[1]); publishProgress(100);// 发送进度100% return (long) 1; } @Override protected void onProgressUpdate(Integer... progress) { progressDialog.setProgress(progress[0]);// 设置进度 super.onProgressUpdate(progress); Log.e("测试", progress[0] + ""); } @Override protected void onPostExecute(Long result) { setTitle(result + "测试"); textView1.setText(html); progressDialog.dismiss(); super.onPostExecute(result); } } }
package com.testasyntextview; /** * 使用Runable */ import android.app.Activity; import android.app.ProgressDialog; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.Html; import android.text.Spanned; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class RunableTestAsynTextViewActivity extends Activity { private TextView textView1; private Button button1; private Context context; private ProgressDialog progressDialog; private Spanned html; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); context = this; textView1 = (TextView) findViewById(R.id.textView1); button1 = (Button) findViewById(R.id.button1); button1.setOnClickListener(l); } private OnClickListener l = new OnClickListener() { @Override public void onClick(View v) { progressDialog = ProgressDialog.show(context, "获取数据中", "等待"); new Thread(new ThreadDemo()).start(); } }; private void getHtmlDate() { try { html = HttpUtil.fromHtml(HttpUtil.getHtml("http://wap.sina.com")); } catch (Exception e) { e.printStackTrace(); } } class ThreadDemo implements Runnable {//一个runable public void run() { getHtmlDate(); myHandler.sendEmptyMessage(0); } } Handler myHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case 0: textView1.setText(html); progressDialog.dismiss(); break; case 1: textView1.setText("当前无数据"); progressDialog.dismiss(); break; } super.handleMessage(msg); } }; }
package com.testasyntextview; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import android.graphics.drawable.Drawable; import android.text.Html; import android.text.Spanned; public class HttpUtil { public static String getHtml(String path) throws Exception { URL url = new URL(/blog_article/path/index.html); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5 * 1000); InputStream inStream = conn.getInputStream();// 通过输入流获取html数据 byte[] data = readInputStream(inStream);// 得到html的二进制数据 String html = new String(data, "utf-8"); return html; } public static byte[] readInputStream(InputStream inStream) throws Exception { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outStream.write(buffer, 0, len); } inStream.close(); return outStream.toByteArray(); } public static Spanned fromHtml(String html) { Spanned sp = Html.fromHtml(html, new Html.ImageGetter() { @Override public Drawable getDrawable(String source) { InputStream is = null; try { is = (InputStream) new URL(/blog_article/source/index.html).getContent(); Drawable d = Drawable.createFromStream(is, "src"); d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); is.close(); return d; } catch (Exception e) { return null; } } }, null); return sp; } }