Windows Phone 是那个1%, 我也是那个1%, 不喜勿喷 .
WP 向来给 android / ios 的粉们一个最直观的印象: 丑.
其实"丑"这个东西会一直下去,而且是个解不开的死循环.
WP 下, 做的华丽的, 目前我用过的只有少数几个, 网易云音乐 和 智机社区, 做的漂亮而且流畅, 其它的只能用渣渣来形容.
Xamarin.Form 的 APP 在 Android 和 IOS 下默认都会有一个"头", 用于显示页面名称和导航按钮.在 WP 下却没有, 像这样:
Xamarin.Form 在 WP 的 MainPage.xaml.cs 中这样定义:
public partial class MainPage : FormsApplicationPage{ ... public MainPage() { InitializeComponent(); SupportedOrientations = SupportedPageOrientation.PortraitOrLandscape; global::Xamarin.Forms.Forms.Init(); LoadApplication(new LBC.Mobi.App()); }
FormsApplicationPage.LoadApplication 方法的定义:
1 protected void LoadApplication(Xamarin.Forms.Application application) 2 { 3 Xamarin.Forms.Application.Current = application; 4 application.PropertyChanged += new PropertyChangedEventHandler(this.ApplicationOnPropertyChanged); 5 this.application = application; 6 application.SendStart(); 7 this.SetMainPage(); 8 } 9 10 11 private void SetMainPage() 12 { 13 if (this.platform == null) 14 this.platform = new Platform((PhoneApplicationPage) this); 15 this.platform.SetPage(this.application.MainPage); 16 if (this.Content == this.platform) 17 return; 18 this.Content = (UIElement) this.platform; 19 }
这样一个调用链:
LoadApplication -> SetMainPage -> 直接给整个页面的 Content 赋值.
所以, 无论你在 MainPage.xaml 中写什么内容, 最终都是无法显示的.
为了达成目的, 我们需要修改 SetMainPage 方法 , 使 this.platform 只作为 MainPage 的一部分, 而不是整个 Content.
但是反编译一下, 可以看到 Xamarin.Form 的封装很蛋疼, 很多都是 internal , private, 不带 virtual, 所以, 通过正常手段是无法对 Xamarin.Form 进行扩展的.
还好, Xamarin 对反射支持的很到位, 无法正常通过继承/重写来实现, 我们照样可以用反射来完成它.
1, 在 MainPage.xaml.cs 中新增方法 LoadApplication:
1 new protected void LoadApplication(Xamarin.Forms.Application application) { 2 //Xamarin.Forms.Application.Current = application; 3 typeof(Xamarin.Forms.Application).GetProperty("Current", BindingFlags.Static | BindingFlags.Public) 4 .SetValue(Xamarin.Forms.Application.Current, application); 5 6 7 application.PropertyChanged += new PropertyChangedEventHandler(this.ApplicationOnPropertyChanged); 8 typeof(FormsApplicationPage).GetField("application", BindingFlags.NonPublic | BindingFlags.Instance) 9 .SetValue(this, application); 10 11 //application.SendStart(); 12 application.GetType().GetMethod("SendStart", BindingFlags.NonPublic | BindingFlags.Instance) 13 .Invoke(application, null); 14 15 this.SetMainPage(); 16 17 var mp = (Xamarin.Forms.NavigationPage)application.MainPage; 18 mp.PropertyChanged += MainPage_PropertyChanged; 19 this.Title = mp.Title; 20 }
为了能显示当前页的标题, 我在上面的方法中加了一段(红色标注), 实际中, 你的 application.MainPage 可能不是 NavigationPage, 需要自行修改.
2,
1 void MainPage_PropertyChanged(object sender, PropertyChangedEventArgs e) { 2 if (e.PropertyName.Equals("CurrentPage")) { 3 this.Title = ((Xamarin.Forms.NavigationPage)sender).CurrentPage.Title; 4 this.PropertyChanged(this, new PropertyChangedEventArgs("Title")); 5 } 6 }
3, 在 MainPage.xaml.cs 中添加 SetMainPage :
1 private void SetMainPage() { 2 if (this.Platform == null) { 3 this.Platform = new Platform((PhoneApplicationPage)this); 4 } 5 6 this.Platform.SetPage(Xamarin.Forms.Application.Current.MainPage); 7 if (this.mainBody.Content != null && this.mainBody.Content.Equals(this.Platform)) 8 return; 9 this.mainBody.Content = (UIElement)this.Platform; 10 }
4, 修改 MainPage.xaml
1 <winPhone:FormsApplicationPage 2 x:Class="Discuz.WinPhone.MainPage" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" 6 xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" 7 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 8 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 9 xmlns:winPhone="clr-namespace:Xamarin.Forms.Platform.WinPhone;assembly=Xamarin.Forms.Platform.WP8" 10 xmlns:local="clr-namespace:Discuz.WinPhone" 11 mc:Ignorable="d" 12 FontFamily="{StaticResource PhoneFontFamilyNormal}" 13 FontSize="{StaticResource PhoneFontSizeNormal}" 14 Foreground="{StaticResource PhoneForegroundBrush}" 15 SupportedOrientations="Portrait" Orientation="Portrait" 16 shell:SystemTray.IsVisible="True" 17 shell:SystemTray.Opacity="0" 18 Background="#1e5263" 19 > 20 <!--shell:SystemTray.BackgroundColor="#2ba9d3"--> 21 22 <Grid> 23 <Grid.RowDefinitions> 24 <RowDefinition Height="15" /><!-- for SystemTray --> 25 <RowDefinition Height="auto" /> 26 <RowDefinition Height="*" /> 27 </Grid.RowDefinitions> 28 <local:TilePanel TileWidth="5" TileHeight="5" Height="65" Grid.Row="0" Grid.RowSpan="2"> 29 <local:TilePanel.Image> 30 <ImageBrush ImageSource="texture.png" /> 31 </local:TilePanel.Image> 32 </local:TilePanel> 33 <StackPanel Orientation="Horizontal" Margin="10,0" Grid.Row="1" VerticalAlignment="Center"> 34 <Image Source="icon.png" Width="40" Height="40" Margin="0,0,10,0" /> 35 <TextBlock Text="{Binding Title}" VerticalAlignment="Center" /> 36 </StackPanel> 37 38 <ContentPresenter x:Name="mainBody" Grid.Row="2" /> 39 </Grid> 40 41 </winPhone:FormsApplicationPage>
好啦, 一个带标题的 XF For WP 的 APP 改造完了.
最终效果:
具体参见:
https://github.com/gruan01/Discuz.Mobi/tree/master/Discuz/Discuz.WinPhone
--------------------
OK, 完