Windows Phone 8: Создаем приложение. Матрица. Часть 1
Windows Phone 8: Создаем приложение. Матрица. Часть 2
Здравствуйте. Сегодня мы продолжим создание приложения, используем новый шаблон «Панорама», а так же добавим всевозможные настройки, что позволит изменять все параметры матрицы. Сразу оговорюсь, что при задании сильно больших чисел в некоторых настройках, резко понижается производительность, но с этим мы еще будем бороться в следующих частях.
Так же хочу сразу вспомнить основные цели этих статей: это написание приложения с использованием максимального числа всего: различных контролов, техник, шаблонов и т.д. Ну а так же получение конечного продукта с заданными свойствами.
Хотелось вторую часть написать полностью отточенную, в плане кода, однако размеры ее стали достаточно большими. Если сразу провести еще и оптимизацию, то это усложнит прослеживание логики. Оптимизация будет в следующей части. Начнем.
Можно посмотреть эти настройки на видео:
Создаем новый проект. Приложение Windows Phone с панорамой.
У меня имя будет SE_Matrix_2d_v_4.
Остальное как в прошлый раз.
Тут у нас добавилось немного кода. В шаблоне Панорама у меня 3 секции:
В первый элемент добавим непосредственно отображение матрицы. Тут ничего не поменялось, все как и в первой части:
Во второй элемент управления добавлены первые 9 настроек из 13, описанных в начале статьи. Ничего сложного: название в TextBlock, TextBox для ввода значений и Button для применения заданного значения. Так же все обернуто в ScrollViewer для возможности вертикальной прокрутки, так как список в один экран не влезает. В большинстве TextBox метод ввода выбран как числа, InputScope=«Number». Однако в паре элементов есть возможность вводить отрицательные значения. Временно используется общая клавиатура InputScope=«Default».
В третьем элементе остались последние 4 настройки: выбор языка, цвета и задание цвета первому элементу, фону, градиента для змейки. Так же все находится в ScrollViewer для вертикальной прокрутки. Однако тут пришлось использовать один нестандартный элемент управления, а именно ColorPicker. Его можно найти в дополнительной библиотеке элементов с названием Coding4Fun.
У меня последние пару недель с нугет проблемы, поэтому качал вручную.
Переходим по ссылке.
Нажимаем на большую красную кнопку с надписью «Via CodePlex Current Release Zip».
Качаем архив с именем «Coding4Fun.Toolkit (Windows Phone 8).zip».
Из архива копируем файл с именем «Coding4Fun.Toolkit.Controls.dll» к себе в проект.
Заходим в проект в папочку «References». Жмем на нее правой кнопкой — Добавить ссылку — Справа внизу выбираем Обзор… Находим нашу библиотеку и жмем ОК.
Заходим в Панель элементов. правой кнопкой где угодно в ее приделах — Выбрать элементы… Выбираем вкладку Windows Phone Components. Справа внизу жмем обзор и снова выбираем Coding4Fun.Toolkit.Controls.dll. Новые элементы будут подсвечены. Смотрим, что б возле элемента ColorPicker была галочка.
Убедитесь, что в начале файла добавилась строчка
Продолжим. Перетаскиваем ColorPicker с Панели элементов. Назначаем имя. Теперь выбирать цвет будем на одном элементе, а применять выбранный цвет разными кнопками к разным характеристикам матрицы.
Так же добавим кнопку, при нажатии на которую будет появляться всплывающее окно для выбора языка:
И еще пара кнопок по аналогии со вторым элементом панорамы.
Внешне настройки выглядят так:
Первым делом создаем свойства класса, которые будут отвечать за настройки:
Рассмотри конструктор класса:
Давайте начнем с выбора цвета. Для начала инициализируем настройки по умолчанию в методе BeginColorSettings();
Задаем цвет фона. Просто присваиваем соответствующему атрибуту цвет, состоящий из компонент ARGB. Почему четыре составляющие? Прозрачность (А) нам будет нужна для создания эффекта затухания.
Однако это просто метод. Для того, что б цвет действительно поменялся, нужно забрать его из контролла ColorPicker, присвоить свойству и вызвать метод ChangeBackground:
Изменим цвет первого символа змейки. Первый символ змейки вызывается в методе RandomElementQ_Async асинхронно:
Ну вот и добрались до градиента. Первое, что нужно сделать — это в обработчиках событий соответствующих кнопок сохранить цвет в свойства класса:
Теперь эти цвета нужно применить к нашим символам. Переходим в метод RandomElementQ_Async и немножко меняем ту часть, которая отвечает за расчет яркости и цвета символов следующим образом:
С цветами разобрались. Теперь давайте решим вопрос с выбором языка. В конструкторе класса мы вызывали метод ListLanguages для инициализации доступных языков. Рассмотрим его более подробно:
Теперь добавляем обработчик кнопки, в которой показывается выбранный язык и при нажатии на которую появляется всплывающее окно:
В сплывающем окне находятся кнопки, при нажатии на которые и выбирается нужный язык. У всех кнопок назначен один и тот же обработчик событий, который определяет контент кнопки, который и является ключом в словаре для выбора языка. Задаем значение выбранного языка в свойство класса actualLanguage:
Теперь все готово, для того, что б можно было выбрать случайный символ из заданного диапазона. Напишем метод RandomActualSymbol, который и будет возвращать случайный символ:
И подставим вызов этого метода везде, где нужно задать символ. Например, в методе Change:
Тут все очень просто. В зависимости от флага flagOnOff, который задан как свойство класса завершаем циклы, в которых происходит обработка непосредственно самой матрицы.
Задаем обработчики событий кнопок «Старт» и «Стоп», которые просто меняют состояние флага на противоположный:
И добавляем внутри нескольких циклов простое условие
А именно в циклах методов Event_Grid_Tap_LayoutRoot, RandomElementQ_Async, Change:
Почему аж в четырех местах? Если змеек мало, то все нормально. Но если количество змеек неприлично большое, например, 50+, то матрица останавливается с заметными тормозами. А так мы останавливаем непосредственно каждый элемент, принимающий участие в работе механизма матрицы.
Ну и напоследок добавим красоты, для задания цвета кнопки, которая сейчас нажата. В конструкторе за это отвечает этот код:
Точнее сказать, задания цвета кнопки при инициализации приложения.
При срабатывании события Event_Button_Click_Clear происходит тоже самое, что и в CreateElement, только без создания элементов. Перебираются все элементы и как символ задается пустота:
Опять все исходит от флага turnOnOff. Что мы делаем? Останавливаем матрицу. Меняем надпись на кнопке. Меняем местами количество строк и столбцов. Удаляем матрицу. Создаем матрицу снова, вызывая метод CreateElement:
Логика схожа с поворотом. Единственная разница в том, что мы не меняем местами количество строк и столбцов, а добавляем к расчету количества строк и столбцов коэффициент, введенный пользователем:
Выполнены по одному шаблону. Получаем введенное пользователем значение, записываем его в соответствующее свойство класса, которое и подставляем в нужном месте, вместо статических значений которые были в предыдущей части.
Например количество одновременно ползущих змеек:
Вторая часть окончена. Знаю, что код немного корявый, но как на меня так наиболее легко проследить логику. В следующей части займемся оптимизацией кода, сохранением настроек при выходе из приложения, локализацией под английский язык, привязкой к данным.
П.С. Если знаете как лучше сделать на данном этапе — прошу в комментарии. И не забывайте, что приложение создается этапами и будут еще как минимум 2 части… Буду благодарен, если подскажете, как решить проблему на последнем видео, так как панорама сдвигается только, если «хватать» за пределами ScrollViewer.
Windows Phone 8: Создаем приложение. Матрица. Часть 2
Здравствуйте. Сегодня мы продолжим создание приложения, используем новый шаблон «Панорама», а так же добавим всевозможные настройки, что позволит изменять все параметры матрицы. Сразу оговорюсь, что при задании сильно больших чисел в некоторых настройках, резко понижается производительность, но с этим мы еще будем бороться в следующих частях.
Так же хочу сразу вспомнить основные цели этих статей: это написание приложения с использованием максимального числа всего: различных контролов, техник, шаблонов и т.д. Ну а так же получение конечного продукта с заданными свойствами.
Скриншот работы приложения
Хотелось вторую часть написать полностью отточенную, в плане кода, однако размеры ее стали достаточно большими. Если сразу провести еще и оптимизацию, то это усложнит прослеживание логики. Оптимизация будет в следующей части. Начнем.
Цели этой части
- Использовать шаблон Панорама
- Создание настроек приложения
Добавлены следующие настройки
- Скорость смены символов в заданном диапазоне
- Количество змеек в очереди
- Количество одновременно ползущих змеек за нажатие
- Размер шрифта
- Старт / Стоп
- Очистка экрана
- Размер(количество) клеточек в матрице
- Длина змейки в заданном диапазоне
- Горизонтальная / Вертикальная ориентация
- Выбор языка падающих символов
- Цвет фона матрицы
- Цвет первого символа
- Градиент для змейки
Можно посмотреть эти настройки на видео:
Перейдем к работе в Visual Studio
Создаем новый проект. Приложение Windows Phone с панорамой.
У меня имя будет SE_Matrix_2d_v_4.
Остальное как в прошлый раз.
MainPage.xaml
Тут у нас добавилось немного кода. В шаблоне Панорама у меня 3 секции:
<!--Элемент управления Panorama-->
<phone:Panorama Title="">
<phone:Panorama.Background>
<ImageBrush />
</phone:Panorama.Background>
<!--Первый элемент Panorama-->
<phone:PanoramaItem >
...
</phone:PanoramaItem>
<!--Второй элемент Panorama-->
<phone:PanoramaItem>
...
</phone:PanoramaItem>
<!--Третий элемент Panorama-->
<phone:PanoramaItem>
...
</phone:PanoramaItem>
</phone:Panorama>
В первый элемент добавим непосредственно отображение матрицы. Тут ничего не поменялось, все как и в первой части:
<!--Первый элемент Panorama-->
<phone:PanoramaItem >
<Grid x:Name="LayoutRootSecond" Background="Black" Margin="0,-32,-2,7" RenderTransformOrigin="0.500,0.500" Tap="Event_Grid_Tap_LayoutRoot"/>
</phone:PanoramaItem>
Во второй элемент управления добавлены первые 9 настроек из 13, описанных в начале статьи. Ничего сложного: название в TextBlock, TextBox для ввода значений и Button для применения заданного значения. Так же все обернуто в ScrollViewer для возможности вертикальной прокрутки, так как список в один экран не влезает. В большинстве TextBox метод ввода выбран как числа, InputScope=«Number». Однако в паре элементов есть возможность вводить отрицательные значения. Временно используется общая клавиатура InputScope=«Default».
Код. Второй элемент Panorama
<!--Второй элемент Panorama-->
<phone:PanoramaItem>
<Grid x:Name="LayoutRootThierd" Grid.Column="1" Background="Transparent" Margin="0, 0,-2,7" RenderTransformOrigin="0.500,0.500" >
<ScrollViewer HorizontalScrollBarVisibility="Auto" >
<Grid x:Name="Grid_SettingsRight" Grid.Column="1" Background="Transparent" Margin="0, 0,-2,7" RenderTransformOrigin="0.500,0.500" >
<!-- Скорость падения змейки в мс/ Скорость смены символов в мс -->
<TextBlock HorizontalAlignment="Left" Margin="28,3,0,0" TextWrapping="Wrap" Text="Скорость смены символов в мс" VerticalAlignment="Top" Width="352"/>
<TextBox x:Name="TextBox_SppeedFrom" InputScope="Number" HorizontalAlignment="Left" Height="67" TextWrapping="Wrap" Text="20" VerticalAlignment="Top" Width="100" Margin="9,28,0,0"/>
<TextBox x:Name="TextBox_SppeedTo" InputScope="Number" HorizontalAlignment="Left" Height="67" TextWrapping="Wrap" Text="40" VerticalAlignment="Top" Width="100" Margin="114,28,0,0"/>
<Button x:Name="Button_SpeedApplay" Content=" Ok " HorizontalAlignment="Left" Height="65" Margin="288,30,0,0" VerticalAlignment="Top" Click="Event_Button_Click_SpeedApplay"/>
<!-- Количество змеек в очереди -->
<TextBlock HorizontalAlignment="Left" Margin="28,95,0,0" TextWrapping="Wrap" Text="Количество змеек в очереди" VerticalAlignment="Top" RenderTransformOrigin="-0.256,0.233" Width="352"/>
<TextBox x:Name="TextBox_CountQueue" InputScope="Number" HorizontalAlignment="Left" Height="67" Margin="10,127,0,0" TextWrapping="Wrap" Text="5" VerticalAlignment="Top" Width="101" RenderTransformOrigin="0.49,-0.049"/>
<Button x:Name="Button_CountQueue" Content=" Ok " HorizontalAlignment="Left" Margin="287,127,0,0" VerticalAlignment="Top" Click="Event_Button_Click_CountQueue" RenderTransformOrigin="1.474,0.483" Height="67"/>
<!-- Количество змеек за нажатие -->
<TextBlock HorizontalAlignment="Left" Margin="28,194,0,0" TextWrapping="Wrap" Text="Количество змеек за нажатие" VerticalAlignment="Top" Width="352"/>
<TextBox x:Name="TextBox_СountSimultaneously" InputScope="Number" HorizontalAlignment="Left" Height="72" Margin="10,221,0,0" TextWrapping="Wrap" Text="1" VerticalAlignment="Top" Width="101"/>
<Button x:Name="Button_СountSimultaneously" Content=" Ok " HorizontalAlignment="Left" Margin="288,221,0,0" VerticalAlignment="Top" Click="Event_Button_Click_СountSimultaneously" RenderTransformOrigin="1.474,0.483"/>
<!-- Размер шрифта -->
<TextBlock HorizontalAlignment="Left" Margin="27,293,0,0" TextWrapping="Wrap" Text="Размер шрифта (+ - [n])" VerticalAlignment="Top" Width="352"/>
<TextBox x:Name="TextBox_FontSize" InputScope="Default" HorizontalAlignment="Left" Height="72" Margin="9,320,0,0" TextWrapping="Wrap" Text="-2" VerticalAlignment="Top" Width="101"/>
<Button x:Name="Button_FontSize" Content=" Ok " HorizontalAlignment="Left" Margin="288,320,0,0" VerticalAlignment="Top" Click="Event_Button_Click_FontSize" RenderTransformOrigin="1.474,0.483"/>
<!-- Количество смены символов в ячейке -->
<TextBlock HorizontalAlignment="Left" Margin="28,392,0,0" TextWrapping="Wrap" Text="Количество смены символов" VerticalAlignment="Top" Width="352"/>
<TextBox x:Name="TextBox_CountSymbol" InputScope="Number" HorizontalAlignment="Left" Height="72" Margin="10,419,0,0" TextWrapping="Wrap" Text="3" VerticalAlignment="Top" Width="101"/>
<Button x:Name="Button_CountSymbol" Content=" Ok " HorizontalAlignment="Left" Margin="288,419,0,0" VerticalAlignment="Top" Click="Event_Button_Click_CountSymbol" RenderTransformOrigin="1.474,0.483"/>
<!-- Сатрт / Стоп анимация -->
<TextBlock x:Name="TextBlock_OnOff" HorizontalAlignment="Left" Margin="28,491,0,0" TextWrapping="Wrap" Text="Вкл/Выкл анимацию" VerticalAlignment="Top" Width="352"/>
<ToggleButton x:Name="Button_Stop" Content="Stop" HorizontalAlignment="Left" Margin="214,523,0,0" VerticalAlignment="Top" Click="Event_Button_Click_Stop" Width="180"/>
<ToggleButton x:Name="Button_Start" Content="Start" HorizontalAlignment="Left" Margin="10,523,0,0" VerticalAlignment="Top" Click="Event_Button_Click_Start" Width="180"/>
<!-- Очистка экрана -->
<TextBlock HorizontalAlignment="Left" Margin="28,605,0,0" TextWrapping="Wrap" Text="Хотите очистит экран?" VerticalAlignment="Top" Width="352"/>
<Button Content=" YES " HorizontalAlignment="Left" Margin="10,632,0,0" VerticalAlignment="Top" Width="371" Click="Event_Button_Click_Clear"/>
<!-- Размер клетки для символа -->
<TextBlock HorizontalAlignment="Left" Margin="27,709,0,0" TextWrapping="Wrap" Text="Размер клетки для символа" VerticalAlignment="Top" Width="369"/>
<TextBox x:Name="TextBox_ElementSize" InputScope="Default" HorizontalAlignment="Left" Height="72" Margin="10,734,0,-17" TextWrapping="Wrap" Text="-6" VerticalAlignment="Top" Width="101"/>
<Button Content="Ok" HorizontalAlignment="Left" Margin="286,736,0,-19" VerticalAlignment="Top" Width="93" Click="Event_Button_Click_ElementSize"/>
<!-- Длина змейки -->
<TextBlock HorizontalAlignment="Left" Margin="27,809,0,0" TextWrapping="Wrap" Text="Длина змейки от: до:" VerticalAlignment="Top" Width="369"/>
<TextBox x:Name="TextBox_MinLength" InputScope="Number" HorizontalAlignment="Left" Height="72" Margin="10,834,0,-17" TextWrapping="Wrap" Text="3" VerticalAlignment="Top" Width="101"/>
<TextBox x:Name="TextBox_MaxLength" InputScope="Number" HorizontalAlignment="Left" Height="67" TextWrapping="Wrap" Text="10" VerticalAlignment="Top" Width="100" Margin="114,834,0,-17"/>
<Button Content="Ok" HorizontalAlignment="Left" Margin="286,836,0,-19" VerticalAlignment="Top" Width="93" Click="Event_Button_Click_MaxLength"/>
<!-- Горизонтакльное / вертикальное расположение -->
<TextBlock HorizontalAlignment="Left" Margin="28,905,0,0" TextWrapping="Wrap" Text="Повернуть матрицу" VerticalAlignment="Top" Width="352"/>
<ToggleButton x:Name="ToggleButton_Turn" Content="Вертикально" HorizontalAlignment="Left" Margin="10,932,0,0" VerticalAlignment="Top" Width="371" Click="Event_Button_Click_Turn"/>
</Grid>
</ScrollViewer>
</Grid>
В третьем элементе остались последние 4 настройки: выбор языка, цвета и задание цвета первому элементу, фону, градиента для змейки. Так же все находится в ScrollViewer для вертикальной прокрутки. Однако тут пришлось использовать один нестандартный элемент управления, а именно ColorPicker. Его можно найти в дополнительной библиотеке элементов с названием Coding4Fun.
Как установить Coding4Fun
У меня последние пару недель с нугет проблемы, поэтому качал вручную.
Переходим по ссылке.
Нажимаем на большую красную кнопку с надписью «Via CodePlex Current Release Zip».
Качаем архив с именем «Coding4Fun.Toolkit (Windows Phone 8).zip».
Из архива копируем файл с именем «Coding4Fun.Toolkit.Controls.dll» к себе в проект.
Заходим в проект в папочку «References». Жмем на нее правой кнопкой — Добавить ссылку — Справа внизу выбираем Обзор… Находим нашу библиотеку и жмем ОК.
Заходим в Панель элементов. правой кнопкой где угодно в ее приделах — Выбрать элементы… Выбираем вкладку Windows Phone Components. Справа внизу жмем обзор и снова выбираем Coding4Fun.Toolkit.Controls.dll. Новые элементы будут подсвечены. Смотрим, что б возле элемента ColorPicker была галочка.
Убедитесь, что в начале файла добавилась строчка
xmlns:Controls="clr-namespace:Coding4Fun.Toolkit.Controls;assembly=Coding4Fun.Toolkit.Controls"
Продолжим. Перетаскиваем ColorPicker с Панели элементов. Назначаем имя. Теперь выбирать цвет будем на одном элементе, а применять выбранный цвет разными кнопками к разным характеристикам матрицы.
<!-- Элемент выбора цвета. Codding4Fun -->
<Controls:ColorPicker x:Name="ColorPicker" VerticalAlignment="Top" Height="360" Margin="10,154,10,0"/>
<TextBlock HorizontalAlignment="Left" Margin="10,114,0,0" TextWrapping="Wrap" Text="Выбирете цвет" VerticalAlignment="Top" Width="412" Height="35"/>
Так же добавим кнопку, при нажатии на которую будет появляться всплывающее окно для выбора языка:
<!-- Выбор языка символов -->
<StackPanel Margin="0,0,0,649" >
<!-- Всплывающее окно -->
<Popup Name="Popup_ButtonDropDownSelectLanguage" Margin="0,0,10,0">
<StackPanel Margin="10,50,0,0" Background="DarkGray" Width="393" Name="StackPanel_ButtonDropDownSelectLanguage">
</StackPanel>
</Popup>
<TextBlock TextWrapping="Wrap" Text="Выбирете язык символов"/>
<!-- Кнопка, отображающая выбранный язык. При нажатии на нее всплывает окно -->
<Button Margin="0,10,10,0" x:Name="Button_SelectLanguage" Content="Китайский" Click="Event_Button_Click_SelectLanguage" Height="74" />
</StackPanel>
И еще пара кнопок по аналогии со вторым элементом панорамы.
Код. Третий элемент Panorama
<!--Третий элемент Panorama-->
<phone:PanoramaItem >
<Grid x:Name="LayoutRoot123" Background="Transparent" Margin="0,-32,-2,7" RenderTransformOrigin="0.500,0.500" >
<ScrollViewer HorizontalScrollBarVisibility="Hidden" Margin="0,26,0,0" >
<Grid Margin="0,0,0,0" Height="984" >
<!-- Выбор языка символов -->
<StackPanel Margin="0,0,0,649" >
<!-- Всплывающее окно -->
<Popup Name="Popup_ButtonDropDownSelectLanguage" Margin="0,0,10,0">
<StackPanel Margin="10,50,0,0" Background="DarkGray" Width="393" Name="StackPanel_ButtonDropDownSelectLanguage">
</StackPanel>
</Popup>
<TextBlock TextWrapping="Wrap" Text="Выбирете язык символов"/>
<!-- Кнопка, отображающая выбранный язык. При нажатии на нее всплывает окно -->
<Button Margin="0,10,10,0" x:Name="Button_SelectLanguage" Content="Китайский" Click="Event_Button_Click_SelectLanguage" Height="74" />
</StackPanel>
<!-- Элемент выбора цвета. Codding4Fun -->
<Controls:ColorPicker x:Name="ColorPicker" VerticalAlignment="Top" Height="360" Margin="10,154,10,0"/>
<TextBlock HorizontalAlignment="Left" Margin="10,114,0,0" TextWrapping="Wrap" Text="Выбирете цвет" VerticalAlignment="Top" Width="412" Height="35"/>
<!-- Выбор фона матрицы -->
<Button x:Name="Button_BackgroundColor" Content="Цвет фона" HorizontalAlignment="Left" Margin="10,533,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.404,-0.757" Width="412" Click="Event_Button_Click_ChangeBackground"/>
<!-- Выбор цвета первого символа -->
<Button x:Name="Button_FirstSymbolColor" Content="Цвет первого символа" HorizontalAlignment="Left" Margin="10,605,0,0" VerticalAlignment="Top" Width="412" Click="Event_Button_Click_FirstSymbolColor"/>
<!-- Выбор цвета градиента змейки -->
<TextBlock HorizontalAlignment="Left" Margin="10,682,0,0" TextWrapping="Wrap" Text="Выберите цвета градиента змейки" VerticalAlignment="Top" RenderTransformOrigin="-0.915,-1.222" Width="412"/>
<Button x:Name="Button_GradientFrom" Content="От" HorizontalAlignment="Left" Margin="10,714,0,0" VerticalAlignment="Top" Width="180" Click="Event_Button_Click_GradientFrom"/>
<Button x:Name="Button_GradientTo" Content="До" HorizontalAlignment="Left" Margin="245,714,0,0" VerticalAlignment="Top" Width="180" Click="Event_Button_Click_GradientTo"/>
</Grid>
</ScrollViewer>
</Grid>
</phone:PanoramaItem>
Код. MainPage.xaml
<phone:PhoneApplicationPage
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:UI="clr-namespace:Microsoft.Advertising.Mobile.UI;assembly=Microsoft.Advertising.Mobile.UI"
xmlns:Controls="clr-namespace:Coding4Fun.Toolkit.Controls;assembly=Coding4Fun.Toolkit.Controls"
x:Class="SE_Matrix_2d_v_4.MainPage"
mc:Ignorable="d"
d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="False">
<!--LayoutRoot представляет корневую сетку, где размещается все содержимое страницы-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<!-- ПРИМЕЧАНИЕ О ЛОКАЛИЗАЦИИ:
Чтобы локализовать отображаемые строки, скопируйте их значения в соответствующим образом названные
ключи в файле ресурсов нейтрального языка приложения (AppResources.resx), а затем
замените жестко заданное текстовое значение между кавычками атрибутов
на выражение привязки, указывающее на имя соответствующей строки.
Пример:
Text="{Binding Path=LocalizedResources.ApplicationTitle, Source={StaticResource LocalizedStrings}}"
Данная привязка указывает на строковый ресурс шаблона с именем "ApplicationTitle".
Добавление поддерживаемых языков на вкладку "Свойства проекта" создает
новый RESX-файл для каждого языка, в котором могут храниться переведенные значения
строк пользовательского интерфейса. Привязка в этих примерах вызывает отрисовку
значений атрибутов из RESX-файла, соответствующего
CurrentUICulture приложения во время выполнения.
-->
<!--Элемент управления Panorama-->
<phone:Panorama Title="">
<phone:Panorama.Background>
<ImageBrush />
</phone:Panorama.Background>
<!--Первый элемент Panorama-->
<phone:PanoramaItem >
<!--Тут создается и отображается матрица-->
<Grid x:Name="LayoutRootSecond" Background="Black" Margin="0,-32,-2,7" RenderTransformOrigin="0.500,0.500" Tap="Event_Grid_Tap_LayoutRoot"/>
</phone:PanoramaItem>
<!--Второй элемент Panorama-->
<phone:PanoramaItem>
<Grid x:Name="LayoutRootThierd" Grid.Column="1" Background="Transparent" Margin="0, 0,-2,7" RenderTransformOrigin="0.500,0.500" >
<ScrollViewer HorizontalScrollBarVisibility="Auto" >
<Grid x:Name="Grid_SettingsRight" Grid.Column="1" Background="Transparent" Margin="0, 0,-2,7" RenderTransformOrigin="0.500,0.500" >
<!-- Скорость падения змейки в мс/ Скорость смены символов в мс -->
<TextBlock HorizontalAlignment="Left" Margin="28,3,0,0" TextWrapping="Wrap" Text="Скорость смены символов в мс" VerticalAlignment="Top" Width="352"/>
<TextBox x:Name="TextBox_SppeedFrom" InputScope="Number" HorizontalAlignment="Left" Height="67" TextWrapping="Wrap" Text="20" VerticalAlignment="Top" Width="100" Margin="9,28,0,0"/>
<TextBox x:Name="TextBox_SppeedTo" InputScope="Number" HorizontalAlignment="Left" Height="67" TextWrapping="Wrap" Text="40" VerticalAlignment="Top" Width="100" Margin="114,28,0,0"/>
<Button x:Name="Button_SpeedApplay" Content=" Ok " HorizontalAlignment="Left" Height="65" Margin="288,30,0,0" VerticalAlignment="Top" Click="Event_Button_Click_SpeedApplay"/>
<!-- Количество змеек в очереди -->
<TextBlock HorizontalAlignment="Left" Margin="28,95,0,0" TextWrapping="Wrap" Text="Количество змеек в очереди" VerticalAlignment="Top" RenderTransformOrigin="-0.256,0.233" Width="352"/>
<TextBox x:Name="TextBox_CountQueue" InputScope="Number" HorizontalAlignment="Left" Height="67" Margin="10,127,0,0" TextWrapping="Wrap" Text="5" VerticalAlignment="Top" Width="101" RenderTransformOrigin="0.49,-0.049"/>
<Button x:Name="Button_CountQueue" Content=" Ok " HorizontalAlignment="Left" Margin="287,127,0,0" VerticalAlignment="Top" Click="Event_Button_Click_CountQueue" RenderTransformOrigin="1.474,0.483" Height="67"/>
<!-- Количество змеек за нажатие -->
<TextBlock HorizontalAlignment="Left" Margin="28,194,0,0" TextWrapping="Wrap" Text="Количество змеек за нажатие" VerticalAlignment="Top" Width="352"/>
<TextBox x:Name="TextBox_СountSimultaneously" InputScope="Number" HorizontalAlignment="Left" Height="72" Margin="10,221,0,0" TextWrapping="Wrap" Text="1" VerticalAlignment="Top" Width="101"/>
<Button x:Name="Button_СountSimultaneously" Content=" Ok " HorizontalAlignment="Left" Margin="288,221,0,0" VerticalAlignment="Top" Click="Event_Button_Click_СountSimultaneously" RenderTransformOrigin="1.474,0.483"/>
<!-- Размер шрифта -->
<TextBlock HorizontalAlignment="Left" Margin="27,293,0,0" TextWrapping="Wrap" Text="Размер шрифта (+ - [n])" VerticalAlignment="Top" Width="352"/>
<TextBox x:Name="TextBox_FontSize" InputScope="Default" HorizontalAlignment="Left" Height="72" Margin="9,320,0,0" TextWrapping="Wrap" Text="-2" VerticalAlignment="Top" Width="101"/>
<Button x:Name="Button_FontSize" Content=" Ok " HorizontalAlignment="Left" Margin="288,320,0,0" VerticalAlignment="Top" Click="Event_Button_Click_FontSize" RenderTransformOrigin="1.474,0.483"/>
<!-- Количество смены символов в ячейке -->
<TextBlock HorizontalAlignment="Left" Margin="28,392,0,0" TextWrapping="Wrap" Text="Количество смены символов" VerticalAlignment="Top" Width="352"/>
<TextBox x:Name="TextBox_CountSymbol" InputScope="Number" HorizontalAlignment="Left" Height="72" Margin="10,419,0,0" TextWrapping="Wrap" Text="3" VerticalAlignment="Top" Width="101"/>
<Button x:Name="Button_CountSymbol" Content=" Ok " HorizontalAlignment="Left" Margin="288,419,0,0" VerticalAlignment="Top" Click="Event_Button_Click_CountSymbol" RenderTransformOrigin="1.474,0.483"/>
<!-- Сатрт / Стоп анимация -->
<TextBlock x:Name="TextBlock_OnOff" HorizontalAlignment="Left" Margin="28,491,0,0" TextWrapping="Wrap" Text="Вкл/Выкл анимацию" VerticalAlignment="Top" Width="352"/>
<ToggleButton x:Name="Button_Stop" Content="Stop" HorizontalAlignment="Left" Margin="214,523,0,0" VerticalAlignment="Top" Click="Event_Button_Click_Stop" Width="180"/>
<ToggleButton x:Name="Button_Start" Content="Start" HorizontalAlignment="Left" Margin="10,523,0,0" VerticalAlignment="Top" Click="Event_Button_Click_Start" Width="180"/>
<!-- Очистка экрана -->
<TextBlock HorizontalAlignment="Left" Margin="28,605,0,0" TextWrapping="Wrap" Text="Хотите очистит экран?" VerticalAlignment="Top" Width="352"/>
<Button Content=" YES " HorizontalAlignment="Left" Margin="10,632,0,0" VerticalAlignment="Top" Width="371" Click="Event_Button_Click_Clear"/>
<!-- Размер клетки для символа -->
<TextBlock HorizontalAlignment="Left" Margin="27,709,0,0" TextWrapping="Wrap" Text="Размер клетки для символа" VerticalAlignment="Top" Width="369"/>
<TextBox x:Name="TextBox_ElementSize" InputScope="Default" HorizontalAlignment="Left" Height="72" Margin="10,734,0,-17" TextWrapping="Wrap" Text="-6" VerticalAlignment="Top" Width="101"/>
<Button Content="Ok" HorizontalAlignment="Left" Margin="286,736,0,-19" VerticalAlignment="Top" Width="93" Click="Event_Button_Click_ElementSize"/>
<!-- Длина змейки -->
<TextBlock HorizontalAlignment="Left" Margin="27,809,0,0" TextWrapping="Wrap" Text="Длина змейки от: до:" VerticalAlignment="Top" Width="369"/>
<TextBox x:Name="TextBox_MinLength" InputScope="Number" HorizontalAlignment="Left" Height="72" Margin="10,834,0,-17" TextWrapping="Wrap" Text="3" VerticalAlignment="Top" Width="101"/>
<TextBox x:Name="TextBox_MaxLength" InputScope="Number" HorizontalAlignment="Left" Height="67" TextWrapping="Wrap" Text="10" VerticalAlignment="Top" Width="100" Margin="114,834,0,-17"/>
<Button Content="Ok" HorizontalAlignment="Left" Margin="286,836,0,-19" VerticalAlignment="Top" Width="93" Click="Event_Button_Click_MaxLength"/>
<!-- Горизонтакльное / вертикальное расположение -->
<TextBlock HorizontalAlignment="Left" Margin="28,905,0,0" TextWrapping="Wrap" Text="Повернуть матрицу" VerticalAlignment="Top" Width="352"/>
<ToggleButton x:Name="ToggleButton_Turn" Content="Вертикально" HorizontalAlignment="Left" Margin="10,932,0,0" VerticalAlignment="Top" Width="371" Click="Event_Button_Click_Turn"/>
</Grid>
</ScrollViewer>
</Grid>
</phone:PanoramaItem>
<!--Третий элемент Panorama-->
<phone:PanoramaItem >
<Grid x:Name="LayoutRoot123" Background="Transparent" Margin="0,-32,-2,7" RenderTransformOrigin="0.500,0.500" >
<ScrollViewer HorizontalScrollBarVisibility="Hidden" Margin="0,26,0,0" >
<Grid Margin="0,0,0,0" Height="984" >
<!-- Выбор языка символов -->
<StackPanel Margin="0,0,0,649" >
<!-- Всплывающее окно -->
<Popup Name="Popup_ButtonDropDownSelectLanguage" Margin="0,0,10,0">
<StackPanel Margin="10,50,0,0" Background="DarkGray" Width="393" Name="StackPanel_ButtonDropDownSelectLanguage">
</StackPanel>
</Popup>
<TextBlock TextWrapping="Wrap" Text="Выбирете язык символов"/>
<!-- Кнопка, отображающая выбранный язык. При нажатии на нее всплывает окно -->
<Button Margin="0,10,10,0" x:Name="Button_SelectLanguage" Content="Китайский" Click="Event_Button_Click_SelectLanguage" Height="74" />
</StackPanel>
<!-- Элемент выбора цвета. Codding4Fun -->
<Controls:ColorPicker x:Name="ColorPicker" VerticalAlignment="Top" Height="360" Margin="10,154,10,0"/>
<TextBlock HorizontalAlignment="Left" Margin="10,114,0,0" TextWrapping="Wrap" Text="Выбирете цвет" VerticalAlignment="Top" Width="412" Height="35"/>
<!-- Выбор фона матрицы -->
<Button x:Name="Button_BackgroundColor" Content="Цвет фона" HorizontalAlignment="Left" Margin="10,533,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.404,-0.757" Width="412" Click="Event_Button_Click_ChangeBackground"/>
<!-- Выбор цвета первого символа -->
<Button x:Name="Button_FirstSymbolColor" Content="Цвет первого символа" HorizontalAlignment="Left" Margin="10,605,0,0" VerticalAlignment="Top" Width="412" Click="Event_Button_Click_FirstSymbolColor"/>
<!-- Выбор цвета градиента змейки -->
<TextBlock HorizontalAlignment="Left" Margin="10,682,0,0" TextWrapping="Wrap" Text="Выберите цвета градиента змейки" VerticalAlignment="Top" RenderTransformOrigin="-0.915,-1.222" Width="412"/>
<Button x:Name="Button_GradientFrom" Content="От" HorizontalAlignment="Left" Margin="10,714,0,0" VerticalAlignment="Top" Width="180" Click="Event_Button_Click_GradientFrom"/>
<Button x:Name="Button_GradientTo" Content="До" HorizontalAlignment="Left" Margin="245,714,0,0" VerticalAlignment="Top" Width="180" Click="Event_Button_Click_GradientTo"/>
</Grid>
</ScrollViewer>
</Grid>
</phone:PanoramaItem>
</phone:Panorama>
<!--Раскомментируйте, чтобы увидеть сетку выравнивания и выровнять
элементы управления по общим границам. Верхнее поле изображения равно -32 пикселя, чтобы
осталось место для области уведомлений. Установите его равным 0 (или вообще удалите поле),
если область уведомлений скрыта.
Перед сдачей приложения удалите этот код XAML и само изображение.-->
<!--<Image Source="/Assets/AlignmentGrid.png" VerticalAlignment="Top" Height="800" Width="480" Margin="0" Grid.Row="0" Grid.RowSpan="2" IsHitTestVisible="False" />-->
</Grid>
</phone:PhoneApplicationPage>
Внешне настройки выглядят так:
MainPage.xaml.cs
Первым делом создаем свойства класса, которые будут отвечать за настройки:
Код. Свойства класса
/* ****************************** Свойства класса ****************************** */
// Случайное число
Random random = new Random();
// Количество змеек после нажатия на экран в очереди
int iteration = 5;
// Количество одновременно появляющихся змеек после нажатия
int countSimultaneously = 3;
// Скорость смены символов
int speedFrom = 20;
int speedTo = 40;
// Размер клетки для символа
int addingSize = -6;
// Итоговый размер шрифта
int fontSize;
// Минимальная и максимальная длина змейки
int minLength = 10;
int maxLength = 15;
// Получаем расширение экрана
double ScreenWidth = System.Windows.Application.Current.Host.Content.ActualWidth - 60;
double ScreenHeight = System.Windows.Application.Current.Host.Content.ActualHeight - 100;
// Коеффициент, отвечающий за количесвто ячеек и частично за размер шрифта
int kolich = 30;
// Размер шрифта задается по формуле kolich + addingFontSize.
int addingFontSize = -2;
// Количество смены символов в ячейке
int countSymbol = 3;
// Включить (false), выключить (true) матрицу
bool flagOnOff = false;
// Включить (false), выключить (true) "поворот экрана"
bool turnOnOff = true;
// Количество строк и столбцов
int countWidth = 10;
int countHeight = 10;
// Словарь, в котором хранятся идентификаторы языка и соответствующие ему ASCII коды символов
Dictionary<string, int[]> languages = new Dictionary<string, int[]>();
// Задаю язык по-умолчанию
string actualLanguage = "Русский";
// Флаг, отвечающий за показывать (true) / не показывать (false) всплывающее окно (PopUp) при выборе языка
bool flagShowLanguages = true;
// Цвет фона матрицы, ARGB
Dictionary<string, int> colorMatrixBackground = new Dictionary<string, int>();
// Цвет первого символа, ARGB
Dictionary<string, int> colorFirstSymbol = new Dictionary<string, int>();
// Цвет градиента змейки от (второй символ) - до (последний символ), ARGB
Dictionary<string, int> gradientFrom = new Dictionary<string, int>();
Dictionary<string, int> gradientTo = new Dictionary<string, int>();
Рассмотри конструктор класса:
Код. Конструктор класса
// Конструктор
public MainPage()
{
InitializeComponent();
// Вызываем функцию настройки начальных значений цветов фона, символов и т.д.
BeginColorSettings();
// Инициализируем список доступных языков, а также соответствующие им ASCII коды символов
ListLanguages();
// Количество строк и столбцов
this.countWidth = (int)Math.Round(ScreenWidth / (kolich + addingSize)) + Math.Abs(addingSize);
this.countHeight = (int)Math.Round(ScreenHeight / (kolich + addingSize)) + 5 + Math.Abs(addingSize);
// Создание сетки элементов, в которой будет сыпаться матрица
CreateElement();
// Подсвечиваем кнопку Вкл или Выкл, зависит от флага
if (this.flagOnOff)
{
Button_Stop.Background = new SolidColorBrush(Colors.Cyan);
Button_Start.Background = new SolidColorBrush(Colors.Black);
}
else
{
Button_Stop.Background = new SolidColorBrush(Colors.Black);
Button_Start.Background = new SolidColorBrush(Colors.Cyan);
}
// Меняем цвет фона матрицы
ChangeBackground();
}
Давайте начнем с выбора цвета. Для начала инициализируем настройки по умолчанию в методе BeginColorSettings();
Код. BeginColorSettings
// Начальные настройки цветов фона, символов и т.д
private void BeginColorSettings()
{
// Задаем начальный цвет фона матрицы
colorMatrixBackground["A"] = 0;
colorMatrixBackground["R"] = 0;
colorMatrixBackground["G"] = 0;
colorMatrixBackground["B"] = 0;
// Задаем начальный цвет первого символа
colorFirstSymbol["A"] = 255;
colorFirstSymbol["R"] = 248;
colorFirstSymbol["G"] = 248;
colorFirstSymbol["B"] = 255;
// Задаем начальный цвет градиента от (второго символа в змейке)
gradientFrom["A"] = 255;
gradientFrom["R"] = 1;
gradientFrom["G"] = 255;
gradientFrom["B"] = 1;
// Задаем начальный цвет градиента до (последнего символа в змейке)
gradientTo["A"] = 0;
gradientTo["R"] = 0;
gradientTo["G"] = 0;
gradientTo["B"] = 0;
}
Цвет фона матрицы
Задаем цвет фона. Просто присваиваем соответствующему атрибуту цвет, состоящий из компонент ARGB. Почему четыре составляющие? Прозрачность (А) нам будет нужна для создания эффекта затухания.
Код. ChangeBackground
// Метод изменения цвета фона матрицы. По умолчанию черный.
private void ChangeBackground()
{
// Задаю цвет фона матрицы
LayoutRootSecond.Background = new SolidColorBrush(new Color()
{
A = (byte)(colorMatrixBackground["A"]) /*Opacity*/,
R = (byte)(colorMatrixBackground["R"]) /*Red*/,
G = (byte)(colorMatrixBackground["G"]) /*Green*/,
B = (byte)(colorMatrixBackground["B"]) /*Blue*/
});
}
Однако это просто метод. Для того, что б цвет действительно поменялся, нужно забрать его из контролла ColorPicker, присвоить свойству и вызвать метод ChangeBackground:
Код. Event_Button_Click_ChangeBackground
// Меняем цвет фона матрицы
private void Event_Button_Click_ChangeBackground(object sender, RoutedEventArgs e)
{
// Передаем в свойство класса colorMatrixBackground выбранный цвет из элемента ColorPicker для фона матрицы
colorMatrixBackground["A"] = ColorPicker.Color.A;
colorMatrixBackground["R"] = ColorPicker.Color.R;
colorMatrixBackground["G"] = ColorPicker.Color.G;
colorMatrixBackground["B"] = ColorPicker.Color.B;
// Задаем выбранный цвет фона матрицы соответствующей кнопке
Button_BackgroundColor.Background = new SolidColorBrush(new Color()
{
A = (byte)(colorMatrixBackground["A"]) /*Opacity*/,
R = (byte)(colorMatrixBackground["R"]) /*Red*/,
G = (byte)(colorMatrixBackground["G"]) /*Green*/,
B = (byte)(colorMatrixBackground["B"]) /*Blue*/
});
// Задаем выбранный цвет фона матрицы
ChangeBackground();
}
Цвет первого символа змейки
Изменим цвет первого символа змейки. Первый символ змейки вызывается в методе RandomElementQ_Async асинхронно:
// Вызываем на прорисовку первый, самый яркий падающий элемент. Асинхронно.
// colorFirstSymbol["A"]. Цвет задается через настройку.
await Change(element, timeOut, colorFirstSymbol);
Код. Event_Button_Click_FirstSymbolColor
// Изменение цвета первого символа
private void Event_Button_Click_FirstSymbolColor(object sender, RoutedEventArgs e)
{
// Передаем в свойство класса colorFirstSymbol выбранный цвет из элемента ColorPicker для фона матрицы
colorFirstSymbol["A"] = ColorPicker.Color.A;
colorFirstSymbol["R"] = ColorPicker.Color.R;
colorFirstSymbol["G"] = ColorPicker.Color.G;
colorFirstSymbol["B"] = ColorPicker.Color.B;
// Задаем выбранный цвет фона матрицы соответствующей кнопке
Button_FirstSymbolColor.Background = new SolidColorBrush(new Color()
{
A = (byte)(colorFirstSymbol["A"]) /*Opacity*/,
R = (byte)(colorFirstSymbol["R"]) /*Red*/,
G = (byte)(colorFirstSymbol["G"]) /*Green*/,
B = (byte)(colorFirstSymbol["B"]) /*Blue*/
});
}
Задание цветов для градиента
Ну вот и добрались до градиента. Первое, что нужно сделать — это в обработчиках событий соответствующих кнопок сохранить цвет в свойства класса:
Код. Event_Button_Click_Gradient
// Настройки. Задаем градиент змейки. Цвет второго символа.
private void Event_Button_Click_GradientFrom(object sender, RoutedEventArgs e)
{
// Передаем в свойство класса gradientFrom выбранный цвет из элемента ColorPicker для фона матрицы
gradientFrom["A"] = ColorPicker.Color.A;
gradientFrom["R"] = ColorPicker.Color.R;
gradientFrom["G"] = ColorPicker.Color.G;
gradientFrom["B"] = ColorPicker.Color.B;
// Задаем выбранный цвет градиента от змейки соответствующей кнопке
Button_GradientFrom.Background = new SolidColorBrush(new Color()
{
A = (byte)(gradientFrom["A"]) /*Opacity*/,
R = (byte)(gradientFrom["R"]) /*Red*/,
G = (byte)(gradientFrom["G"]) /*Green*/,
B = (byte)(gradientFrom["B"]) /*Blue*/
});
}
/ Настройки. Задаем градиент змейки. Цвет последнего символа.
private void Event_Button_Click_GradientTo (object sender, RoutedEventArgs e)
{
// Передаем в свойство класса gradientTo выбранный цвет из элемента ColorPicker для фона матрицы
gradientTo["A"] = ColorPicker.Color.A;
gradientTo["R"] = ColorPicker.Color.R;
gradientTo["G"] = ColorPicker.Color.G;
gradientTo["B"] = ColorPicker.Color.B;
// Задаем выбранный цвет градиента до змейки соответствующей кнопке
Button_GradientTo.Background = new SolidColorBrush(new Color()
{
A = (byte)(gradientTo["A"]) /*Opacity*/,
R = (byte)(gradientTo["R"]) /*Red*/,
G = (byte)(gradientTo["G"]) /*Green*/,
B = (byte)(gradientTo["B"]) /*Blue*/
});
}
Теперь эти цвета нужно применить к нашим символам. Переходим в метод RandomElementQ_Async и немножко меняем ту часть, которая отвечает за расчет яркости и цвета символов следующим образом:
Код. RandomElementQ_Async и Change
Меняем
на
А так же перед вызовом Task dsvv = Change(previousElement, timeOut, SymbolColor); добавляем:
И меняем определение метода Change, где вместо int Opacity ставим Dictionary<string, int> SymbolColor:
А внутри этого метода задаем цвет символа с помощью новых значений. Меняем
на
//int greenCoefficient = (int)Math.Round(255 / (double)(count + 1)) - 1;
на
int A_Coefficient = (int)Math.Round((gradientFrom["A"] - 10) / (double)(count + 1)) - 1;
int R_Coefficient = (int)Math.Round((gradientFrom["R"] - gradientTo["R"]) / (double)(count + 1)) - 1;
int G_Coefficient = (int)Math.Round((gradientFrom["G"] - gradientTo["G"]) / (double)(count + 1)) - 1;
int B_Coefficient = (int)Math.Round((gradientFrom["B"] - gradientTo["B"]) / (double)(count + 1)) - 1;
А так же перед вызовом Task dsvv = Change(previousElement, timeOut, SymbolColor); добавляем:
// Вызываем извлеченные элементы
// (greenCoefficient * (k + 1)) - 20 Высчитываем яркость так, что б разница между первым и последним была на всех змейках одинаковая
// и равномерно распределялась независимо от ее длины(количества элементов)
SymbolColor["A"] = (gradientFrom["A"] - ((i - k) * A_Coefficient));
SymbolColor["R"] = (gradientFrom["R"] - ((i - k) * R_Coefficient));
SymbolColor["G"] = (gradientFrom["G"] - ((i - k) * G_Coefficient));
SymbolColor["B"] = (gradientFrom["B"] - ((i - k) * B_Coefficient));
И меняем определение метода Change, где вместо int Opacity ставим Dictionary<string, int> SymbolColor:
// Метод изменения символов в заданном элеменете
public async Task Change(TextBlock element, int timeOut, Dictionary<string, int> SymbolColor)
А внутри этого метода задаем цвет символа с помощью новых значений. Меняем
// Формируем нужный цвет с заданной яркостью
SolidColorBrush NewColor = new SolidColorBrush(new Color()
{
A = (byte)(255) /*Opacity*/,
R = (byte)(0) /*Red*/,
G = (byte)(Opacity) /*Green*/,
B = (byte)(0) /*Blue*/
});
на
// Формируем нужный цвет с заданной яркостью
SolidColorBrush NewColor = new SolidColorBrush(new Color()
{
A = (byte)(SymbolColor["A"]) /*Opacity*/,
R = (byte)(SymbolColor["R"]) /*Red*/,
G = (byte)(SymbolColor["G"]) /*Green*/,
B = (byte)(SymbolColor["B"]) /*Blue*/
});
Выбор языка
С цветами разобрались. Теперь давайте решим вопрос с выбором языка. В конструкторе класса мы вызывали метод ListLanguages для инициализации доступных языков. Рассмотрим его более подробно:
Код. ListLanguages
// Заполняем словарь идентификаторами языка и соответствующие ему ASCII коды символов
public void ListLanguages()
{
// Добавляем в словарь ключь - название языка и значение - массив, состоящий из ASCII кодов символов.
languages.Add("Матрица", new int[] { 64, 127 });
languages.Add("Китаский", new int[] { 19968, 20223 });
languages.Add("Английский", new int[] { 64, 127 });
languages.Add("Цифры", new int[] { 48, 57 });
languages.Add("Случайные символы", new int[] { 0, 1000 });
languages.Add("Русский", new int[] { 1040, 1103 });
// Добавляем языки в всплывающую панель для возможности их выбора
foreach (var language in languages)
{
// Создаю кнопку
Button newLang = new Button();
// Задаю надпись кнопки, соответствует языку
newLang.Content = language.Key.ToString();
// Горизонтальное выравнивание
newLang.HorizontalAlignment = HorizontalAlignment.Stretch;
// Толщина рамки
newLang.BorderThickness = new Thickness(1);
// Смещение
newLang.Margin = new Thickness(0,0,0,0);
// Событие, при нажатии на кнопку. Одно на все.
newLang.Click += Event_Button_Click_SelectLanguageUpdate;
// Добавляю созданую и настроенную кнопку в всплывающее окно
StackPanel_ButtonDropDownSelectLanguage.Children.Add(newLang);
}
}
Теперь добавляем обработчик кнопки, в которой показывается выбранный язык и при нажатии на которую появляется всплывающее окно:
Код. Event_Button_Click_SelectLanguage
// Кнопка, в которой показывается текущее выбранное значение языка. при нажатии на нее всплывает меню для выбора другого языка.
private void Event_Button_Click_SelectLanguage(object sender, RoutedEventArgs e)
{
// Показывать (true) / не показывать (false) всплывающее окно (PopUp) при выборе языка
if (flagShowLanguages)
{
// Показать всплывающее окно
Popup_ButtonDropDownSelectLanguage.IsOpen = true;
flagShowLanguages = false;
}
else
{
// Скрыть всплывающее окно
Popup_ButtonDropDownSelectLanguage.IsOpen = false;
flagShowLanguages = true;
}
}
В сплывающем окне находятся кнопки, при нажатии на которые и выбирается нужный язык. У всех кнопок назначен один и тот же обработчик событий, который определяет контент кнопки, который и является ключом в словаре для выбора языка. Задаем значение выбранного языка в свойство класса actualLanguage:
Код. Event_Button_Click_SelectLanguageUpdate
// Всплывающее меню выбора языка.
private void Event_Button_Click_SelectLanguageUpdate(object sender, RoutedEventArgs e)
{
// Если нажата кнопка выбора языка, но другой язык не выбран, то при повторном нажатии меню свернется.
if (!flagShowLanguages)
{
// Скрыть всплывающее окно
Popup_ButtonDropDownSelectLanguage.IsOpen = false;
flagShowLanguages = true;
}
// Получаем название кнопки, которую нажали
string newLanguagr = (sender as Button).Content.ToString();
// Обновляем название кнопки, отображающей выбранный язык
Button_SelectLanguage.Content = newLanguagr;
// Задаем значение языка, в котором выбирать символы случайным образом
this.actualLanguage = newLanguagr;
}
Теперь все готово, для того, что б можно было выбрать случайный символ из заданного диапазона. Напишем метод RandomActualSymbol, который и будет возвращать случайный символ:
Код. RandomActualSymbol
// Вызываем эту функцию везде, где нужно показать случайный символ из выбранного языка.
public string RandomActualSymbol()
{
// Получаем массив по ключу, содержащий ASCII коды символов языка, заданного в actualLanguage
int[] sd = (languages[actualLanguage]);
// Выбираем случайнфй символ в диапазоне от первого до последнего символа в заданом языке
return char.ConvertFromUtf32(this.random.Next((int)sd.GetValue(0), (int)sd.GetValue(1)));
}
И подставим вызов этого метода везде, где нужно задать символ. Например, в методе Change:
// Каждый раз разный символ из заданного диапазона
element.Text = RandomActualSymbol();
Старт / Стоп
Тут все очень просто. В зависимости от флага flagOnOff, который задан как свойство класса завершаем циклы, в которых происходит обработка непосредственно самой матрицы.
Задаем обработчики событий кнопок «Старт» и «Стоп», которые просто меняют состояние флага на противоположный:
Код. Event_Button_Click_Stop и Event_Button_Click_Start
// Настройки. Выключаем возможность анимирования змеек
private void Event_Button_Click_Stop(object sender, RoutedEventArgs e)
{
this.flagOnOff = true;
// Если flagOnOff в true то подсвечиваем кнопку Stop
if (this.flagOnOff)
{
Button_Stop.Background = new SolidColorBrush(Colors.Cyan);
Button_Start.Background = new SolidColorBrush(Colors.Black);
}
}
// Настройки. Включаем возможность анимирования змеек
private void Event_Button_Click_Start(object sender, RoutedEventArgs e)
{
this.flagOnOff = false;
// Если flagOnOff в false то подсвечиваем кнопку Start
if (!this.flagOnOff)
{
Button_Stop.Background = new SolidColorBrush(Colors.Black);
Button_Start.Background = new SolidColorBrush(Colors.Cyan);
}
}
И добавляем внутри нескольких циклов простое условие
if (flagOnOff) break;
А именно в циклах методов Event_Grid_Tap_LayoutRoot, RandomElementQ_Async, Change:
Код. Остановка матрицы
В Event_Grid_Tap_LayoutRoot:
В RandomElementQ_Async тут:
и тут:
В Change:
// Количество одновременно появляющихся змеек после нажатия
for (int i = 0; i < countSimultaneously; i++)
{
// Останавливаем анимацию
if (flagOnOff) break;
В RandomElementQ_Async тут:
// Цикл формирует змейку заданной длины length
for (int i = 0; i <= length; i++)
{
// Останавливаем анимацию
if (flagOnOff) break;
и тут:
// Перебираем все элементы, составляющие змейку на данном этапе. С каждым циклом она увеличивается, пока не достигнет нужной длины.
for (int k = 0; k <= i; k++)
{
// Останавливаем анимацию
if (flagOnOff) break;
В Change:
// Количество смены символов в каждой ячейке
for (int i = 0; i < countSymbol; i++)
{
// Останавливаем анимацию
if (flagOnOff) break;
Почему аж в четырех местах? Если змеек мало, то все нормально. Но если количество змеек неприлично большое, например, 50+, то матрица останавливается с заметными тормозами. А так мы останавливаем непосредственно каждый элемент, принимающий участие в работе механизма матрицы.
Ну и напоследок добавим красоты, для задания цвета кнопки, которая сейчас нажата. В конструкторе за это отвечает этот код:
// Подсвечиваем кнопку Вкл или Выкл, зависит от флага
if (this.flagOnOff)
{
Button_Stop.Background = new SolidColorBrush(Colors.Cyan);
Button_Start.Background = new SolidColorBrush(Colors.Black);
}
else
{
Button_Stop.Background = new SolidColorBrush(Colors.Black);
Button_Start.Background = new SolidColorBrush(Colors.Cyan);
}
Точнее сказать, задания цвета кнопки при инициализации приложения.
Очистка экрана
При срабатывании события Event_Button_Click_Clear происходит тоже самое, что и в CreateElement, только без создания элементов. Перебираются все элементы и как символ задается пустота:
Код. Event_Button_Click_Clear
// Настройки. Очистка экрана
private void Event_Button_Click_Clear(object sender, RoutedEventArgs e)
{
// Перебираем сетку ячеек и устанавливаем в каждой ячейке как символ - пустоту
for ( int i = 0; i < countWidth; i++)
{
for (int j = 0; j < countHeight; j++)
{
// Формируем имя элемента, который будем очищать
string elementName = "TB_" + i + "_" + j;
// Получаем элемент по его имени
object wantedNode = LayoutRoot.FindName(elementName);
TextBlock element = (TextBlock)wantedNode;
// Очищаем значение
element.Text = "";
}
}
}
Горизонтальная / Вертикальная ориентация
Опять все исходит от флага turnOnOff. Что мы делаем? Останавливаем матрицу. Меняем надпись на кнопке. Меняем местами количество строк и столбцов. Удаляем матрицу. Создаем матрицу снова, вызывая метод CreateElement:
Код. Event_Button_Click_Turn
// Настройки. Вериткальная / горизонтальная матрица
private void Event_Button_Click_Turn(object sender, RoutedEventArgs e)
{
// Включить (true), выключить (false) "поворот экрана"
if (turnOnOff)
{
// Сохраняем значение false в свойство класса turnOnOff
this.turnOnOff = false;
// Надпись на конопке с именем ToggleButton_Turn меняю на Горизонтально
ToggleButton_Turn.Content = "Горизонтально";
// Количество строк и столбцов. Инвертируем для горизонтали.
this.countHeight = (int)Math.Round(ScreenWidth / (kolich + addingSize)) + 5 + Math.Abs(addingSize);
this.countWidth = (int)Math.Round(ScreenHeight / (kolich + addingSize)) + 5 + Math.Abs(addingSize);
}
else
{
// Сохраняем значение true в свойство класса turnOnOff
this.turnOnOff = true;
// Надпись на конопке с именем ToggleButton_Turn меняю на Вертикально
ToggleButton_Turn.Content = "Вертикально";
// Количество строк и столбцов. Возвращаем для вертикали
this.countWidth = (int)Math.Round(ScreenWidth / (kolich + addingSize)) + Math.Abs(addingSize);
this.countHeight = (int)Math.Round(ScreenHeight / (kolich + addingSize)) + 5 + Math.Abs(addingSize);
}
// Останавливаем матрицу
this.flagOnOff = true;
// Удаляем матрицу (сами ячейки)
LayoutRootSecond.Children.Clear();
// Перерисовываем ячейки заново с новыми параметрами
CreateElement();
// Включаем матрицу
this.flagOnOff = false;
}
Изменение размера матрицы
Логика схожа с поворотом. Единственная разница в том, что мы не меняем местами количество строк и столбцов, а добавляем к расчету количества строк и столбцов коэффициент, введенный пользователем:
Код. Event_Button_Click_ElementSize
// Настройки. Размер клетки для символа
private void Event_Button_Click_ElementSize(object sender, RoutedEventArgs e)
{
// Сохраняем значение их соответствующего TextBox TextBox_ElementSize в свойство класса addingSize
this.addingSize = int.Parse(TextBox_ElementSize.Text.ToString());
// Количество строк и столбцов. Возвращаем для вертикали
this.countWidth = (int)Math.Round(ScreenWidth / (kolich + addingSize)) + Math.Abs(addingSize);
this.countHeight = (int)Math.Round(ScreenHeight / (kolich + addingSize)) + 5 + Math.Abs(addingSize);
// Останавливаем матрицу
this.flagOnOff = true;
// Удаляем матрицу (сами ячейки)
LayoutRootSecond.Children.Clear();
// Перерисовываем ячейки заново с новыми параметрами
CreateElement();
// Включаем матрицу
this.flagOnOff = false;
}
Остальные настройки
Выполнены по одному шаблону. Получаем введенное пользователем значение, записываем его в соответствующее свойство класса, которое и подставляем в нужном месте, вместо статических значений которые были в предыдущей части.
Например количество одновременно ползущих змеек:
Код. Event_Grid_Tap_LayoutRoot
Вместо статики
Делаем динамику:
// Событие при нажатии на эелемет Grid (на экран)
private void Event_Grid_Tap_LayoutRoot(object sender, System.Windows.Input.GestureEventArgs e)
{
// Количество одновременно появляющихся змеек после нажатия
for (int i = 0; i < 5; i++)
{
// Останавливаем анимацию
if (flagOnOff) break;
Start();
//Задержка между вызовами. Для красоты матрицы.
Task.Delay(100);
}
}
Делаем динамику:
// Событие при нажатии на эелемет Grid (на экран)
private void Event_Grid_Tap_LayoutRoot(object sender, System.Windows.Input.GestureEventArgs e)
{
// Количество одновременно появляющихся змеек после нажатия
for (int i = 0; i < countSimultaneously; i++)
{
// Останавливаем анимацию
if (flagOnOff) break;
Start();
//Задержка между вызовами. Для красоты матрицы.
Task.Delay(100);
}
}
Код. MainPage.xaml.cs
using System;
using System.Net;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.Windows.Media;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Collections.ObjectModel;
namespace SE_Matrix_2d_v_4
{
public partial class MainPage : PhoneApplicationPage
{
/* ****************************** Свойства класса ****************************** */
// Случайное число
Random random = new Random();
// Количество змеек после нажатия на экран в очереди
int iteration = 5;
// Количество одновременно появляющихся змеек после нажатия
int countSimultaneously = 3;
// Скорость смены символов
int speedFrom = 20;
int speedTo = 40;
// Размер клетки для символа
int addingSize = -6;
// Итоговый размер шрифта
int fontSize;
// Минимальная и максимальная длина змейки
int minLength = 10;
int maxLength = 15;
// Получаем расширение экрана
double ScreenWidth = System.Windows.Application.Current.Host.Content.ActualWidth - 60;
double ScreenHeight = System.Windows.Application.Current.Host.Content.ActualHeight - 100;
// Коеффициент, отвечающий за количесвто ячеек и частично за размер шрифта
int kolich = 30;
// Размер шрифта задается по формуле kolich + addingFontSize.
int addingFontSize = -2;
// Количество смены символов в ячейке
int countSymbol = 3;
// Включить (false), выключить (true) матрицу
bool flagOnOff = false;
// Включить (false), выключить (true) "поворот экрана"
bool turnOnOff = true;
// Количество строк и столбцов
int countWidth = 10;
int countHeight = 10;
// Словарь, в котором хранятся идентификаторы языка и соответствующие ему ASCII коды символов
Dictionary<string, int[]> languages = new Dictionary<string, int[]>();
// Задаю язык по-умолчанию
string actualLanguage = "Русский";
// Флаг, отвечающий за показывать (true) / не показывать (false) всплывающее окно (PopUp) при выборе языка
bool flagShowLanguages = true;
// Цвет фона матрицы, ARGB
Dictionary<string, int> colorMatrixBackground = new Dictionary<string, int>();
// Цвет первого символа, ARGB
Dictionary<string, int> colorFirstSymbol = new Dictionary<string, int>();
// Цвет градиента змейки от (второй символ) - до (последний символ), ARGB
Dictionary<string, int> gradientFrom = new Dictionary<string, int>();
Dictionary<string, int> gradientTo = new Dictionary<string, int>();
/* ****************************** Методы класса ****************************** */
// Конструктор
public MainPage()
{
InitializeComponent();
// Вызываем функцию настройки начальных значений цветов фона, символов и т.д.
BeginColorSettings();
// Инициализируем список доступных языков, а также соответствующие им ASCII коды символов
ListLanguages();
// Количество строк и столбцов
this.countWidth = (int)Math.Round(ScreenWidth / (kolich + addingSize)) + Math.Abs(addingSize);
this.countHeight = (int)Math.Round(ScreenHeight / (kolich + addingSize)) + 5 + Math.Abs(addingSize);
// Создание сетки элементов, в которой будет сыпаться матрица
CreateElement();
// Подсвечиваем кнопку Вкл или Выкл, зависит от флага
if (this.flagOnOff)
{
Button_Stop.Background = new SolidColorBrush(Colors.Cyan);
Button_Start.Background = new SolidColorBrush(Colors.Black);
}
else
{
Button_Stop.Background = new SolidColorBrush(Colors.Black);
Button_Start.Background = new SolidColorBrush(Colors.Cyan);
}
// Меняем цвет фона матрицы
ChangeBackground();
}
// Создание сетки элементов, в которой будет сыпаться матрица
public void CreateElement()
{
// Вычисляем тоговый размер шрифта для разметки / переразметки
this.fontSize = kolich + addingFontSize + addingSize;
// Создаем сетку ячеек
for (int i = 0; i < countWidth; i++)
{
for (int j = 0; j < countHeight; j++)
{
// Создаем TextBlock
TextBlock element = new TextBlock();
// Задаем имя элемента TextBlock
element.Name = "TB_" + i + "_" + j;
// Задаем начальный символ при инициализации сетки ячеек
// element.Text = char.ConvertFromUtf32(random.Next(0x4E00, 0x4FFF)); // Случайный символ из заданного диапазона
// element.Text = random.Next(0, 9).ToString(); // Случайным числом
element.Text = ""; // Пустота
// Задаем смещение каждого нового элемента TextBlock
// Также отвечает за разворот вертикальный / горизонтальный
int turnY = j * (kolich + addingSize);
int turnX = i * (kolich + addingSize);
// Включить (false), выключить (true) "поворот экрана"
if (turnOnOff)
{
// Вертикальное, стандартное расположение
element.Margin = new Thickness(turnX, turnY, 0, 0);
}
else
{
// Повернутое, горизонтальное расположение
element.Margin = new Thickness(turnY, turnX, 0, 0);
}
// Задаем цвет символа
element.Foreground = new SolidColorBrush(Colors.Green);
// Задаем размер шрифта
element.FontSize = fontSize;
// Добавляем созданный элемент в Grid
LayoutRootSecond.Children.Add(element);
}
}
}
// Событие при нажатии на эелемет Grid (на экран)
private void Event_Grid_Tap_LayoutRoot(object sender, System.Windows.Input.GestureEventArgs e)
{
// Количество одновременно появляющихся змеек после нажатия
for (int i = 0; i < countSimultaneously; i++)
{
// Останавливаем анимацию
if (flagOnOff) break;
Start();
//Задержка между вызовами. Для красоты матрицы.
Task.Delay(100);
}
}
// Метод запуска змейки
public async void Start()
{
int count;
// Количество змеек после нажатия на экран в очереди
for (count = 0; count < iteration; count++)
{
// Начало змейки по горизонтали случайным образом
int ranX = random.Next(0, countWidth);
// Начало змейки по вертикали случайным образом
int ranY = random.Next(-5, countHeight - 1);
// Длина змейки случайным образом
int length = random.Next(minLength, maxLength);
// Скорость смены символов в змейке случайным образом
int time = random.Next(speedFrom, speedTo);
await Task.Delay(1);
//Обработка змейки
await RandomElementQ_Async(ranX, ranY, length, time);
}
}
// Определяю элемент, в котором нужно менять символы
public async Task RandomElementQ_Async(int x, int y, int length, int timeOut)
{
// Словарь для хранения идентификаторов ячеек, которые вызывались на предыдущем этапе.
Dictionary<int, TextBlock> dicElem = new Dictionary<int, TextBlock>();
// Задаем цвет символов
Dictionary<string, int> SymbolColor = new Dictionary<string, int>();
// Счетчик, нужен для обработки случаев, когда не выполняется условие if ((y + i) < countHeight && (y + i) >= 0). Смотри на 4 строчки вниз.
// Тоесть элемент в котором нужно менять символы выше или ниже нашей сетки (матрицы элементов).
int count = 0;
// Цикл формирует змейку заданной длины length
for (int i = 0; i <= length; i++)
{
// Останавливаем анимацию
if (flagOnOff) break;
//Проверяем, что б змейка отображалась только в координатах, которые существуют в нашей сетке
if ((y + i) < countHeight && (y + i) >= 0)
{
// Формируем имя элемента, в котором будут меняться символы
string elementName = "TB_" + x + "_" + (y + i);
// Получаем элемент по его имени
object wantedNode = LayoutRoot.FindName(elementName);
TextBlock element = (TextBlock)wantedNode;
// Отправляем элемент в словарь, из которого он будет извлекаться для эффекта "падения" и "затухания" змейки
dicElem[count] = (element);
// Определяем коеффициент для подсчета яркости. Первый элемент(который падает) - всега самый яркий, последний - самый темный.
// Отнимаем 1, потому, что последний элемент (когда к - максимальное) в итоге получается больше 255 и становится ярким.
int A_Coefficient = (int)Math.Round((gradientFrom["A"] - 10) / (double)(count + 1)) - 1;
int R_Coefficient = (int)Math.Round((gradientFrom["R"] - gradientTo["R"]) / (double)(count + 1)) - 1;
int G_Coefficient = (int)Math.Round((gradientFrom["G"] - gradientTo["G"]) / (double)(count + 1)) - 1;
int B_Coefficient = (int)Math.Round((gradientFrom["B"] - gradientTo["B"]) / (double)(count + 1)) - 1;
//int greenCoefficient = (int)Math.Round(255 / (double)(count + 1)) - 1;
// Вызываем на прорисовку первый, самый яркий падающий элемент. Асинхронно.
// colorFirstSymbol["A"]. Цвет задается через настройку.
await Change(element, timeOut, colorFirstSymbol);
// Перебираем все элементы, составляющие змейку на данном этапе. С каждым циклом она увеличивается, пока не достигнет нужной длины.
for (int k = 0; k <= i; k++)
{
// Останавливаем анимацию
if (flagOnOff) break;
// Если змейка начинаеися "выше" начальных координат (например, если y = -5)
if (dicElem.ContainsKey(k))
{
//Извлекаем элементы, которые должны следовать за самым ярким. Создаем эффект "затухания" цвета
TextBlock previousElement = dicElem[k];
// Вызываем извлеченные элементы
// (greenCoefficient * (k + 1)) - 20 Высчитываем яркость так, что б разница между первым и последним была на всех змейках одинаковая
// и равномерно распределялась независимо от ее длины(количества элементов)
SymbolColor["A"] = (gradientFrom["A"] - ((i - k) * A_Coefficient));
SymbolColor["R"] = (gradientFrom["R"] - ((i - k) * R_Coefficient));
SymbolColor["G"] = (gradientFrom["G"] - ((i - k) * G_Coefficient));
SymbolColor["B"] = (gradientFrom["B"] - ((i - k) * B_Coefficient));
Task dsvv = Change(previousElement, timeOut, SymbolColor);
}
}
count++;
}
}
}
// Метод изменения символов в заданном элеменете
public async Task Change(TextBlock element, int timeOut, Dictionary<string, int> SymbolColor)
{
// Формируем нужный цвет с заданной яркостью
SolidColorBrush NewColor = new SolidColorBrush(new Color()
{
A = (byte)(SymbolColor["A"]) /*Opacity*/,
R = (byte)(SymbolColor["R"]) /*Red*/,
G = (byte)(SymbolColor["G"]) /*Green*/,
B = (byte)(SymbolColor["B"]) /*Blue*/
});
// При каждом "падении" на 1 клеточку равномерно "затухает"
element.Foreground = NewColor;
// Количество смены символов в каждой ячейке
for (int i = 0; i < countSymbol; i++)
{
// Останавливаем анимацию
if (flagOnOff) break;
// Каждый раз разный символ из заданного диапазона
element.Text = RandomActualSymbol();
// Размер шрифта
element.FontSize = fontSize;
// Скорость смены символов в ячейке
await Task.Delay(timeOut);
}
}
// Загрузка данных для элементов ViewModel
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
}
/* ****************************** События ****************************** */
// Настройки. Скорость смены символов.
private void Event_Button_Click_SpeedApplay(object sender, RoutedEventArgs e)
{
// Сохраняем значение их соответствующего TextBox TextBox_SppeedFrom в свойство класса speedFrom
this.speedFrom = int.Parse(TextBox_SppeedFrom.Text.ToString());
// Сохраняем значение их соответствующего TextBox TextBox_SppeedTo в свойство класса speedTo
this.speedTo = int.Parse(TextBox_SppeedTo.Text.ToString());
}
// Настройки. Количество змеек в очереди
private void Event_Button_Click_CountQueue(object sender, RoutedEventArgs e)
{
// Сохраняем значение их соответствующего TextBox TextBox_CountQueue в свойство класса iteration
this.iteration = int.Parse(TextBox_CountQueue.Text.ToString());
}
// Настройки. Количество змеек за нажатие
private void Event_Button_Click_СountSimultaneously(object sender, RoutedEventArgs e)
{
// Сохраняем значение их соответствующего TextBox TextBox_СountSimultaneously в свойство класса countSimultaneously
this.countSimultaneously = int.Parse(TextBox_СountSimultaneously.Text.ToString());
}
// Настройки. Размер шрифта
private void Event_Button_Click_FontSize(object sender, RoutedEventArgs e)
{
// Сохраняем значение их соответствующего TextBox TextBox_FontSize в свойство класса addingFontSize
this.addingFontSize = int.Parse(TextBox_FontSize.Text.ToString());
// Вычисляем тоговый размер шрифта для разметки / переразметки
this.fontSize = kolich + addingFontSize + addingSize;
}
// Настройки. Количество смены символов в ячейке
private void Event_Button_Click_CountSymbol(object sender, RoutedEventArgs e)
{
// Сохраняем значение их соответствующего TextBox TextBox_CountSymbol в свойство класса countSymbol
this.countSymbol = int.Parse(TextBox_CountSymbol.Text.ToString());
}
// Настройки. Выключаем возможность анимирования змеек
private void Event_Button_Click_Stop(object sender, RoutedEventArgs e)
{
this.flagOnOff = true;
// Если flagOnOff в true то подсвечиваем кнопку Stop
if (this.flagOnOff)
{
Button_Stop.Background = new SolidColorBrush(Colors.Cyan);
Button_Start.Background = new SolidColorBrush(Colors.Black);
}
}
// Настройки. Включаем возможность анимирования змеек
private void Event_Button_Click_Start(object sender, RoutedEventArgs e)
{
this.flagOnOff = false;
// Если flagOnOff в false то подсвечиваем кнопку Start
if (!this.flagOnOff)
{
Button_Stop.Background = new SolidColorBrush(Colors.Black);
Button_Start.Background = new SolidColorBrush(Colors.Cyan);
}
}
// Настройки. Очистка экрана
private void Event_Button_Click_Clear(object sender, RoutedEventArgs e)
{
// Перебираем сетку ячеек и устанавливаем в каждой ячейке как символ - пустоту
for ( int i = 0; i < countWidth; i++)
{
for (int j = 0; j < countHeight; j++)
{
// Формируем имя элемента, который будем очищать
string elementName = "TB_" + i + "_" + j;
// Получаем элемент по его имени
object wantedNode = LayoutRoot.FindName(elementName);
TextBlock element = (TextBlock)wantedNode;
// Очищаем значение
element.Text = "";
}
}
}
// Настройки. Размер клетки для символа
private void Event_Button_Click_ElementSize(object sender, RoutedEventArgs e)
{
// Сохраняем значение их соответствующего TextBox TextBox_ElementSize в свойство класса addingSize
this.addingSize = int.Parse(TextBox_ElementSize.Text.ToString());
// Количество строк и столбцов. Возвращаем для вертикали
this.countWidth = (int)Math.Round(ScreenWidth / (kolich + addingSize)) + Math.Abs(addingSize);
this.countHeight = (int)Math.Round(ScreenHeight / (kolich + addingSize)) + 5 + Math.Abs(addingSize);
// Останавливаем матрицу
this.flagOnOff = true;
// Удаляем матрицу (сами ячейки)
LayoutRootSecond.Children.Clear();
// Перерисовываем ячейки заново с новыми параметрами
CreateElement();
// Включаем матрицу
this.flagOnOff = false;
}
// Настройки. Задаем максимальную и минимальную длину змейки
private void Event_Button_Click_MaxLength(object sender, RoutedEventArgs e)
{
// Сохраняем значение их соответствующего TextBox TextBox_MinLength в свойство класса minLength
this.minLength = int.Parse(TextBox_MinLength.Text.ToString());
// Сохраняем значение их соответствующего TextBox TextBox_MaxLength в свойство класса maxLength
this.maxLength = int.Parse(TextBox_MaxLength.Text.ToString());
}
// Настройки. Вериткальная / горизонтальная матрица
private void Event_Button_Click_Turn(object sender, RoutedEventArgs e)
{
// Включить (true), выключить (false) "поворот экрана"
if (turnOnOff)
{
// Сохраняем значение false в свойство класса turnOnOff
this.turnOnOff = false;
// Надпись на конопке с именем ToggleButton_Turn меняю на Горизонтально
ToggleButton_Turn.Content = "Горизонтально";
// Количество строк и столбцов. Инвертируем для горизонтали.
this.countHeight = (int)Math.Round(ScreenWidth / (kolich + addingSize)) + 5 + Math.Abs(addingSize);
this.countWidth = (int)Math.Round(ScreenHeight / (kolich + addingSize)) + 5 + Math.Abs(addingSize);
}
else
{
// Сохраняем значение true в свойство класса turnOnOff
this.turnOnOff = true;
// Надпись на конопке с именем ToggleButton_Turn меняю на Вертикально
ToggleButton_Turn.Content = "Вертикально";
// Количество строк и столбцов. Возвращаем для вертикали
this.countWidth = (int)Math.Round(ScreenWidth / (kolich + addingSize)) + Math.Abs(addingSize);
this.countHeight = (int)Math.Round(ScreenHeight / (kolich + addingSize)) + 5 + Math.Abs(addingSize);
}
// Останавливаем матрицу
this.flagOnOff = true;
// Удаляем матрицу (сами ячейки)
LayoutRootSecond.Children.Clear();
// Перерисовываем ячейки заново с новыми параметрами
CreateElement();
// Включаем матрицу
this.flagOnOff = false;
}
// Заполняем словарь идентификаторами языка и соответствующие ему ASCII коды символов
public void ListLanguages()
{
// Добавляем в словарь ключь - название языка и значение - массив, состоящий из ASCII кодов символов.
languages.Add("Матрица", new int[] { 64, 127 });
languages.Add("Китаский", new int[] { 19968, 20223 });
languages.Add("Английский", new int[] { 64, 127 });
languages.Add("Цифры", new int[] { 48, 57 });
languages.Add("Случайные символы", new int[] { 0, 1000 });
languages.Add("Русский", new int[] { 1040, 1103 });
// Добавляем языки в всплывающую панель для возможности их выбора
foreach (var language in languages)
{
// Создаю кнопку
Button newLang = new Button();
// Задаю надпись кнопки, соответствует языку
newLang.Content = language.Key.ToString();
// Горизонтальное выравнивание
newLang.HorizontalAlignment = HorizontalAlignment.Stretch;
// Толщина рамки
newLang.BorderThickness = new Thickness(1);
// Смещение
newLang.Margin = new Thickness(0,0,0,0);
// Событие, при нажатии на кнопку. Одно на все.
newLang.Click += Event_Button_Click_SelectLanguageUpdate;
// Добавляю созданую и настроенную кнопку в всплывающее окно
StackPanel_ButtonDropDownSelectLanguage.Children.Add(newLang);
}
}
// Вызываем эту функцию везде, где нужно показать случайный символ из выбранного языка.
public string RandomActualSymbol()
{
// Получаем массив по ключу, содержащий ASCII коды символов языка, заданного в actualLanguage
int[] sd = (languages[actualLanguage]);
// Выбираем случайнфй символ в диапазоне от первого до последнего символа в заданом языке
return char.ConvertFromUtf32(this.random.Next((int)sd.GetValue(0), (int)sd.GetValue(1)));
}
// Кнопка, в которой показывается текущее выбранное значение языка. при нажатии на нее всплывает меню для выбора другого языка.
private void Event_Button_Click_SelectLanguage(object sender, RoutedEventArgs e)
{
// Показывать (true) / не показывать (false) всплывающее окно (PopUp) при выборе языка
if (flagShowLanguages)
{
// Показать всплывающее окно
Popup_ButtonDropDownSelectLanguage.IsOpen = true;
flagShowLanguages = false;
}
else
{
// Скрыть всплывающее окно
Popup_ButtonDropDownSelectLanguage.IsOpen = false;
flagShowLanguages = true;
}
}
// Всплывающее меню выбора языка.
private void Event_Button_Click_SelectLanguageUpdate(object sender, RoutedEventArgs e)
{
// Если нажата кнопка выбора языка, но другой язык не выбран, то при повторном нажатии меню свернется.
if (!flagShowLanguages)
{
// Скрыть всплывающее окно
Popup_ButtonDropDownSelectLanguage.IsOpen = false;
flagShowLanguages = true;
}
// Получаем название кнопки, которую нажали
string newLanguagr = (sender as Button).Content.ToString();
// Обновляем название кнопки, отображающей выбранный язык
Button_SelectLanguage.Content = newLanguagr;
// Задаем значение языка, в котором выбирать символы случайным образом
this.actualLanguage = newLanguagr;
}
// Меняем цвет фона матрицы
private void Event_Button_Click_ChangeBackground(object sender, RoutedEventArgs e)
{
// Передаем в свойство класса colorMatrixBackground выбранный цвет из элемента ColorPicker для фона матрицы
colorMatrixBackground["A"] = ColorPicker.Color.A;
colorMatrixBackground["R"] = ColorPicker.Color.R;
colorMatrixBackground["G"] = ColorPicker.Color.G;
colorMatrixBackground["B"] = ColorPicker.Color.B;
// Задаем выбранный цвет фона матрицы соответствующей кнопке
Button_BackgroundColor.Background = new SolidColorBrush(new Color()
{
A = (byte)(colorMatrixBackground["A"]) /*Opacity*/,
R = (byte)(colorMatrixBackground["R"]) /*Red*/,
G = (byte)(colorMatrixBackground["G"]) /*Green*/,
B = (byte)(colorMatrixBackground["B"]) /*Blue*/
});
// Задаем выбранный цвет фона матрицы
ChangeBackground();
}
// Метод изменения цвета фона матрицы. По умолчанию черный.
private void ChangeBackground()
{
// Задаю цвет фона матрицы
LayoutRootSecond.Background = new SolidColorBrush(new Color()
{
A = (byte)(colorMatrixBackground["A"]) /*Opacity*/,
R = (byte)(colorMatrixBackground["R"]) /*Red*/,
G = (byte)(colorMatrixBackground["G"]) /*Green*/,
B = (byte)(colorMatrixBackground["B"]) /*Blue*/
});
}
// Начальные настройки цветов фона, символов и т.д
private void BeginColorSettings()
{
// Задаем начальный цвет фона матрицы
colorMatrixBackground["A"] = 0;
colorMatrixBackground["R"] = 0;
colorMatrixBackground["G"] = 0;
colorMatrixBackground["B"] = 0;
// Задаем начальный цвет первого символа
colorFirstSymbol["A"] = 255;
colorFirstSymbol["R"] = 248;
colorFirstSymbol["G"] = 248;
colorFirstSymbol["B"] = 255;
// Задаем начальный цвет градиента от (второго символа в змейке)
gradientFrom["A"] = 255;
gradientFrom["R"] = 1;
gradientFrom["G"] = 255;
gradientFrom["B"] = 1;
// Задаем начальный цвет градиента до (последнего символа в змейке)
gradientTo["A"] = 0;
gradientTo["R"] = 0;
gradientTo["G"] = 0;
gradientTo["B"] = 0;
}
// Изменение цвета первого символа
private void Event_Button_Click_FirstSymbolColor(object sender, RoutedEventArgs e)
{
// Передаем в свойство класса colorFirstSymbol выбранный цвет из элемента ColorPicker для фона матрицы
colorFirstSymbol["A"] = ColorPicker.Color.A;
colorFirstSymbol["R"] = ColorPicker.Color.R;
colorFirstSymbol["G"] = ColorPicker.Color.G;
colorFirstSymbol["B"] = ColorPicker.Color.B;
// Задаем выбранный цвет фона матрицы соответствующей кнопке
Button_FirstSymbolColor.Background = new SolidColorBrush(new Color()
{
A = (byte)(colorFirstSymbol["A"]) /*Opacity*/,
R = (byte)(colorFirstSymbol["R"]) /*Red*/,
G = (byte)(colorFirstSymbol["G"]) /*Green*/,
B = (byte)(colorFirstSymbol["B"]) /*Blue*/
});
}
// Настройки. Задаем градиент змейки. Цвет второго символа.
private void Event_Button_Click_GradientFrom(object sender, RoutedEventArgs e)
{
// Передаем в свойство класса gradientFrom выбранный цвет из элемента ColorPicker для фона матрицы
gradientFrom["A"] = ColorPicker.Color.A;
gradientFrom["R"] = ColorPicker.Color.R;
gradientFrom["G"] = ColorPicker.Color.G;
gradientFrom["B"] = ColorPicker.Color.B;
// Задаем выбранный цвет градиента от змейки соответствующей кнопке
Button_GradientFrom.Background = new SolidColorBrush(new Color()
{
A = (byte)(gradientFrom["A"]) /*Opacity*/,
R = (byte)(gradientFrom["R"]) /*Red*/,
G = (byte)(gradientFrom["G"]) /*Green*/,
B = (byte)(gradientFrom["B"]) /*Blue*/
});
}
// Настройки. Задаем градиент змейки. Цвет последнего символа.
private void Event_Button_Click_GradientTo (object sender, RoutedEventArgs e)
{
// Передаем в свойство класса gradientTo выбранный цвет из элемента ColorPicker для фона матрицы
gradientTo["A"] = ColorPicker.Color.A;
gradientTo["R"] = ColorPicker.Color.R;
gradientTo["G"] = ColorPicker.Color.G;
gradientTo["B"] = ColorPicker.Color.B;
// Задаем выбранный цвет градиента до змейки соответствующей кнопке
Button_GradientTo.Background = new SolidColorBrush(new Color()
{
A = (byte)(gradientTo["A"]) /*Opacity*/,
R = (byte)(gradientTo["R"]) /*Red*/,
G = (byte)(gradientTo["G"]) /*Green*/,
B = (byte)(gradientTo["B"]) /*Blue*/
});
}
}
}
Выводы
Вторая часть окончена. Знаю, что код немного корявый, но как на меня так наиболее легко проследить логику. В следующей части займемся оптимизацией кода, сохранением настроек при выходе из приложения, локализацией под английский язык, привязкой к данным.
П.С. Если знаете как лучше сделать на данном этапе — прошу в комментарии. И не забывайте, что приложение создается этапами и будут еще как минимум 2 части… Буду благодарен, если подскажете, как решить проблему на последнем видео, так как панорама сдвигается только, если «хватать» за пределами ScrollViewer.