MSN Onit上线
经过Red/SAFI团队近半年的设计开发,MSN Onit应用终于上线了。包括iPad iPhone及WP7三个平台同时上线提供下载,是一个专注于提供男性时尚生活内容的电子杂志,产品基于Windows Azure+App的结构,下载地址
相关文章:
经过Red/SAFI团队近半年的设计开发,MSN Onit应用终于上线了。包括iPad iPhone及WP7三个平台同时上线提供下载,是一个专注于提供男性时尚生活内容的电子杂志,产品基于Windows Azure+App的结构,下载地址
相关文章:
新版的SilverlightSpy目前已经支持对Windows Phone7的应用进行调试了,可以在界面中查看大概的内存占用以及每个页面当前渲染的UI组件等信息,非常有帮助。
ViewModel处理数据的载入事件,所以最清楚在什么情况下出现进度条,因此最佳的方式是由ViewModel来负责动态改变一个进度条显示与否的事件,在前端进行有效的数据绑定,我们首先在homeVm中设置相应的属性:
private Visibility _loadProgressVisibility;
public Visibility LoadProgressVisibility
{
get { return _loadProgressVisibility; }
set {
_loadProgressVisibility = value;
RaisePropertyChanged("LoadProgressVisibility");
}
}
//Show / hide progress bar methods
private void StartLoadProgress(bool isLoad)
{
if(isLoad)
{
LoadProgressVisibility = Visibility.Visible;
}
else
{
LoadProgressVisibility = Visibility.Collapsed;
}
}
然后在前端画面中放置ProgressBar控件,注意勾选上IsIndeterminate状态,表示显示为连续动画,而不是百分的形式:
同时将进度条的Visibility属性与VM中的相应数据进行绑定:
为了进一步改善进度条的性能,还有一些工作要做,Windows Phone 7 内置的Progress Bar的控件的动画性能并不令人满意,有时候会出现卡死,相应的解释参考这篇文章The high performance ProgressBar for Windows Phone。为了解决这个问题,我们可以用此文中提到的方法,引用文中提到的控件与样式,并将相应的Style文件放置到我们的App.xaml之中,我们只需要给我们的ProgressBar的xaml加一个样式:
<ProgressBar
VerticalAlignment="Top" IsIndeterminate="True"
Visibility="{Binding LoadProgressVisibility}"
Style="{StaticResource PerformanceProgressBar}"
/>
为了配合应用的风格,我们也可以使用Blend修改进度条的外观,以下是制作完成后,并将前景色与我们skin中的前景色绑定的效果:
在Home中打算实现一个按钮用来随机变化背景的颜色。在这里我们先将home.xaml中系统自动为我们生成的有关Application Bar的预置代码取消注释:
<!-- Sample code showing usage of ApplicationBar-->
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBarIconButton x:Name="appbar_button1" IconUri="/Images/appbar_button1.png" Text="Button 1"></shell:ApplicationBarIconButton>
<shell:ApplicationBarIconButton x:Name="appbar_button2" IconUri="/Images/appbar_button2.png" Text="Button 2"></shell:ApplicationBarIconButton>
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem x:Name="menuItem1" Text="MenuItem 1"></shell:ApplicationBarMenuItem>
<shell:ApplicationBarMenuItem x:Name="menuItem2" Text="MenuItem 2"></shell:ApplicationBarMenuItem>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
在Blend的界面下我们可以直接修改Application Bar的外观和内容,我们可以使用自己的PNG图片作为图标,但注意一定要符合Windows Phone 7 Design Guideline的规格,SDK中提供的默认图标储存在
C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.0\Icons\dark
我们可以基于默认的PNG的大小48×48象素制作自己的图标:
由于目前版本的Application Bar不可以附加MVVM Light提供的Command,所以我们必须通过标准的Click事件进行中转,代码如下:
[xaml]
[/xaml]
private void appbar_changeSkin_Click(object sender, System.EventArgs e)
{
var vm = DataContext as HomeVm;
if(vm!=null)
{
//Execute Command
vm.ClickChangeSkinCommand.Execute(null);
}
}
这样在VM中通过ClickChangeSkinCommand方法调用随机颜色的方法,就实现了简单的动态换肤效果。
WP7页面间的跳转已经有很多资料,这里我们了解一下如何使用MVVM Light框架来封装我们的Command实现一些特定的操作。首先按设计稿我们准备好了DetailView,在这个页面的制作过程中我们使用了自定义的字体,使用方法是在Blend中,将字体文件作为资源加入到项目之中:
这样我们在文本的字体选择中就能看见新加载的字体了。需要说明的是,Windows Phone 7对OTF的支持比较差,一般来说用TTF的字体就没有什么问题。
回到Home.xaml,我们将用于承现Theme的项目改为Button,用于接受用户的tap/click事件:
<DataTemplate x:Key="NewestThemeItemTemplate">
<Button>
<StackPanel Margin="0,0,0,20">
...
</StackPanel>
</Button>
</DataTemplate>
同时我们设计了一个没有边框的Button Style用于所有需要点击的按钮,我们将这个样式放置在App.xaml层级,用于给其它页面也能重复使用:
<Button Style="{StaticResource SimpleButtonStyle}">
<!--in App.xaml-->
<Style x:Key="SimpleButtonStyle" TargetType="Button">
<Setter Property="Padding" Value="0"/>
<Setter Property="FontSize" Value="{StaticResource PhoneFontSizeNormal}"/>
<Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilyNormal}"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
然后在HomeVm.cs建立RelayCommand的命令
public RelayCommand<ThemeItem> ClickThemeCommand { get; private set; }
private void OnClickThemeCommand(ThemeItem item)
{
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(new Uri("/Views/Detail.xaml",UriKind.Relative));
}
//同时在构建中加入初始化方法
ClickThemeCommand = new RelayCommand<ThemeItem>(OnClickThemeCommand);
在Home.xaml中,进行相应的命令与按钮的绑定:
<Button Style="{StaticResource SimpleButtonStyle}"
cmd:ButtonBaseExtensions.Command="{Binding Home.ClickThemeCommand,Source={StaticResource Locator}}"
cmd:ButtonBaseExtensions.CommandParameter="{Binding}"
>
这样,在点击首页的newest中的每个Item时,页面就可以顺利的导向下一个Detail.xaml了。
同时,我们可以在HomeVm中增加参数来保存用户点击的某个颜色对象。当打开Detail视图时,通过ViewModelLocator去获得这个对象,即可显示当前的Theme数据:
Theme = ViewModelLocator.Home.GetCurrentTheme();
在这个Detail视图中我们还需要根据主题的id继续访问Kuler的API去获得一个评论列表,调用API的方式与以前一样,这样就完成了我们的Detail视图。
有了原型文件之后,能方便的检查实现效果及上机调试,接下来的工作就变得更加简单,我们只需要为运行时准备合适的数据就可以了。所以在这篇中的目录是去访问网络获得我们真正需要的数据。这里我们将采用的是微软的一个新的异步调用框架 Reactive Extensions for .NET (Rx) 该框架已经被内置到了Windows Phone 7 的SDK之中,我们可以直接采用。
我们在引用中加入Microsoft.Phone.Reactive以及System.Observable:
继续编写HomeVm.cs,将运行时的数据改为运行BuildData方法,具体实现如下:
private void BuildData()
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://kuler-api.adobe.com/rss/get.cfm?listtype=recent&key=***YOUR KULER API KEY****");
var response = Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)();
response.Subscribe(OnGetNewestThemesData);
}
XNamespace ns = "http://kuler.adobe.com/kuler/API/rss/";
public void OnGetNewestThemesData(WebResponse res)
{
using(var responseStream = res.GetResponseStream())
{
var sr = new StreamReader(responseStream, Encoding.UTF8);
var result = sr.ReadToEnd();
//Parse XML data;
var xml = XDocument.Parse(result);
var query = (from c in xml.Descendants(ns+"themeItem")
select new ThemeItem
{
Id = c.Element(ns + "themeID").Value,
Title = c.Element(ns + "themeTitle").Value,
Author = c.Element(ns + "themeAuthor").Element(ns + "authorLabel").Value,
AuthorId = c.Element(ns + "themeAuthor").Element(ns + "authorID").Value,
Tags = c.Element(ns + "themeTags").Value,
Rating = c.Element(ns + "themeRating").Value,
DownloadCount = c.Element(ns + "themeDownloadCount").Value,
CreatedAt = c.Element(ns + "themeCreatedAt").Value,
ThemeSwitches = (from s in c.Element(ns +"themeSwatches").Descendants(ns +"swatch")
select s.Element(ns + "swatchHexColor").Value
).ToList()
}
).ToList();
Debug.WriteLine("Parse XML Success..");
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
foreach (var item in query) {NewestThemes.Add(item);}
RandomAppTheme();
});
}
}
记得修改有关设计时数据的判断:
if (IsInDesignMode)
{
BuildSampleData();
}
else
{
BuildData();
}
这样,在再次预览我们程序的的时候,我们的数据已经变为了真实的数据了:
构建原型指的是构建基于XAML语言,可用于实际应用开发的前端资源文件。一方面当所有的设计稿转化为XAML后,能直接交给程序员进行开发,同时由于XAML语言本身具备很强的交互能力,能够让我们快速的预览应用的最终效果,帮助我们调整用户体验与UI细节。
整个过程有点像网站开发中的制作HTML的过程,但由于XAML语言有更强的扩展性和技术细节,一个原型应用我认为可以有几种完全不同的制作思路:
静态XAML就不用多说,完全是将设计稿进行1:1的转换为XAML语言。模板化与控件化的XAML页面是指在制作的过程中就能够根据最终的应用程序进行一些有效的样式提取,拥有更好的控件复用,样式重用等特性。
而基于MVVM模式构建的XAML,是目前最理想的方式。MVVM是近几年Silverlight开发中比较突出调强的一种开发模式。其基本原则是基于传统的MVC模式,让样式与数据与控制进行有效的分离。而由于基于Silverlight开发中突出的XAML表现层语言的特性,MVVM引入了全新的ViewModel的概念,通过ViewModel与XAML之间的数据绑定,来帮助快速的进行页面的呈现,是一种非常高效的开发方式。
同时,包括微软和其它开发者在不断开发各种有效的MVVM框架,使得MVVM模式还可以与Expression Blend进行有效的协同,使得在设计时就能够预览最终效果,进一步增强了设计到开发转化的工作流。有关MVVM的细则在这个系列中不再重复。我们会通过下面的过程来认识这样一种开发模式。
在这里我们选用的是一个比较轻量和简单的MVVM Light Toolkit框架。MVVM Light经过几个版本的开发,目前已经提供了Windows Phone 7的模板,可以在Codeplex下载。
如果你正确的按照安装指南安装了MVVM Light Toolkit的话,那么在Visual Studio中将能够直接创建基于MVVM Light的WP7工程文件:
Visual Studio默认创建了MainViewPage.xaml在项目中作为我们的应用入口。我们在项目中创建一个新的文件夹叫Views来放置我们所有的View文件,我们在新建的Views文件夹上右键单击选New Item,在接下来的对话框中选择MvvmView(WP7),命名为Home.xaml:
我们打开位于项目的Properties/WMAppManifest.xml,将位于DefaultTask中的NavigationPage属性批量向/Views/Home.xaml:
接下来我们可以用Expression Blend来可视化的构建Home页面的UI布局:
[Placeholder]
Home视图主要采用Panorama Control,有关此控件的用法都可以从其它地方找到。接下来我们就要开始设计针对Home视图所使用的ViewModel。所谓ViewModel实际上就是用于某个View的数据模型。该模型的设计目标是保证View上的所有动态信息能够获得正确的数据,以及承担数据载入、刷新等操作。我们可以在ViewModel目录下,建立一个命为HomeVm的MvvmViewModel(WP7)项目:
通过对首页的UI设计的检查我们发现首页主要需要获得二个List类型的数据 ,一个是newest,另一个是most popular。而其下面所承载的数据都是单个的颜色方案(Theme)。我们通过检查Kuler的API来看看每一个颜色方案(Theme)的数据结构:
<kuler:themeItem>
<kuler:themeID>1175537</kuler:themeID>
<kuler:themeTitle>Friends and foes</kuler:themeTitle>
<kuler:themeImage>http://kuler-api.adobe.com/kuler/themeImages/theme_1175537.png</kuler:themeImage>
<kuler:themeAuthor>
<kuler:authorID>254539</kuler:authorID>
<kuler:authorLabel>syork</kuler:authorLabel>
</kuler:themeAuthor>
<kuler:themeTags/>
<kuler:themeRating>4</kuler:themeRating>
<kuler:themeDownloadCount>1128</kuler:themeDownloadCount>
<kuler:themeCreatedAt>20110105</kuler:themeCreatedAt>
<kuler:themeEditedAt>20110105</kuler:themeEditedAt>
<kuler:themeSwatches>
<kuler:swatch>
<kuler:swatchHexColor>2F2933</kuler:swatchHexColor>
<kuler:swatchColorMode>rgb</kuler:swatchColorMode>
<kuler:swatchChannel1>0.185839</kuler:swatchChannel1>
<kuler:swatchChannel2>0.161431</kuler:swatchChannel2>
<kuler:swatchChannel3>0.2</kuler:swatchChannel3>
<kuler:swatchChannel4>0.0</kuler:swatchChannel4>
<kuler:swatchIndex>0</kuler:swatchIndex>
</kuler:swatch>
<kuler:swatch/>
<kuler:swatch/>
<kuler:swatch/>
<kuler:swatch/>
</kuler:themeItem>
通过API中返回数据的情况,我们可以实现自己的Theme的对象:
public class ThemeItem
{
public string Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string AuthorId { get; set; }
public string Tags { get; set; }
public string Rating { get; set; }
public string DownloadCount { get; set; }
public string CreatedAt { get; set; }
public List<string> ThemeSwitches { get; set; }
public ThemeItem()
{
ThemeSwitches = new List<string>();
}
}
然后我们在HomeVm中引用我们的对象,形成用于Home的数据结构:
public class HomeVm : ViewModelBase
{
private ObservableCollection<ThemeItem> NewestThemes { get; set; };
private ObservableCollection<ThemeItem> MostPopularItems { get; set; };
/// <summary>
/// Initializes a new instance of the HomeVm class.
/// </summary>
public HomeVm()
{
NewestThemes = new ObservableCollection<ThemeItem>();
MostPopularItems = new ObservableCollection<ThemeItem>();
////if (IsInDesignMode)
////{
//// // Code runs in Blend --> create design time data.
////}
////else
////{
//// // Code runs "for real": Connect to service, etc...
////}
}
}
接下来需要做的是为UI准备一组设计时(Design Time)的数据,设计时的数据区别于最终编译运行后的数据,他将只呈现在Blend软件的环境之中,帮助我们更好的在Blend中对UI进行布局。将HomeVm构造的if(IsInDesignMode)的大段取消注释,并加入相应的方法用于构建样例数据:
public class HomeVm : ViewModelBase
{
public ObservableCollection<ThemeItem> NewestThemes { get; set; }
public ObservableCollection<ThemeItem> MostPopularItems { get; set; }
/// <summary>
/// Initializes a new instance of the HomeVm class.
/// </summary>
public HomeVm()
{
if (IsInDesignMode)
{
BuildSampleData();
}
else
{
//Runtime Data
}
}
private void BuildSampleData()
{
NewestThemes = new ObservableCollection<ThemeItem>();
MostPopularItems = new ObservableCollection<ThemeItem>();
ThemeItem theme1 = new ThemeItem();
theme1.Id = "1175537";
theme1.Title = "Friends and foes";
theme1.Author = "syork";
theme1.AuthorId = "254539";
theme1.Tags = string.Empty;
theme1.Rating = "4";
theme1.DownloadCount = "1128";
theme1.CreatedAt = "20110105";
theme1.ThemeSwitches = new List<string>();
theme1.ThemeSwitches.Add("2F2933");
theme1.ThemeSwitches.Add("01A2A6");
theme1.ThemeSwitches.Add("29D9C2");
theme1.ThemeSwitches.Add("BDF271");
theme1.ThemeSwitches.Add("FFFFA6");
ThemeItem theme2 = new ThemeItem();
theme2.Id = "782171";
theme2.Title = "Sea Wolf";
theme2.Author = "xizzzy";
theme2.AuthorId = "132523";
theme2.Tags = string.Empty;
theme2.Rating = "4";
theme2.DownloadCount = "2038";
theme2.CreatedAt = "20100213";
theme2.ThemeSwitches = new List<string>();
theme2.ThemeSwitches.Add("DC3522");
theme2.ThemeSwitches.Add("D9CB9E");
theme2.ThemeSwitches.Add("374140");
theme2.ThemeSwitches.Add("2A2C2B");
theme2.ThemeSwitches.Add("1E1E20");
NewestThemes.Add(theme1);
NewestThemes.Add(theme2);
}
}
接下来我们在ViewModelLocator.cs尝试实例化我们的这个ViewModel,我们在ViewModelLocator中会发现MVVM Light模板还保留着默认的MainViewModel的相关信息,我们可以手工进行代码删除。在当前类中输入MVVM Light提供的Code Snippet,并按Tab键,自动生成相关的代码:

对生成的代码进行修改如下所示:
public class ViewModelLocator
{
private static HomeVm _home;
/// <summary>
/// Gets the HomeVm property.
/// </summary>
public static HomeVm Home
{
get
{
if (_home == null)
{
CreateHomeVm();
}
return _home;
}
}
/// <summary>
/// Gets the HomeVm property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public HomeVm HomeVm
{
get
{
return Home;
}
}
/// <summary>
/// Provides a deterministic way to delete the HomeVm property.
/// </summary>
public static void ClearHomeVm()
{
_home.Cleanup();
_home = null;
}
/// <summary>
/// Provides a deterministic way to create the HomeVm property.
/// </summary>
public static void CreateHomeVm()
{
if (_home == null)
{
_home = new HomeVm();
}
}
/// <summary>
/// Cleans up all the resources.
/// </summary>
public static void Cleanup()
{
ClearHomeVm();
}
/// <summary>
/// Initializes a new instance of the ViewModelLocator class.
/// </summary>
public ViewModelLocator()
{
////if (ViewModelBase.IsInDesignModeStatic)
////{
//// // Create design time view models
////}
////else
////{
//// // Create run time view models
////}
}
}
打开Home.xaml,在第一行标记中加入如下属性:
DataContext="{Binding Main, Source={StaticResource Locator}}"
如果我们在Blend中再次打开Home.xaml,我们将看见我们的设计时数据已经关联到了Data面板之中
接下来就是开始构建XAML的UI界面了。虽然Blend提供了从PSD文件导入图层为位图的方式,但我个人更喜欢在Blend及XAML中直接实现各个控件效果,以达到更好的控制UI元素的目的。所以我会先放置设计稿到Blend文件的底层中作为参考图层,然后再放置相应的控件。打开Home.xaml并清空系统给我们放置的默认元素,整个对象的结构应该如下所示:
记得在选中PhoneApplicationPage层级的同时,在属性面板中将Show SystemTray设置为不选,这样在应用程序中的本页面下,将不显示手机屏幕顶部的信号,电池等系统托盘的信息:
在主画面中放置好我们的设计图作为参考:
然后我们放置Panorama控件,并简单设定一下标题等信息:
下面将是最有意思的一部分。我们知道我们在ViewModel中建立的二个集合分别用于newest和mostpopular面板,所以我们只需要简单的将第一个叫newestItems的集合拖入第一个PanoramaItem的Gird中:
Blend将自动根据我们的数据源来创建一个ListBox显示数据,调整ListBox的外观相关属性如图所示:
右键单击ListBox,编辑ItemTemplate:
由于我们的设计稿已经放置在画面下方,所以我们可以根据设计稿的要求,来调整各个数据的显示位置,并清理不需要的数据,进行简单的布局操作后,结果如下,注意我们放置了一个空的Border用来放置一会需要绑定的颜色预览:
接下来我们需要实现显示我们的配色方案。比较简单的做法是通过ItemsControl控件来放置五个方块。每个方块的颜色由ItemsControl的数据源提供。所以我们先放置一个ItemsControl到画面之中,同时继续从数据源中将ThemeSwitches数据源拖动到ItemsControl之上:
如果成功的绑定数据的话,我们已经能在画面中看见一些颜色数值进入了ItemsControl之中,接下来我们需要做的是对ItemsControl中每一个呈现数据用的项目进行一些模板的定制:
我们去掉Blend自动为我们生成的元素,在里面放置一个60×60的矩形:
由于我们数据源中的颜色数值只是一个字符串,所以为了与这个方块的背景色进行绑定,我们需要做一些转换操作,接下来我们在Visual Studio中做一个额外的编码工作,创建一个值转换器(ValueConverter),代码如下:
public class ColorBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string s = System.Convert.ToString(value);
byte a = System.Convert.ToByte("FF", 16);
byte r = System.Convert.ToByte(s.Substring(0, 2), 16);
byte g = System.Convert.ToByte(s.Substring(2, 2), 16);
byte b = System.Convert.ToByte(s.Substring(4, 2), 16);
return new SolidColorBrush(Color.FromArgb(a, r, g, b));
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
回到Blend中,刷新解决方案之后,我们回到定义方块的地方,选中矩形,在Properties面板的颜色选择中,点击Fill右侧的方块,选择Data Binding…

在接下来的对话框中,在下方的Value Converter选项中,点击右边的更多按钮,在对话框中选中我们刚才建立的ColorBrushConverter
确定后如果没什么问题,你将能看颜色方块出现。接下来我们调整一下ItemsControl的ItemsPanel,也就是控件对其下面的所有Item元素进行布局的这个面板。回到上一级,继续操作ItemsControl。如下图所示,创建一个ItemsPanel:
在ItemsPanel中,将默认的StackPanel的Orientation改为Horizontal,也就是水平方向:
处理好之后就能看见实际显示的效果了。我们可以继续修饰一下一些UI元素的细节,达到和设计稿一致的效果。但需要注意的是,现在进行发布预览无法获得和在Blend中一样的效果,因为的样例数据只在设计时呈现,如果需要在运行时用现在的样例数据进行预览,我们可以简单的修改一下HomeVm.cs:
public HomeVm()
{
if (IsInDesignMode)
{
BuildSampleData();
}
else
{
BuildSampleData();
}
}
我们在这个判断语句中都使用BuildSampleData,就能临时使用我们制作的数据进行预览,最终的运行效果如下:

我们可以用同样的方法,在most popular区域中实现另一种不同布局的数据呈现方式,但需要注意的是,由于这个区域的布局与左侧不同,所以我们将不使用ListBox来构建,我们在画面中直接放置一个ItemsControl,并将相应的数据源绑定到ItemsControl下面:

继续对ItemsTemplate进行一些UI上的调整,思路与上面的类似,制作完成后结果如下:
接下来我们需要实现类似设计图中的WarpPanel的效果。每一种排版方式我们都可以通过编程实现,但更好的是微软在最新版的Tool Kit中已经提供了WarpPanel以及其它有用的控件,所以我们只需要引用相应的库就可以实现。ToolKit的最新版可以在CodePlex获得。安装好Tool Kit后,我们在项目中引入相应的库:
返回Blend,我们为ItemsControl创建一个新的ItemsPanel,删掉默认这个模板下的StackPanel,从Assets中找到WrapPanel,拖动到这个控件下面:
接下来还需要做的是将WrapPanel的排列方向由水平改为垂直:
回到主画面后,我们已经看到了实际的效果,但我们发现面板的右侧被截短,这时候我们需要先将整个这个PanoramaItem的对齐方式由默认的垂直改为水平,让这个PanoramaItem可以水平方向向右延展:
然后我们将这个PanoramaItem的高度设为定高,并向左对齐,如图所示:
这样我们的第二个most popular区域就已经顺利完成了。
优化一些细节,比如设计一个字母大小写的转换器,让画面中绑定文字的地方,通过转换器变为全大写的形式,以符合设计稿的要求。
接下来我们实现其它设计细节,编辑应用程序的标题,也就是Panorama的Header:
我们在空的默认中随意插入一个文本框,用以达到与默认的Title似近的效果,方便我们预览:
通过调整形成如下图所示的效果,比较特殊的是,我们将Logo中的一部分作为一段XAML Path置入,这样在未来我们可以实现对其颜色的动态绑定,在后面我们将用得上,Blend本身支持从Photoshop或Illustrator文件导入,所以我们只需要导入一个AI路径文件,该路径就会自动转换为XAML的Path:
最后我们做一些小的功能上的优化,让每次使用应用程序时的背景,都从最新的主题上随机取得一个,我们在HomeVm中建立二个新的属性,同时在建立样例数据后, 加一个随机的方法:
public string ThemeBackground { get; set; }
public string ThemeForeground { get; set; }
//...
private void RandomAppTheme()
{
ThemeItem randomTheme = NewestThemes[(new Random()).Next(NewestThemes.Count - 1)];
ThemeBackground = randomTheme.ThemeSwitches[0];
ThemeForeground = randomTheme.ThemeSwitches[1];
}
然后我们在Blend中对背景进行数据绑定:
同样,我们对需要设置ThemeForeground的一些元素也进行相应的绑定。最后运行时的效果如下,这样我们在每次运行时都能获得不一样的配色效果:
最近二个多月的时间内一直在从事有关Windows Phone 7 的应用设计与开发工作,马上也会有项目在WP7 App Market上线。在未来半年的重点可能都在这里,我会写下更多的经验。这次我想通过几篇贴子全面的介绍一下有关Windows Phone 7开发的一个基本的流程及一些值得一用的框架。帮助身边的朋友更快的上手WP7的应用开发。
这次应用的设计目标是实现一个基于Adobe Kuler API的一个颜色应用程序。主要功能是从Kuler取得一些最新的或最热门的配色方案,并展示在手机上。一个典型的基于Web API的应用程序。
Adobe Kuler是我做设计时常用的一个服务,上面收集了由用户提交的各种颜色方案,并通过点击率、打分、Tag等方式进行分类:
通过了解一下Kuler API所能提供的数据,并结合Windows Phone 7的基本交互形式,大概画下了自己的构想:
从基本形式上整个应用由二个视图组成,一个是Home,另一个是Detail View。也就是常见的List/Detail View的交互形式。打算在Home视图上采用Panorama Control,在Detail视图上采用Pivot Control。如果大家对Windows Phone 7的Wireframe设计感兴趣,可以试试我在早些时候制作的OmniGraffle的Windows Phone 7 Wireframe Stencil。
接下来进入设计阶段。这个应用采用常规的Windows Phone 7 的Panorama和Pivot控件,所以可以直接通过微软提供的Windows Phone 7 Design Template来进行设计。同时UI Design and Interaction Guide for Windows Phone 7也会对设计WP7应用有一定的帮助。
设计阶段的工作完成之后,接下来就是要进入具体的实施阶段。
在windows phone7的开发中,因为硬件设备在性能兼容性方面的限制,每个控件被限定为其渲染面积不超过2048×2048,所以如果有一个Textblock过长的话,会导致超出的部分不予渲染,解决办法是放置到多个textblock中,已经有人制作了相应的控件帮助我们简单实现这样的功能。链接在此