Статьи

Windows Phone: Pizza Splitter

Сегодня я показываю исходный код моего приложения PizzaSplitter для Windows Phone.

Это простое и понятное приложение, которое не сохраняет настроек и имеет макет на основе StackPanel.

Загрузить решение PizzaSplitter.WP

Это решение требует Telerik RadControls для Windows Phone. Вы можете скачать пробную версию здесь.

Первая страница — MainPage.xaml:

<phone:PhoneApplicationPage
    x:Class="PizzaSplitter.WP.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:telerikInput="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Input"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
    shell:SystemTray.IsVisible="True" xmlns:my="clr-namespace:Microsoft.Advertising.Mobile.UI;assembly=Microsoft.Advertising.Mobile.UI">
 
    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
 
        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="PIZZA SPLITTER" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="slice divider" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>
 
        <!--ContentPanel - place additional content here-->
        <StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <StackPanel Height="80" Orientation="Horizontal">
                <TextBlock Text="Number Of People"
                           FontSize="30"
                           VerticalAlignment="Center"
                           HorizontalAlignment="Center"
                           TextAlignment="Center"
                           Width="250" />
                <telerikInput:RadNumericUpDown Name="NumberOfPeopleRadNumericUpDown"
                                               Value="5"
                                               Width="195"
                                               Margin="10,0,0,0"
                                               FontSize="36"
                                               BorderThickness="0" Style="{StaticResource RadNumericUpDownStyle1}"/>
            </StackPanel>
            <StackPanel Height="80" Orientation="Horizontal">
                <TextBlock  Text="Number Of Pizzas"
                            FontSize="30"  
                            VerticalAlignment="Center"
                            HorizontalAlignment="Center"
                            TextAlignment="Center"
                            Width="250"/>
                <telerikInput:RadNumericUpDown Name="NumberOfPizzasRadNumericUpDown"
                                               Value="3"
                                               Width="195"
                                               Margin="10,0,0,0"
                                               FontSize="36"
                                               TextOptions.TextHintingMode="Animated"
                                               BorderThickness="0" Style="{StaticResource RadNumericUpDownStyle1}"/>
            </StackPanel>
            <StackPanel Height="80" Orientation="Horizontal">
                <TextBlock  Text="Slices Per Pizza"
                            FontSize="30"
                            VerticalAlignment="Center"
                            HorizontalAlignment="Center"  
                            TextAlignment="Center"
                            Width="250"/>
                <telerikInput:RadNumericUpDown Name="SlicesPerPizzaRadNumericUpDown"
                                               Value="8"
                                               Width="195"
                                               Margin="10,0,0,0"
                                               FontSize="36"
                                               TextOptions.TextHintingMode="Animated"
                                               BorderThickness="0" Style="{StaticResource RadNumericUpDownStyle1}"/>
            </StackPanel>
            <Button Content="Calculate Split"
                    Margin="10,15,10,10"
                    Height="72"
                    HorizontalAlignment="Left"
                    Name="CalculateSplitButton"
                    VerticalAlignment="Top"
                    Width="440" Click="CalculateSplitButtonClick" />
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                <StackPanel Height="125" Width="200" Margin="10,10,5,10">
                    <TextBlock Name="SlicesPerPersonTextBlock"
                               Text="4"
                               FontSize="60"
                               TextAlignment="Center"
                               Foreground="Green"/>
                    <TextBlock Text="Slices Per Person" FontSize="24" TextAlignment="Center" />
                </StackPanel>
                <StackPanel Height="125" Width="200" Margin="5,10,10,10">
                    <TextBlock Name="RemainingSlicesTextBlock"
                               Text="4"
                               FontSize="60"
                               TextAlignment="Center"
                               Foreground="Red"/>
                    <TextBlock Text="Remaining Slices" FontSize="24" TextAlignment="Center" />
                </StackPanel>
            </StackPanel>
            <my:AdControl Height="80" Name="adControl1" Width="480" Margin="-15,10,0,0">
                <my:AdControl.ApplicationId>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</my:AdControl.ApplicationId>
                <my:AdControl.AdUnitId>xxxxxxxx</my:AdControl.AdUnitId>                
            </my:AdControl>
        </StackPanel>
    </Grid>
</phone:PhoneApplicationPage>

Возможно, вы заметили пользовательский StaticResource RadNumericUpDownStyle1, примененный к Telerik RadNumericUpDown. Мне нужно было убрать границу на кнопках увеличения и уменьшения. Это повлекло за собой открытие решения в Expression Blend и создание собственного стиля в элементе управления. Я использовал эту страницу документа. После сохранения в Blend я снова открыл решение в Visual Studio, и оно стало доступно для применения к другим экземплярам этого элемента управления. Я уверен, что мог бы сделать это в Blend, но я был немного не в себе, поэтому решил вернуться к VS2010 и просто кодировать его. Есть 3 стиля. Один для кнопок «вверх» и «вниз» и третий для всего элемента управления. Этот третий использует первые два. Blend помещает пользовательский стиль в файл App.xaml:

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:telerikInput="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Input"
    x:Class="PizzaSplitter.WP.App"
    >
 
    <!--Application Resources-->
    <Application.Resources>
        <Style x:Key="NumericUpDownRepeatButtonStyle" TargetType="Button">
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="BorderBrush" Value="{StaticResource PhoneForegroundBrush}"/>
            <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
            <Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}"/>
            <Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilySemiBold}"/>
            <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMediumLarge}"/>
        </Style>
        <Style x:Key="IncreaseButtonStyle" BasedOn="{StaticResource NumericUpDownRepeatButtonStyle}" TargetType="Button">
            <Setter Property="BorderThickness" Value="1, 0, 0, 0"/>
            <Setter Property="BorderBrush" Value="{StaticResource PhoneSubtleBrush}"/>
            <Setter Property="Padding" Value="25, 25, 25, 25"/>
            <Setter Property="VerticalAlignment" Value="Stretch"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ButtonBase">
                        <Grid Background="Transparent">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal"/>
                                    <VisualState x:Name="MouseOver"/>
                                    <VisualState x:Name="Pressed">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneContrastBackgroundBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneContrastForegroundBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Disabled">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ButtonBackground">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Transparent"/>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Border x:Name="ButtonBackground" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="0" Margin="{TemplateBinding Margin}">
                                <ContentControl x:Name="ContentContainer" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
                                    <Grid>
                                        <Rectangle Fill="{Binding Foreground, ElementName=ContentContainer}" Height="5" Width="17"/>
                                        <Rectangle Fill="{Binding Foreground, ElementName=ContentContainer}" Height="17" Width="5"/>
                                    </Grid>
                                </ContentControl>
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="DecreaseButtonStyle" BasedOn="{StaticResource NumericUpDownRepeatButtonStyle}" TargetType="Button">
            <Setter Property="BorderThickness" Value="1, 0, 0, 0"/>
            <Setter Property="Padding" Value="25, 25, 25, 25"/>
            <Setter Property="BorderBrush" Value="{StaticResource PhoneSubtleBrush}"/>
            <Setter Property="VerticalAlignment" Value="Stretch"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ButtonBase">
                        <Grid Background="Transparent">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal"/>
                                    <VisualState x:Name="MouseOver"/>
                                    <VisualState x:Name="Pressed">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneContrastBackgroundBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneContrastForegroundBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Disabled">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ButtonBackground">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Transparent"/>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Border x:Name="ButtonBackground" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="0" Margin="{TemplateBinding Margin}">
                                <ContentControl x:Name="ContentContainer" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
                                    <Grid>
                                        <Rectangle Fill="{Binding Foreground, ElementName=ContentContainer}" Height="5" Width="17"/>
                                    </Grid>
                                </ContentControl>
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="RadNumericUpDownStyle1" TargetType="telerikInput:RadNumericUpDown">
            <Setter Property="IncreaseButtonStyle" Value="{StaticResource IncreaseButtonStyle}"/>
            <Setter Property="DecreaseButtonStyle" Value="{StaticResource DecreaseButtonStyle}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="BorderBrush" Value="{StaticResource PhoneSubtleBrush}"/>
            <Setter Property="Margin" Value="{StaticResource PhoneHorizontalMargin}"/>
            <Setter Property="HorizontalContentAlignment" Value="Left"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Padding" Value="5"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="telerikInput:RadNumericUpDown">
                        <Border x:Name="LayoutRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition/>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="Auto"/>
                                </Grid.ColumnDefinitions>
                                <Grid.RowDefinitions>
                                    <RowDefinition/>
                                </Grid.RowDefinitions>
                                <Grid Grid.Column="0" Margin="{TemplateBinding Padding}" Grid.Row="0">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto"/>
                                        <ColumnDefinition/>
                                        <ColumnDefinition Width="Auto"/>
                                    </Grid.ColumnDefinitions>
                                    <TextBlock Grid.Column="0" HorizontalAlignment="Left" Text="{TemplateBinding Header}" VerticalAlignment="Center"/>
                                    <TextBlock x:Name="PART_ValueTextBlock" Grid.Column="1" FontFamily="Segoe WP Semibold" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="5, 0, 5, 0" Text="{TemplateBinding ValueString}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                                    <TextBlock Grid.Column="2" HorizontalAlignment="Right" Text="{TemplateBinding Suffix}" VerticalAlignment="Center"/>
                                </Grid>
                                <Button x:Name="PART_DecreaseButton" Grid.Column="1" BorderThickness="0"/>
                                <Button x:Name="PART_IncreaseButton" Grid.Column="2" BorderThickness="0"/>
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Application.Resources>
 
    <Application.ApplicationLifetimeObjects>
        <!--Required object that handles lifetime events for the application-->
        <shell:PhoneApplicationService
            Launching="Application_Launching" Closing="Application_Closing"
            Activated="Application_Activated" Deactivated="Application_Deactivated"/>
    </Application.ApplicationLifetimeObjects>
 
</Application>

Файл кода (MainPage.xaml.cs) содержит большую часть бизнес-логики для приложения.

 

using System;
using System.Globalization;
using System.Windows;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using Microsoft.Phone.Tasks;
 
namespace PizzaSplitter.WP
{
    public partial class MainPage : PhoneApplicationPage
    {
        private PizzaMath pm;
        public MainPage()
        {
            InitializeComponent();
 
            ApplicationBar = new ApplicationBar();
            ApplicationBar.IsMenuEnabled = true;
            ApplicationBar.IsVisible = true;
            ApplicationBar.Opacity = 1.0;
            ApplicationBar.Mode = ApplicationBarMode.Minimized;
 
            var helpItem = new ApplicationBarIconButton(new Uri("/Images/appbar.questionmark.rest.png", UriKind.Relative));
            var rateItem = new ApplicationBarIconButton(new Uri("/Images/appbar.favs.rest.png", UriKind.Relative));
 
            helpItem.Text = "help";
            rateItem.Text = "rate";
 
            helpItem.Click += HelpClick;
            rateItem.Click += RateItemOnClick;
 
            ApplicationBar.Buttons.Add(helpItem);
            ApplicationBar.Buttons.Add(rateItem);
 
            NumberOfPeopleRadNumericUpDown.Value = 3;
            NumberOfPizzasRadNumericUpDown.Value = 2;
            SlicesPerPizzaRadNumericUpDown.Value = 8;
 
            SlicesPerPersonTextBlock.Text = "0";
            RemainingSlicesTextBlock.Text = "0";
        }
        private void HelpClick(object sender, EventArgs eventArgs)
        {
            NavigationService.Navigate(new Uri("/Help.xaml", UriKind.Relative));
        }
        private void RateItemOnClick(object sender, EventArgs eventArgs)
        {
            MarketplaceReviewTask rate = new MarketplaceReviewTask();
            rate.Show();
        }
        private void CalculateSplitButtonClick(object sender, RoutedEventArgs e)
        {
            pm = new PizzaMath(   (int)NumberOfPeopleRadNumericUpDown.Value,
                                            (int)NumberOfPizzasRadNumericUpDown.Value,
                                            (int)SlicesPerPizzaRadNumericUpDown.Value);
            if (pm.Calculate())
            {
                SlicesPerPersonTextBlock.Text = pm.SlicesPerPerson.ToString(CultureInfo.InvariantCulture);
                RemainingSlicesTextBlock.Text = pm.SlicesRemaining.ToString(CultureInfo.InvariantCulture);
            }
        }
    }
}

И, наконец, класс PizzaMath:

using System;
 
namespace PizzaSplitter.WP
{
    public class PizzaMath
    {
        public int NumberOfPeople { get; private set; }
        public int NumberOfPizzas { get; private set; }
        public int SlicesPerPizza { get; private set; }
 
        public int SlicesPerPerson { get; private set; }
        public int SlicesRemaining { get; private set; }
 
        public PizzaMath(int numberOfPeople, int numberOfPizzas, int slicesPerPizza)
        {
            NumberOfPeople = numberOfPeople;
            NumberOfPizzas = numberOfPizzas;
            SlicesPerPizza = slicesPerPizza;
            SlicesPerPerson = 0;
            SlicesRemaining = 0;
        }
        public bool Calculate()
        {
            try
            {
                SlicesRemaining = (NumberOfPizzas * SlicesPerPizza) % NumberOfPeople;
                SlicesPerPerson = (((NumberOfPizzas * SlicesPerPizza) - SlicesPerPerson) / NumberOfPeople);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
}