zoukankan      html  css  js  c++  java
  • wp开发杂记

    Navigation for Windows Phone 7 using MVVM

    By Geert31. March 2011 10:32

    While we were working on the support for Windows Phone 7 in Catel, we encountered a complex problem about navigation inside WP7 from within view-models.

    As you probably know by know, the view-models in Catel support services to handle message boxes, UI dialogs (not in WP7, but in Silverlight and WPF), running processes (WPF only) and more. After a good night of sleep, it turned out that the navigation service will look a bit like the IUIVisualizerService that already exists in Catel.

    But we are not there yet, there are some “side-effects” that we need to keep in mind with Windows Phone 7:

    • You cannot pass arguments using default navigation 
      The only way to pass arguments is via query parameters (url.xaml?parameter=value) 
    • Care about tomb stoning 
      When tomb stoning, a page is navigated to again (so we can’t instantiate a view-model in view X and go to Y, and then tomb stone the app in Y, you won’t have your view-model again

    So, how can we solve this problem? In the end, when you have the solution at hand, it is always easy. But what we wanted to accomplish with the navigation service in Catel is that you would be able to pass parameters to other view-models without actually having to care about the NavigationContext to request the parameters.

    Introducing parameters (navigation context) in ViewModelBase

    We used to following code to “upgrade” the ViewModelBase class in WP7:

     1: /// <summary>
     2: /// Gets the navigation context.
     3: /// </summary>
     4: /// <value>The navigation context.</value>
     5: /// <remarks>
     6: /// Note that the navigation contexts is first available in the <see cref="Initialize"/> method, 
     7: /// not in the constructor.
     8: /// </remarks>
     9: protected Dictionary<string, string> NavigationContext { get { return _navigationContext; } }

    The _navigationContext is an private dictionary containing the actual keys available in the NavigationContextof the PhoneApplicationPage the view-model is available for.

    The navigation context can be updated via an internal method:

     1: /// <summary>
     2: /// Updates the navigation context. The navigation context provided by this class is different
     3: /// from the <see cref="NavigationContext"/>. Therefore, this method updates the navigation context
     4: /// to match it to the values of the <param name="navigationContext"/>.
     5: /// </summary>
     6: /// <param name="navigationContext">The navigation context.</param>
     7: internal void UpdateNavigationContext(NavigationContext navigationContext)
     8: {
     9: lock (_navigationContext)
     10: {
     11: _navigationContext.Clear();
     12:  
     13: if (navigationContext != null)
     14: {
     15: foreach (string key in navigationContext.QueryString.Keys)
     16: {
     17: _navigationContext.Add(key, navigationContext.QueryString[key]);
     18: }
     19: }
     20: }
     21: }

    Then, in the OnLoaded event of the PhoneApplicationPage, we update the navigation context (but only if it is a ViewModelBase of Catel:

     1: if (_viewModel is ViewModelBase)
     2: {
     3: ((ViewModelBase)_viewModel).UpdateNavigationContext(NavigationContext);
     4: }

    Using parameters in a view model

    Now we have a view-model that has support for parameters, let’s take a look how easy it is to use them. In an implementation of the ViewModelBase, the query parameters are available in the Initialize method:

     1: /// <summary>
     2: /// Initializes the object by setting default values.
     3: /// </summary> 
     4: protected override void Initialize()
     5: {
     6: int shopIndex = -1;
     7: if (NavigationContext.ContainsKey("ShopIndex"))
     8: {
     9: int.TryParse(NavigationContext["ShopIndex"], out shopIndex);
     10: }
     11:  
     12: if (shopIndex != -1)
     13: {
     14: _existingShop = true;
     15: Shop = UserData.Instance.Shops[shopIndex];
     16: }
     17: else
     18: {
     19: Shop = new Shop();
     20: }
     21: }

    As you see, the navigation parameter is passed as an index of a list. Normally, you would pass a unique object ID that allows you to fetch the object (model). In this view-model, we choose to either fetch an existing shop, or create a new one.

    Using the INavigationService

    We know that we have a view-model that supports parameters, so let’s take a look at the actual navigation service. I don’t want to scare you off, but also want to show you all the capabilities of the INavigationService, so here is the full interface declaration:

     1: public interface INavigationService
     2: {
     3: /// <summary>
     4: /// Gets a value indicating whether it is possible to navigate back.
     5: /// </summary>
     6: /// <value>
     7: /// <c>true</c> if it is possible to navigate back; otherwise, <c>false</c>.
     8: /// </value>
     9: bool CanGoBack { get; }
     10:  
     11: /// <summary>
     12: /// Gets a value indicating whether it is possible to navigate forward.
     13: /// </summary>
     14: /// <value>
     15: /// <c>true</c> if it is possible to navigate backforward otherwise, <c>false</c>.
     16: /// </value>
     17: bool CanGoForward { get; }
     18:  
     19: /// <summary>
     20: /// Navigates back to the previous page.
     21: /// </summary>
     22: void GoBack();
     23:  
     24: /// <summary>
     25: /// Navigates forward to the next page.
     26: /// </summary>
     27: void GoForward();
     28:  
     29: /// <summary>
     30: /// Navigates to a specific location.
     31: /// </summary>
     32: void Navigate(string uri);
     33:  
     34: /// <summary>
     35: /// Navigates to a specific location.
     36: /// </summary>
     37: /// <param name="uri">The URI.</param>
     38: /// <param name="parameters">Dictionary of parameters, where the key is the name of the parameter, 
     39: /// and the value is the value of the parameter.</param>
     40: void Navigate(string uri, Dictionary<string, object> parameters);
     41:  
     42: /// <summary>
     43: /// Navigates to a specific location.
     44: /// </summary>
     45: void Navigate(Uri uri);
     46:  
     47: /// <summary>
     48: /// Navigates the specified location registered using the view model type.
     49: /// </summary>
     50: /// <typeparam name="TViewModelType">The view model type.</typeparam>
     51: void Navigate<TViewModelType>();
     52:  
     53: /// <summary>
     54: /// Navigates the specified location registered using the view model type.
     55: /// </summary>
     56: /// <typeparam name="TViewModelType">The view model type.</typeparam>
     57: /// <param name="parameters">Dictionary of parameters, where the key is the name of the parameter, 
     58: /// and the value is the value of the parameter.</param>
     59: void Navigate<TViewModelType>(Dictionary<string, object> parameters);
     60:  
     61: /// <summary>
     62: /// Navigates the specified location registered using the view model type.
     63: /// </summary>
     64: /// <param name="viewModelType">The view model type.</param>
     65: void Navigate(Type viewModelType);
     66:  
     67: /// <summary>
     68: /// Navigates the specified location registered using the view model type.
     69: /// </summary>
     70: /// <param name="viewModelType">The view model type.</param>
     71: /// <param name="parameters">Dictionary of parameters, where the key is the name of the parameter, 
     72: /// and the value is the value of the parameter.</param>
     73: void Navigate(Type viewModelType, Dictionary<string, object> parameters);
     74:  
     75: /// <summary>
     76: /// Registers the specified view model and the page type. This way, Catel knowns what
     77: /// page to show when a specific view model page is requested.
     78: /// </summary>
     79: /// <param name="viewModelType">Type of the view model.</param>
     80: /// <param name="pageType">Type of the page.</param>
     81: /// <exception cref="ArgumentException">when <paramref name="viewModelType"/> does not implement <see cref="IViewModel"/>.</exception>
     82: /// <exception cref="ArgumentException">when <paramref name="pageType"/> is not of type <see cref="PhoneApplicationPage"/>.</exception>
     83: void Register(Type viewModelType, Type pageType);
     84:  
     85: /// <summary>
     86: /// Registers the specified view model and the page type. This way, Catel knowns what
     87: /// page to show when a specific view model page is requested.
     88: /// </summary>
     89: /// <param name="name">Name of the registered page.</param>
     90: /// <param name="pageType">Type of the page.</param>
     91: /// <exception cref="ArgumentException">when <paramref name="name"/> is <c>null</c> or empty.</exception>
     92: /// <exception cref="ArgumentException">when <paramref name="pageType"/> is not of type <see cref="PhoneApplicationPage"/>.</exception>
     93: void Register(string name, Type pageType);
     94:  
     95: /// <summary>
     96: /// This unregisters the specified view model.
     97: /// </summary>
     98: /// <param name="viewModelType">Type of the view model to unregister.</param>
     99: /// <returns>
     100: /// <c>true</c> if the view model is unregistered; otherwise <c>false</c>.
     101: /// </returns>
     102: bool Unregister(Type viewModelType);
     103:  
     104: /// <summary>
     105: /// This unregisters the specified view model.
     106: /// </summary>
     107: /// <param name="name">Name of the registered page.</param>
     108: /// <returns>
     109: /// <c>true</c> if the view model is unregistered; otherwise <c>false</c>.
     110: /// </returns>
     111: bool Unregister(string name);
     112: }

    It might look complex, but it’s not. Most of the methods are about navigating to a specific uri, with or without arguments.

    The basic method is the Navigate(uri, parameters) method. Every other method calls this instance which will convert the uri and parameters into a valid html encoded request string and pass it to the Navigate(Uri)method eventually.

    Thus, a simple example on how to use the service inside a view-model is this:

     1: var navigationService = GetService<INavigationService>();
     2: navigationService.Navigate("/UI/Pages/ShopPage.xaml");

    And, if you want to use parameters:

     1: var parameters = new Dictionary<string, object>();
     2: parameters.Add("ShopIndex", Shops.IndexOf(SelectedShop));
     3:  
     4: var navigationService = GetService<INavigationService>();
     5: navigationService.Navigate("/UI/Pages/ShopPage.xaml", parameters);

    This is not all. If you have studied the interface carefully, you might have seen the Navigate<TViewModel>method. This is a very special method that we also used in the IUIVisualizerService. In your view-model, you don’t want to navigate to a specific page, you want to run another view-model. Therefore, it is possible to navigate via view-model types instead of the actual pages. Without parameters, the code would look like this:

     1: var navigationService = GetService<INavigationService>();
     2: navigationService.Navigate<ShopViewModel>();

    And again it is very easy to pass parameters to the view-model:

     1: var parameters = new Dictionary<string, object>();
     2: parameters.Add("ShopIndex", Shops.IndexOf(SelectedShop));
     3:  
     4: var navigationService = GetService<INavigationService>();
     5: navigationService.Navigate<ShopViewModel>(parameters);

     //-------------------------------------------------------------------

    Windows Phone开发经验谈(18)-总结两种滚动条到底部加载数据的方法

      如今大多数手机用户所关心的网络流量的消耗,当你的App涉及到从网络服务中获取数据,应该尽可能以最有效的方式。让用户等待,就算你的应用程序下载大量的数据不会影响用户体验。而不是让你的App只下载少量的数据。今天我就来和大家介绍下WP中两种让滚动条到底部后再加载数据的方法。

    一、

        我先介绍第一种方法,贴出几个关键代码用于查找控件。

    复制代码
     public static T FindFirstChildOfType<T>(DependencyObject root) where T : class
    {
    Queue<DependencyObject> queue = new Queue<DependencyObject>();
    queue.Enqueue(root);
    while (0 < queue.Count)
    {
    DependencyObject dependencyObject = queue.Dequeue();
    if (dependencyObject == null)
    {
    return default(T);
    }
    int num = VisualTreeHelper.GetChildrenCount(dependencyObject) - 1;
    while (0 <= num)
    {
    DependencyObject child = VisualTreeHelper.GetChild(dependencyObject, num);
    T t = child as T;
    if (t != null)
    {
    return t;
    }
    queue.Enqueue(child);
    num--;
    }
    }
    return default(T);
    }
    public static T FindChildOfType<T>(DependencyObject root) where T : class
    {
    Queue<DependencyObject> queue = new Queue<DependencyObject>();
    queue.Enqueue(root);
    while (queue.Count > 0)
    {
    DependencyObject dependencyObject = queue.Dequeue();
    int num = VisualTreeHelper.GetChildrenCount(dependencyObject) - 1;
    while (0 <= num)
    {
    DependencyObject child = VisualTreeHelper.GetChild(dependencyObject, num);
    T t = child as T;
    if (t != null)
    {
    return t;
    }
    queue.Enqueue(child);
    num--;
    }
    }
    return default(T);
    }
    复制代码

    在ListBox控件的Loaded方法中添加如下代码

    复制代码
     private void AddHandler()
    {
    this.m_scrollViewer = CommonUtil.FindChildOfType<ScrollViewer>(this.lstList);
    if (this.m_scrollViewer != null)
    {
    this.m_scrollBar = CommonUtil.FindChildOfType<ScrollBar>(this.m_scrollViewer);
    if (this.m_scrollBar != null)
    {
    m_scrollBar.ValueChanged += bar_ValueChanged;
    m_scrollBar.SizeChanged += bar_SizeChanged;
    }
    }
    }
    void lstList_Loaded(object sender, RoutedEventArgs e)
    {
    AddHandler();
    }
    复制代码

    当滚动条到达底部的时候就会执行 MainPageVM.LoadNextData();方法...这个方法可以改成你想要的。

    复制代码
     void bar_SizeChanged(object sender, SizeChangedEventArgs e)
    {
    }
    void bar_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
    {
    ScrollBar bar = (ScrollBar)sender;
    object obj2 = bar.GetValue(RangeBase.ValueProperty);
    object obj3 = bar.GetValue(RangeBase.MaximumProperty);
    if ((obj2 != null) && (obj3 != null))
    {
    double num = (double)obj2;
    double num2 = ((double)obj3) - 2.0;
    if (num >= num2)
    {
    this.LoadingMore.Visibility = Visibility.Visible;
    this.LoadingMoreRing.IsActive = true;
    var MainPageVM = base.DataContext as MainPageViewModel;
    MainPageVM.LoadNextData();
    }
    }
    }
    复制代码

    第一种方法很简单明了...不需要用任何控件就能实现了!

    -----------------------------------------------------------------------

    二、

    第二种则是用控件来实现的..首先你要下载DanielVaughan.ScrollViewerMonitor.rar,这是国外程序员封装好的一个控件

    使用方法更加简单,如下所示:

    <ListBox ItemsSource="{Binding Items}" u:ScrollViewerMonitor.AtEndCommand="{Binding FetchMoreDataCommand}" />

    注意的这里的AtEndCommand是一个附加属性,当用户滚动到列表末尾后,就会执行指定的命令。

    总结,这里说的两种方法是我比较常用到的...分享给大家..有什么建议或者意见欢迎留言讨论!

    Windows Phone开发经验谈(17)-两则改善用户体验的开发技巧

    不知道大家有没一种体会,就是你在软件中加入了一种功能之后,有的用户希望你不要加入这项功能,有的则是非常喜欢新加入的功能。这种情况就实在令人头疼。这里就说两个这样的例子。

        1、退出确认功能

         有的时候开发者担心用户操作出错,而特别设置了一个退出确认功能,但是市场上大部分用户却不是很喜欢,但是你如果把这项功能去掉就又会导致误操作,实在是众口难调..所以你就必须在设置里面加入一个选项,让用户选择是否要退出确认功能,实现代码如下:

    首先建立一个属性ExitConfirm来存储系统的设置..判断是否是退出确认

    复制代码
     private IsolatedStorageSettings m_IsolatedStorageSettings = IsolatedStorageSettings.ApplicationSettings;
    public bool ExitConfirm
    {
    get
    {
    return (this.m_IsolatedStorageSettings.Contains("ExitConfirm") && ((bool) this.m_IsolatedStorageSettings["ExitConfirm"]));
    }
    set
    {
    if (this.ExitConfirm != value)
    {
    this.m_IsolatedStorageSettings["ExitConfirm"] = value;
    if (this.PropertyChanged != null)
    {
    this.PropertyChanged(this, new PropertyChangedEventArgs("ExitConfirm"));
    }
    }
    }
    }
    复制代码

    再在主界面中重写OnBackKeyPress方法代码如下所示

    复制代码
     protected override void OnBackKeyPress(CancelEventArgs e)
    {
    if (Theme.Instance.ExitConfirm && (MessageBox.Show("确定退出超级词典", "提示", MessageBoxButton.OKCancel) == MessageBoxResult.Cancel))
    {
    e.Cancel = true;
    }
    base.OnBackKeyPress(e);
    }
    复制代码


    这样退出确认功能只会在ExitConfirm为true的时候提示,用户如果没有设置退出确认则不会显示了...很简单的小技巧但是却改善了用户体验

        

        2、图标透明

    有的时候开发者把图标配上底色自我感觉很不错..当应用发布后却遭到很多用户反馈说是否能把图标改成透明的,我就曾经遇到过这样的反馈。其实这并不一定图标配色出了问题,而可能是有的用户希望APP的Icon能够和系统的主题一致,但是你或者其他一些用户又喜欢有底色的Icon..这又如何是好呢?其实很简单,还是按照第一条的结论,也就是再添加一个设置项,来设置是否需要图标透明..下面可以一起跟我一起做一遍:

    首先做2个173*173的图标的,一个底色的叫Background.png另一个透明的叫Background2.png,默认是有底色的...在系统设置里面添加一个ToggleSwitch并添加一个事件ToggleSwitch_Checked代码如下:

    复制代码
     private void ToggleSwitch_Checked(object sender, RoutedEventArgs e)
    {
    ShellTile tile = Enumerable.First<ShellTile>(ShellTile.ActiveTiles);
    if (tile != null)
    {
    string uriString = string.Empty;
    if (this.toggleSwitch.IsChecked.Value)
    {
    uriString = "/Background2.png";
    }
    else
    {
    uriString = "/Background.png";
    }
    Theme.Instance.DefaultTile = this.toggleSwitch.IsChecked.Value;
    StandardTileData data = new StandardTileData();
    this.imgTile.Source = new BitmapImage(new Uri(uriString, UriKind.RelativeOrAbsolute));
    data.BackgroundImage = new Uri(uriString, UriKind.RelativeOrAbsolute);
    tile.Update(data);
    }
    }
    复制代码

    这样用户就能够自由的选择APP的Icon是否要背景了。

    这两则示例虽然简单,却大大提升了用户体验,不必太多的代码换来的是让用户感觉到你所做软件非常的用心体贴,何乐而不为。

    有什么疑问欢迎留言讨论。

    Windows Phone开发经验谈(15)-动态的改变APP的字体大小

      虽然Windows Phone 8快要出来了...但是丝毫不能使我减少对WP7的研究...这次教大家如何动态改变APP的字体大小,想看具体的演示可以去windows phone市场下载 公交路线查询http://www.windowsphone.com/?appsid=384ba16d-d30f-44a5-9a8e-e395eea269df

        我在公交路线查询里面设置了3种的字体大小(大,中,小) 我用一个枚举来表示

     public enum FontSizePattern
    {
    Small = 0,
    Middle,
    Large
    }

       但是大家要知道,虽然字体分为了大、中、小,但是很多地方的字体显示大小还是不一样的,所以任何一种字体大小里面还要设置不同的字体SIZE,这里我把字体大小的配置写成XAML文件

       large.xaml

    复制代码
    <ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    >
    <sys:Double x:Key="FontSize18">22.4</sys:Double>
    <sys:Double x:Key="FontSize22">26.4</sys:Double>
    <sys:Double x:Key="FontSize24">28.8</sys:Double>
    <sys:Double x:Key="FontSize30">32.8</sys:Double>
    <sys:Double x:Key="FontSize36">43.2</sys:Double>
    <sys:Double x:Key="FontSize48">57.6</sys:Double>
    <sys:Double x:Key="FontSize60">72.0</sys:Double>
    <sys:Double x:Key="FontSize72">86.4</sys:Double>
    </ResourceDictionary>
    
    复制代码

    middle.xaml

    复制代码
    <ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    >
    <sys:Double x:Key="FontSize18">22.0</sys:Double>
    <sys:Double x:Key="FontSize22">22.0</sys:Double>
    <sys:Double x:Key="FontSize24">24.0</sys:Double>
    <sys:Double x:Key="FontSize30">28.0</sys:Double>
    <sys:Double x:Key="FontSize36">36.0</sys:Double>
    <sys:Double x:Key="FontSize48">48.0</sys:Double>
    <sys:Double x:Key="FontSize60">60.0</sys:Double>
    <sys:Double x:Key="FontSize72">72.0</sys:Double>
    </ResourceDictionary>
    复制代码

    small.xaml

    复制代码
    <ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    >
    <sys:Double x:Key="FontSize18">16.6</sys:Double>
    <sys:Double x:Key="FontSize22">17.6</sys:Double>
    <sys:Double x:Key="FontSize24">19.2</sys:Double>
    <sys:Double x:Key="FontSize30">23.8</sys:Double>
    <sys:Double x:Key="FontSize36">28.8</sys:Double>
    <sys:Double x:Key="FontSize48">38.4</sys:Double>
    <sys:Double x:Key="FontSize60">48.0</sys:Double>
    <sys:Double x:Key="FontSize72">57.6</sys:Double>
    </ResourceDictionary>
    
    复制代码

    现在我们要做的是用户选择不同的字体的时候加载不同的xaml文件就可以了,接下来我们来创建一个类Configuration 用来存储用户的配置信息同样用到了AppSettingHelper这个类,可以去Windows Phone开发经验谈(14)-动态的改变APP的语言里面获取...下面是Configuration的代码

    复制代码
    using System.ComponentModel;
    namespace ChinaBus.Utility
    {
    public class Configuration : INotifyPropertyChanged
    {
    Configuration()
    {
    this._fontSizePattern = AppSettingHelper.GetValueOrDefault<FontSizePattern>("FontSizePattern", FontSizePattern.Small);
    this._themesPattern = AppSettingHelper.GetValueOrDefault<ThemesPattern>("ThemesPattern", ThemesPattern.Blue);
     }
    private static Configuration _instance;
    public static Configuration Instance
    {
    get
    {
    if (Configuration._instance == null)
    {
    Configuration._instance = new Configuration();
    }
    return Configuration._instance;
    }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
    if (this.PropertyChanged != null)
    {
    this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    }
    private ThemesPattern _themesPattern;
    public ThemesPattern ThemesPattern
    {
    get
    {
    return this._themesPattern;
    }
    set
    {
    if (this._themesPattern != value)
    {
    this._themesPattern = value;
    this.UpdateValue("ThemesPattern", value);
    this.OnPropertyChanged("ThemesPattern");
    }
    }
    }
    private FontSizePattern _fontSizePattern;
    public FontSizePattern FontSizePattern
    {
    get
    {
    return this._fontSizePattern;
    }
    set
    {
    if (this._fontSizePattern != value)
    {
    this._fontSizePattern = value;
    this.UpdateValue("FontSizePattern", value);
    this.OnPropertyChanged("FontSizePattern");
    }
    }
    }
    private void UpdateValue(string key, object value)
    {
    AppSettingHelper.AddOrUpdateValue(key, value);
    }
    }
    }
    复制代码

    这时候再创建一个类叫做FontSizeProvider 用来加载xaml获取各种字体的大小,下面是完整的代码。

    复制代码
    using System;
    using System.ComponentModel;
    using System.Windows;
    using System.IO.IsolatedStorage;
    namespace ChinaBus.Utility
    {
    public class FontSizeProvider : INotifyPropertyChanged
    {
    private double _fontSize18;
    private double _fontSize22;
    private double _fontSize24;
    private double _fontSize30;
    private double _fontSize36;
    private double _fontSize48;
    private double _fontSize60;
    private double _fontSize72;
    public event PropertyChangedEventHandler PropertyChanged;
    public double F18
    {
    get
    {
    return this._fontSize18;
    }
    private set
    {
    if (this._fontSize18 != value)
    {
    this._fontSize18 = value;
    if (this.PropertyChanged != null)
    {
    this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("F18"));
    }
    }
    }
    }
    public double F22
    {
    get
    {
    return this._fontSize22;
    }
    private set
    {
    if (this._fontSize22 != value)
    {
    this._fontSize22 = value;
    if (this.PropertyChanged != null)
    {
    this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("F22"));
    }
    }
    }
    }
    public double F24
    {
    get
    {
    return this._fontSize24;
    }
    private set
    {
    if (this._fontSize24 != value)
    {
    this._fontSize24 = value;
    if (this.PropertyChanged != null)
    {
    this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("F24"));
    }
    }
    }
    }
    public double F30
    {
    get
    {
    return this._fontSize30;
    }
    private set
    {
    if (this._fontSize30 != value)
    {
    this._fontSize30 = value;
    if (this.PropertyChanged != null)
    {
    this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("F30"));
    }
    }
    }
    }
    public double F36
    {
    get
    {
    return this._fontSize36;
    }
    private set
    {
    if (this._fontSize36 != value)
    {
    this._fontSize36 = value;
    if (this.PropertyChanged != null)
    {
    this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("F36"));
    }
    }
    }
    }
    public double F48
    {
    get
    {
    return this._fontSize48;
    }
    private set
    {
    if (this._fontSize48 != value)
    {
    this._fontSize48 = value;
    if (this.PropertyChanged != null)
    {
    this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("F48"));
    }
    }
    }
    }
    public double F60
    {
    get
    {
    return this._fontSize60;
    }
    private set
    {
    if (this._fontSize60 != value)
    {
    this._fontSize60 = value;
    if (this.PropertyChanged != null)
    {
    this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("F60"));
    }
    }
    }
    }
    public double F72
    {
    get
    {
    return this._fontSize72;
    }
    private set
    {
    if (this._fontSize72 != value)
    {
    this._fontSize72 = value;
    if (this.PropertyChanged != null)
    {
    this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("F72"));
    }
    }
    }
    }
    public FontSizeProvider()
    {
    this.ApplyPattern();
    Configuration.Instance.PropertyChanged += new PropertyChangedEventHandler(this.Configuration_PropertyChanged);
    }
    private void Configuration_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
    if (string.Compare(e.PropertyName, "FontSizePattern", StringComparison.OrdinalIgnoreCase) == 0)
    {
    this.ApplyPattern();
    }
    }
    ~FontSizeProvider()
    {
    Configuration.Instance.PropertyChanged -= new PropertyChangedEventHandler(this.Configuration_PropertyChanged);
    }
    private void ApplyPattern()
    {
    FontSizePattern fontSizePattern = Configuration.Instance.FontSizePattern;
    string text = string.Format("/ChinaBus;component/Themes/Fonts/{0}.xaml", fontSizePattern.ToString());
    ResourceDictionary resourceDictionary = new ResourceDictionary();
    Application.LoadComponent(resourceDictionary, new Uri(text, UriKind.Relative));
    this.F18 = (double)resourceDictionary["FontSize18"];
    this.F22 = (double)resourceDictionary["FontSize22"];
    this.F24 = (double)resourceDictionary["FontSize24"];
    this.F30 = (double)resourceDictionary["FontSize30"];
    this.F36 = (double)resourceDictionary["FontSize36"];
    this.F48 = (double)resourceDictionary["FontSize48"];
    this.F60 = (double)resourceDictionary["FontSize60"];
    this.F72 = (double)resourceDictionary["FontSize72"];
    }
    }
    }
    复制代码

    这时候你在app.xaml加入下面代码

     <utility:FontSizeProvider x:Key="FontSizeProvider"/>

    这样你就可以在xaml里面使用,代码如下

     <TextBlock Text="{Binding BusDescription}"
    TextWrapping="Wrap"
    Margin="0,0,50,0" Foreground="{Binding Path=BusForeground, Source={StaticResource ThemeProvider}}"
    FontSize="{Binding Path=F18, Source={StaticResource FontSizeProvider}}" 


    当然在CS文件里面也同样可以使用,你可以用单例模式来实现..也可以每次都创建一个类来实现,代码如下

     FontSizeProvider fzp = new FontSizeProvider();
    var tbBacklineInfo = new TextBlock();
    tbBacklineInfo.TextWrapping = TextWrapping.Wrap;
    tbBacklineInfo.Text = info.BackLine;
    tbBacklineInfo.FontSize = fzp.F30;


    好了..介绍完了...有什么问题欢迎留言讨论!

    //-------------------------------------------------

    MVVM in real life Windows Phone applications Part1

    published on: 7/20/2011 | Views: 13404 | Tags: Mango MVVM

    Currently rated 4.71 by 7 people

    by WindowsPhoneGeek

    In this article I am going to talk about using MVVM in real life Windows Phone applications.

    You can take a look at our previous post: Windows Phone Mango: Getting Started with MVVM in 10 Minutes for reference. There I demonstrated what is MVVM and the basics of MVVM without any unnecessary complications. For the purposes of demonstration I used only one page without implementing any page navigation. However, in a real life Windows Phone application you will almost certainly have more than a single page, so you will need to navigate between them. That is why I have decided to start a series of a few posts that explain in details how to build a more complex Windows Phone application which uses MVVM, Page Navigation, Local Database, etc.

    Getting Started

    To begin with, lets take as a base the sample application we created previously in the Windows Phone Mango: Getting Started with MVVM in 10 Minutes article. We will add additional functionality step by step so that the final result should be a fully functional Windows Phone MVVM application. In short we have the following structure with two pages and navigation between them.:

    110-0

    Step1: First lets focus on the View. We will split the XAML into two pages:

    VIEW

    • EditPersonPage.xaml
    • MainPage.xaml

    MainPage.xaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
        <TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
        <TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        <Button Content="LoadData" Command="{Binding LoadDataCommand}" />
    </StackPanel>
     
    <ListBox Grid.Row="1" x:Name="listBox" ItemsSource="{Binding DataSource}"
             HorizontalContentAlignment="Stretch">
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem">
                <Setter Property="HorizontalContentAlignment" Value="Stretch" />
            </Style>
        </ListBox.ItemContainerStyle>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                 
                    <StackPanel Orientation="Horizontal" Grid.Row="0">
                        <TextBlock Text="Name:" Style="{StaticResource PhoneTextTitle2Style}" />
                        <TextBlock Text="{Binding Name}" Style="{StaticResource PhoneTextTitle2Style}" />
                    </StackPanel>
                    <StackPanel Orientation="Horizontal" Grid.Row="1">
                        <TextBlock Text="Age:" Margin="15,0,0,0" />
                        <TextBlock Text="{Binding Age}" />
                    </StackPanel>
     
                    <Button Content="Edit" HorizontalAlignment="Right" Margin="2" Grid.Column="1" Grid.RowSpan="2"
                            Command="{Binding DataContext.EditPersonCommand, ElementName=listBox}"
                            CommandParameter="{Binding DataContext, RelativeSource={RelativeSource TemplatedParent}}"/>
                     
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    In the ItemTemplate of the ListBox which is used to visualize each one of the items there are two TextBlock controls and a button (used to navigate to the edit page). The TextBlock bindings are trivial, just a simple binding to a property of the corresponding Person object. The button is more interesting because we want to bind it to a command placed inside our PersonViewModel class. What is more, we also want to pass as a parameter the corresponding Person object. In order to bind the button to the command, we crate a binding to the EditPersonCommand property of the object that is set as the DataContext (this is the PersonViewModel instance) of the control with name listBox. Next, to pass the corresponding Person object as a parameter to the command, we bind the CommandParameter property of the button to the DataContext property of its TemplatedParent. Since the Button is inside a DataTemplate, the TemplatedParent is the visual container element that is constructed using the template in order to display a Person object, which is set as its DataContext.

    NOTE: You can find the EditPersonCommand implementation in the PersonViewModel class given below.

    Step2: Implementing a ViewModel base class. The purpose of this class is to implement common functionality (ex: INotifyPropertyChanged) so that it is not repeated in every ViewModel.

    VIEW MODEL

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public virtual void Initialize(IDictionary<string, string> parameters)
        {
        }
     
        public event PropertyChangedEventHandler PropertyChanged;
     
        protected virtual void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
     
        protected T GetService<T>() where T : class
        {
            if (typeof(T) == typeof(INavigationService))
            {
                return new SimpleNavigationService() as T;
            }
            else if (typeof(T) == typeof(IPersonRepository))
            {
                return new SimplePersonRepository() as T;
            }
            return null;
        }
    }

    Step3: Implementing the SimplePersonRepository class: encapsulates operations over a collection of Person objects.

    NOTE: SimplePersonRepository is an implementation of IPersonRepository  interface which will allow us to later replace the implementation with another one:

    1
    2
    3
    4
    5
    public interface IPersonRepository
    {
        IEnumerable<Person> GetPersonList();
        Person GetPersonByID(int id);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class SimplePersonRepository : IPersonRepository
    {
        private static List<Person> PersonList = null;
     
        public IEnumerable<Person> GetPersonList()
        {
            if (PersonList == null)
            {
                PersonList = new List<Person>();
                PersonList.Add(new Person() { ID = 1, Name = "John", Age = 32 });
                PersonList.Add(new Person() { ID = 2, Name = "Kate", Age = 27 });
                PersonList.Add(new Person() { ID = 3, Name = "Sam", Age = 30 });
            }
     
            return PersonList;
        }
     
        public Person GetPersonByID(int id)
        {
            return PersonList.FirstOrDefault(p => p.ID == id);
        }
    }

     

    Step4: Split PersonViewModel class from the previous article in two, so that it now contains only functionality for providing the data source for the list control, and for invoking the person edit page. The rest of the functionality will be moved to a new EditPersonViewModel class, which we will be discussing in the next post. As a result we will have the following view model classes:

    • PersonViewModel
    • EditPersonViewModel

    PersonViewModel

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    public class PersonViewModel : ViewModelBase
    {
        private ObservableCollection<Person> personDataSource;
        private ICommand loadDataCommand;
        private ICommand editPersonCommand;
     
        public PersonViewModel()
        {
            this.loadDataCommand = new DelegateCommand(this.LoadDataAction);
            this.editPersonCommand = new DelegateCommand(this.EditPersonAction);
        }
     
        public override void Initialize(IDictionary<string, string> parameters)
        {
            base.Initialize(parameters);
     
            if (parameters.Count > 0)
            {
                this.LoadDataAction(null);
            }
        }
     
        private void LoadDataAction(object p)
        {
            IPersonRepository personRepository = this.GetService<IPersonRepository>();
            if (personRepository == null)
            {
                return;
            }
     
            IEnumerable<Person> personList = personRepository.GetPersonList();
            this.DataSource = new ObservableCollection<Person>(personList);
        }
     
        private void EditPersonAction(object p)
        {
            Person person = p as Person;
            if (person == null)
            {
                return;
            }
            INavigationService navigationService = this.GetService<INavigationService>();
            if (navigationService == null)
            {
                return;
            }
     
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add(Person.IdParameterKey, person.ID.ToString());
     
            navigationService.Navigate("/Views/EditPersonPage.xaml", parameters);
        }
     
        public ICommand LoadDataCommand
        {
            get
            {
                return this.loadDataCommand;
            }
        }
     
        public ICommand EditPersonCommand
        {
            get
            {
                return this.editPersonCommand;
            }
        }
     
        public ObservableCollection<Person> DataSource
        {
            get
            {
                if (this.personDataSource == null)
                {
                    this.personDataSource = new ObservableCollection<Person>();
                }
                return this.personDataSource;
            }
            private set
            {
                if (this.personDataSource != value)
                {
                    this.personDataSource = value;
                    this.RaisePropertyChanged("DataSource");
                }
            }
        }
    }

    In the PersonViewModel class above, you will find the EditPersonAction method, which implements the EditPersonCommand that is bound to the Edit buttons in the list box control. This method retrieves an INavigationService implementation and then uses it to navigate to the edit page, passing the ID of the edited Person object as a parameter.

    The INavigationService interface that is used by the view model classes to navigate to other pages looks like this:

    1
    2
    3
    4
    5
    public interface INavigationService
    {
        void Navigate(string uri);
        void Navigate(string uri, IDictionary<string, string> parameters);
    }

    In a next post we will be discussing how to create an implementation of the INavigationService interface, but again this interface will allow as to easily replace the implementation with another one.

    In this post I talked about MVVM in real life Windows Phone application Part1. The full source code will be available at the end of the next Part2 of the article. Stay tuned for the rest of the posts in this series.

    I hope that the post was helpful.


  • 相关阅读:
    强连通分量(Kosaraju)
    拓扑排序
    树状数组BIT
    差分
    RMQ(ST表)
    LCA(Tarjan)
    LCA(ST倍增)
    海亮SC2019 树上数数(转载)
    海亮SC
    【十二省联考2019】异或粽子/可持久化01trie
  • 原文地址:https://www.cnblogs.com/fx2008/p/2715149.html
Copyright © 2011-2022 走看看