Поиск на сайте: Расширенный поиск


Новые программы oszone.net Читать ленту новостей RSS
CheckBootSpeed - это диагностический пакет на основе скриптов PowerShell, создающий отчет о скорости загрузки Windows 7 ...
Вы когда-нибудь хотели создать установочный диск Windows, который бы автоматически установил систему, не задавая вопросо...
Если после установки Windows XP у вас перестала загружаться Windows Vista или Windows 7, вам необходимо восстановить заг...
Программа подготовки документов и ведения учетных и отчетных данных по командировкам. Используются формы, утвержденные п...
Red Button – это мощная утилита для оптимизации и очистки всех актуальных клиентских версий операционной системы Windows...

Судоку для Windows Phone 7

Текущий рейтинг: 3.33 (проголосовало 3)
 Посетителей: 1396 | Просмотров: 1799 (сегодня 1)  Шрифт: - +

Судоку стала популярной игрой за последнее десятилетие, прочно поселившись в большинстве газет на страницах рядом с кроссвордами. Созданы игровые шоу, в основу которых положена судоку. Если вы не знаете об этой игре, то ее суть заключается в расстановке чисел. Игровое поле имеет размер 9x9 клеток; цель игры — расставить числа от 1 до 9 так, чтобы в каждой строке, столбце или внутреннем поле 3x3 числа не повторялись. Сама природа этой игры позволяет комфортно играть в нее на портативных устройствах, и устройства с Windows Phone 7 не должны быть исключением. После недавнего выпуска Windows Phone 7 можно ожидать появления на рынке множества приложений-судоку, и вы можете даже внести свою лепту, следуя этой статье.

Введение в MVVM

Мое приложение примерно соответствует проектировочному шаблону Model-View-ViewModel (MVVM). Хотя реальных моделей не будет (поскольку этому приложению не требуется хранилище в виде базы данных), все равно этот шаблон является отличным средством обучения, так как в его основе на самом деле лежит ViewModel.

На освоение шаблона MVVM потребуется некоторое время, но, как только все встанет на свои места, вы обнаружите, что он обеспечивает отличное разделение UI- и прикладной логики. Более того, он открывает мощь механизма связывания с данными в Silverlight, в то же время освобождая вас от написания большей части рутинного кода обновления UI (FirstNameTextBox.Text = MyPerson.FirstName канет в прошлое!). Подробнее о связывании с данными в Silverlight, см. статью «Data Binding» в MSDN Library по ссылке tinyurl.com/SLdatabind.

Из-за размера и простоты этого приложения, а также целей данной статьи сторонняя инфраструктура MVVM не используется. Однако вполне вероятно, что со временем ваше приложение вырастет в нечто более сложное, и тогда вы поступите мудро, если начнете работать с какой-нибудь сторонней инфраструктурой вроде MVVM Light Toolkit (mvvmlight.codeplex.com). Она предоставит бесплатный протестированный код, который иначе рано или поздно вам пришлось бы писать самостоятельно (замечено по опыту).

Создание приложения

После установки средств разработки с create.msdn.com начните с создания нового проекта Windows Phone 7, открыв Visual Studio и выбрав File | New | Project, а затем (в диалоге нового проекта) Visual C# | Silverlight for Windows Phone | Windows Phone Application. Создайте две папки — Views и ViewModels, — следуя шаблону MVVM. В этот момент вы также можете начать отладку, если хотите взглянуть на эмулятор, предоставляемый в составе SDK.

Игру «судоку» можно разбить на три концепции: игровое поле размером 9х9 состоит из 81 клетки, все игровое поле разбито на квадраты, каждый квадрат надо заполнить числами от 1 до 9. Чтобы создать представления для этих элементов, щелкните правой кнопкой мыши папку Views и выберите Add | New Item. Укажите в диалоге Windows Phone User Control присвойте имя первому файлу:GameBoardView.xaml. Повторите то же самое для SquareView.xaml и InputView.xaml. Теперь в папку ViewModel добавьте следующие классы: GameBoardViewModel и SquareViewModel. Представление ввода (Input View) не потребует ViewModel. Вам также понадобиться создать базовый класс для своих ViewModel, чтобы избежать дублирования кода. Добавьте класс ViewModelBase в папку ViewModels. К этому моменту ваше решение должно выглядеть аналогично показанному на рис. 1.

*

Рис. 1 Судоку для Windows Phone 7 с Views и ViewModels

Базовый класс ViewModelBase

Класс ViewModelBase должен реализовать интерфейс INotifyPropertyChanged из System.ComponentModel. Этот интерфейс позволяет связывать открытые свойства в ViewModels с элементами управления в представлениях. Реализация интерфейса INotifyPropertyChanged довольно проста:нужно реализовать лишь событие PropertyChanged. Ваш класс ViewModelBase.cs должен выглядеть аналогично показанному ниже (не забудьте о выражении using для System.ComponentModel):

public class ViewModelBase : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler
    PropertyChanged;
  private void NotifyPropertyChanged(String info)
  {
    if (PropertyChanged != null)
    {
      PropertyChanged(this,
        new PropertyChangedEventArgs(info));
    }
  }
}

Большинство сторонних инфраструктур MVVM включают базовый класс ViewModel, содержащий этот стереотипный код. Все ваши классы ViewModel будут наследовать от ViewModelBase. Для свойств в ViewModel, с которыми будет связан UI, нужно вызывать NotifyPropertyChanged в аксессоре set. Это позволяет автоматически обновлять UI при изменении значения какого-либо свойства. Реализовать все свойства таким способом довольно утомительно, и это в каком-то смысле расплата за то, что вам не требуется писать код, обновляющий UI.

Реализация индивидуальных клеток

Начнем с реализации класса SquareViewModel. Добавьте открытые свойства Value, Row и Column целочисленного типа, а также IsSelected, IsValid и IsEditable булева типа. Хотя UI можно напрямую связать со свойством Value, это вызовет проблемы, потому что в «неосвоенных» клетках будет отображаться нулевое значение. Чтобы избежать этого, можно либо реализовать конвертер привязки (binding converter), либо создать свойство StringValue только для чтения, которое будет возвращать пустую строку при нулевом значении свойства Value.

SquareViewModel будет также отвечать за уведомление UI о своем текущем состоянии. В этом приложении у каждого квадрата (клетки) может быть четыре состояния:Default, Invalid, Selected и UnEditable. Обычно в этом случае используют перечисление, но в инфраструктуре Silverlight у них нет нескольких методов, имеющихся в полной версии Microsoft .NET Framework. Это вызывает исключение при сериализации, поэтому состояния были реализованы как константы:

public class BoxStates
{
  public const int Default = 1;
  public const int Invalid = 2;
  public const int Selected = 3;
  public const int UnEditable = 4;
}

Теперь откройте SquareView.xaml. Вы заметите, что на уровне элементов управления были применены некоторые стили, определяющие размер и цвет шрифта. Набор предопределенных стилей обычно содержится в отдельном файле ресурсов, но в данном случае Windows Phone 7 сама предоставляет их вашему приложению по умолчанию. Ресурсы описаны на странице MSDN Library «Theme Resources for Windows Phone» по ссылке tinyurl.com/WP7Resources. Некоторые из этих стилей будут использоваться в приложении, чтобы его цветовая гамма соответствовала выбранной теме в ОС. Тему можно выбрать в эмуляторе, перейдя на главный экран и щелкнув стрелку, а затем Settings | Theme. Там вы сможете изменить нужные вам цвета (рис. 2).

*

Рис. 2 Экран настройки темы в Windows Phone 7

В сетку в SquareView.xaml поместите Border и TextBlock:

<Grid x:Name="LayoutRoot" MouseLeftButtonDown=
    "LayoutRoot_MouseLeftButtonDown">
    <Border x:Name="BoxGridBorder"
      BorderBrush="{StaticResource PhoneForegroundBrush}"
      BorderThickness="{Binding Path=BorderThickness}">
      <TextBlock x:Name="MainText"
        VerticalAlignment="Center" Margin="0" Padding="0"
        TextAlignment="Center" Text=
        "{Binding Path=StringValue}">
      </TextBlock>
    </Border>
  </Grid>

Отделенный код для SquareView.xaml.cs см. в исходном коде, который можно скачать для этой статьи. Конструктор требует передачи экземпляра SquareViewModel. Он будет предоставлен после связывания игровой доски. Кроме того, когда пользователь щелкает в границах сетки, генерируется собственное событие. Использование собственных событий — один из способов, обеспечивающих взаимодействие разных ViewModel друг с другом; однако в более крупных приложениях такой подход приведет к путанице. Другой вариант — реализовать класс Messenger, который упрощает взаимодействие. В большинстве инфраструктур MVVM класс Messenger (иногда называемый Mediator) уже имеется.

С точки зрения борцов за чистоту шаблона MVVM, обновление UI с использованием отделенного кода может показаться создающим мешанину, но необходимые элементы не так-то просто обработать через BindingConverter. BorderThickness объекта BoxGridBorder основано на двух свойствах, а кисти Foreground и Background берутся из ресурсов приложения, которые недоступны в BindingConverter.

Реализация игровой доски

Теперь можно реализовать представление GameBoard и ViewModel. Это представление — просто сетка 9x9. Почти так же прост отделенный код (см. комплект кода, который можно скачать для этой статьи) — всего лишь открытое свойство, предоставляющее ViewModel, и парочка закрытых методов для обработки щелчка дочернего поля и связывания с игровой сеткой.

ViewModel содержит основной объем кода. Он включает методы проверки игровой доски после ввода пользователя, решения головоломки и сохранения/загрузки игровой доски из хранилища. Игровая доска сериализуется в XML при сохранении, а для записи в файл используется IsolatedStorage. Полную реализацию см. в скачиваемом исходном коде; наиболее интересен код для работы с хранилищем, поэтому он и показан на рис. 3 (заметьте, что вы должны добавить ссылку на System.Xml.Serialization).

Рис. 3. Код для работы с хранилищем игровой доски

public void SaveToDisk()
{
  using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
  {
    if (store.FileExists(FileName))
    {
      store.DeleteFile(FileName);
    }

    using (IsolatedStorageFileStream stream = store.CreateFile(FileName))
    {
      using (StreamWriter writer = new StreamWriter(stream))
      {
        List<SquareViewModel> s = new List<SquareViewModel>();
        foreach (SquareViewModel item in GameArray)
          s.Add(item);

        XmlSerializer serializer = new XmlSerializer(s.GetType());
        serializer.Serialize(writer, s);
      }
    }
  }
}

public static GameBoardViewModel LoadFromDisk()
{
  GameBoardViewModel result = null;

  using (IsolatedStorageFile store = IsolatedStorageFile.
    GetUserStoreForApplication())
  {
    if (store.FileExists(FileName))
    {
      using (IsolatedStorageFileStream stream =
        store.OpenFile(FileName, FileMode.Open))
      {
        using (StreamReader reader = new StreamReader(stream))
        {
          List<SquareViewModel> s = new List<SquareViewModel>();
          XmlSerializer serializer = new XmlSerializer(s.GetType());
          s = (List<SquareViewModel>)serializer.Deserialize(
            new StringReader(reader.ReadToEnd()));

          result = new GameBoardViewModel();
          result.GameArray = LoadFromSquareList(s);
        }
      }
    }
  }

  return result;
}

Реализация сетки ввода

Представление ввода также довольно простое — всего несколько кнопок, вложенных в элементы управления StackPanel. Отделенный код, показанный на рис. 4, предоставляет собственное событие для передачи значения нажатой кнопки в приложение и два метода, помогающих поддерживать игру в портретном и ландшафтном режимах.

Рис. 4. Отделенный код для представления ввода

public event EventHandler SendInput;

private void UserInput_Click(object sender, RoutedEventArgs e)
{
  int inputValue = int.Parse(((Button)sender).Tag.ToString());
  if (SendInput != null)
      SendInput(inputValue, null);
}

public void RotateVertical()
{
  TopRow.Orientation = Orientation.Vertical;
  BottomRow.Orientation = Orientation.Vertical;
  OuterPanel.Orientation = Orientation.Horizontal;
}

public void RotateHorizontal()
{
  TopRow.Orientation = Orientation.Horizontal;
  BottomRow.Orientation = Orientation.Horizontal;
  OuterPanel.Orientation = Orientation.Vertical;
}

Связывание представлений в MainPage.xaml

Наконец, приложения составляются воедино с помощью реализации MainPage.xaml. Представления Input и GameBoard помещаются в сетку. Это приложение потребует использования всего пространства экрана, поэтому PageTitle TextBlock, автоматически вставленный при создании проекта, нужно удалить. ApplicationTitle TextBlock будет виден только в портретном режиме. Также мы задействуем преимущества панели приложения (Application Bar) в Windows Phone 7. Применяя Application Bar, вы придадите своему приложению стиль, принятый в Windows Phone 7, и предоставите игре «судоку» корректный интерфейс, позволяющий пользователю решать, сбрасывать и запускать новую головоломку:

<phone:PhoneApplicationPage.ApplicationBar>
   <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
     <shell:ApplicationBarIconButton x:Name="NewGame"
      IconUri="/Images/appbar.favs.rest.png" Text="New Game"
      Click="NewGame_Click"></shell:ApplicationBarIconButton>
     <shell:ApplicationBarIconButton x:Name="Solve"
      IconUri="/Images/appbar.share.rest.png" Text="Solve"
      Click="Solve_Click"></shell:ApplicationBarIconButton>
     <shell:ApplicationBarIconButton x:Name="Clear"
      IconUri="/Images/appbar.refresh.rest.png" Text="Clear"
      Click="Clear_Click"></shell:ApplicationBarIconButton>
  </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Изображения взяты из набора значков, предоставляемого Microsoft специально для Windows Phone 7; они устанавливаются с инструментарием в C:\\Program Files (x86)\\Microsoft SDKs\\Windows Phone\\v7.0\\Icons. После импорта изображений в проект выберите свойства изображения и измените Build Action с Resource на Content, а Copy to Output Directory — с Do Not Copy на Copy If Newer.

Последний фрагмент головоломки этого приложения — реализация отделенного кода MainPage. В конструкторе свойство SupportedOrientations устанавливается в true, чтобы разрешить поворот экрана приложения при повороте устройства пользователем. Кроме того, обрабатывается событие SendInput представления InputView, и введенное значение пересылается в GameBoard:

public MainPage()
{
  InitializeComponent();
  SupportedOrientations = SupportedPageOrientation.Portrait |
    SupportedPageOrientation.Landscape;
  InputControl.SendInput += new
    EventHandler(InputControl_SendInput);
}

void InputControl_SendInput(object sender, EventArgs e)
{
  MainBoard.GameBoard.SendInput((int)sender);
}

Для обработки загрузки и сохранения игровой доски нужно реализовать методы Navigation:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
  GameBoardViewModel board =
    GameBoardViewModel.LoadFromDisk();
  if (board == null)
    board = GameBoardViewModel.LoadNewPuzzle();

  MainBoard.GameBoard = board;
  base.OnNavigatedTo(e);
}

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
  MainBoard.GameBoard.SaveToDisk();
  base.OnNavigatedFrom(e);
}

Когда устройство поворачивается, приложение получает уведомление. Именно в момент обработки этого уведомления InputView перемещается из нижней части игровой доски в ее правую часть и поворачивается ( рис. 5).

Рис. 5. Код для обработки поворота устройства

protected override void OnOrientationChanged(OrientationChangedEventArgs e)
{
  switch (e.Orientation)
  {
    case PageOrientation.Landscape:
    case PageOrientation.LandscapeLeft:
    case PageOrientation.LandscapeRight:
      TitlePanel.Visibility = Visibility.Collapsed;
      Grid.SetColumn(InputControl, 1);
      Grid.SetRow(InputControl, 0);
      InputControl.RotateVertical();
      break;
    case PageOrientation.Portrait:
    case PageOrientation.PortraitUp:
    case PageOrientation.PortraitDown:
      TitlePanel.Visibility = Visibility.Visible;
      Grid.SetColumn(InputControl, 0);
      Grid.SetRow(InputControl, 1);
      InputControl.RotateHorizontal();
      break;
    default:
      break;
  }
  base.OnOrientationChanged(e);
}

Здесь же обрабатываются щелчки элементов меню:

private void NewGame_Click(object sender, EventArgs e)
{
  MainBoard.GameBoard = GameBoardViewModel.LoadNewPuzzle();
}

private void Solve_Click(object sender, EventArgs e)
{
  MainBoard.GameBoard.Solve();
}

private void Clear_Click(object sender, EventArgs e)
{
  MainBoard.GameBoard.Clear();
}

Вот и все, игра реализована, и в нее можно играть (рис. 6 и 7).

*

Рис. 6. Судоку в портретном режиме

*

Рис. 7. Решенная игра в ландшафтном режиме

В этой статье мы реализовали интересную игру, которая помогает скоротать время, стоя в очередной пробке. На ее примере я продемонстрировал, как приступить к созданию приложений Windows Phone 7 на основе Silverlight. Кроме того, вы увидели, как применять сериализацию и пользовательское хранилище для сохранения состояния приложения, а также как обеспечить поддержку приложением нескольких ориентаций экрана. Вдобавок вы ознакомились с шаблоном MVVM и научились связыванию данных в нем.

Автор: Эдам Миллер  •  Иcточник: MSDN Magazine  •  Опубликована: 20.07.2011
Нашли ошибку в тексте? Сообщите о ней автору: выделите мышкой и нажмите CTRL + ENTER
Теги:   Windows Phone 7, Судоку.


Оценить статью:
Вверх
Комментарии посетителей
Комментарии отключены. С вопросами по статьям обращайтесь в форум.