0 added
0 removed
Original
2026-01-01
Modified
2026-02-21
1
<p><a>#Руководства</a></p>
1
<p><a>#Руководства</a></p>
2
<ul><li>24 июн 2020</li>
2
<ul><li>24 июн 2020</li>
3
<li>0</li>
3
<li>0</li>
4
</ul><p>Клиент-серверная разработка - одна из самых востребованных отраслей программирования. Зная её азы, можно создавать как мессенджеры, так и онлайн-игры.</p>
4
</ul><p>Клиент-серверная разработка - одна из самых востребованных отраслей программирования. Зная её азы, можно создавать как мессенджеры, так и онлайн-игры.</p>
5
<p>vlada_maestro / shutterstock</p>
5
<p>vlada_maestro / shutterstock</p>
6
<p>Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.</p>
6
<p>Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.</p>
7
<p>В этой серии статей мы напишем клиент-серверное приложение на C# - простейший мессенджер. Серия состоит из трёх частей:</p>
7
<p>В этой серии статей мы напишем клиент-серверное приложение на C# - простейший мессенджер. Серия состоит из трёх частей:</p>
8
<ul><li>Вёрстка приложения - мы создадим графический интерфейс на C# и XAML для Windows.</li>
8
<ul><li>Вёрстка приложения - мы создадим графический интерфейс на C# и XAML для Windows.</li>
9
<li>Создание WebAPI на ASP.NET - составим базу данных и разработаем серверную часть приложения.</li>
9
<li>Создание WebAPI на ASP.NET - составим базу данных и разработаем серверную часть приложения.</li>
10
<li>Объединение клиента и сервера - напишем запросы к серверу и позаботимся, чтобы всё работало как надо.</li>
10
<li>Объединение клиента и сервера - напишем запросы к серверу и позаботимся, чтобы всё работало как надо.</li>
11
</ul><p>Язык C# пригодится в разработке чего угодно. Возможности WPF<em>(система создания графических интерфейсов)</em>позволяют создавать красивые и функциональные приложения для Windows, а ASP.NET - мощные серверные приложения.</p>
11
</ul><p>Язык C# пригодится в разработке чего угодно. Возможности WPF<em>(система создания графических интерфейсов)</em>позволяют создавать красивые и функциональные приложения для Windows, а ASP.NET - мощные серверные приложения.</p>
12
<p>Я постараюсь объяснить подробно, но охватить всё невозможно, поэтому вам нужно знать основы C#, ООП, ASP.NET, WPF и работы в Visual Studio.</p>
12
<p>Я постараюсь объяснить подробно, но охватить всё невозможно, поэтому вам нужно знать основы C#, ООП, ASP.NET, WPF и работы в Visual Studio.</p>
13
<p>Вот несколько статей, с которыми стоит ознакомиться, если вы чего-то не знаете:</p>
13
<p>Вот несколько статей, с которыми стоит ознакомиться, если вы чего-то не знаете:</p>
14
<ul><li><a>ООП в C#</a><em>(серия статей).</em></li>
14
<ul><li><a>ООП в C#</a><em>(серия статей).</em></li>
15
<li><a>Как за час создать сайт на ASP.NET</a>.</li>
15
<li><a>Как за час создать сайт на ASP.NET</a>.</li>
16
<li><a>Что такое MVVM: проектирование приложений для Windows</a>.</li>
16
<li><a>Что такое MVVM: проектирование приложений для Windows</a>.</li>
17
<li><a>Асинхронное программирование: Как работает процессор</a><em>(серия статей).</em></li>
17
<li><a>Асинхронное программирование: Как работает процессор</a><em>(серия статей).</em></li>
18
</ul><p>Мы не<strong></strong>рассматриваем регистрацию, поиск контактов, хранение сообщений, продвинутый дизайн. Вместо этого вы узнаете азы создания клиент-серверных приложений и сможете сделать всё самостоятельно.</p>
18
</ul><p>Мы не<strong></strong>рассматриваем регистрацию, поиск контактов, хранение сообщений, продвинутый дизайн. Вместо этого вы узнаете азы создания клиент-серверных приложений и сможете сделать всё самостоятельно.</p>
19
<p>Исходный код мессенджера вы найдете на <a>GitHub</a>.</p>
19
<p>Исходный код мессенджера вы найдете на <a>GitHub</a>.</p>
20
<p>Приложение мы поделим на экраны:</p>
20
<p>Приложение мы поделим на экраны:</p>
21
<ul><li>экран авторизации;</li>
21
<ul><li>экран авторизации;</li>
22
<li>экран с контактами;</li>
22
<li>экран с контактами;</li>
23
<li>экран с чатом.</li>
23
<li>экран с чатом.</li>
24
</ul><p><strong>Экран</strong> - это элемент<em>Border</em>, который по умолчанию скрыт от пользователя. Виден будет только активный экран.</p>
24
</ul><p><strong>Экран</strong> - это элемент<em>Border</em>, который по умолчанию скрыт от пользователя. Виден будет только активный экран.</p>
25
<p>На <strong>экране авторизации</strong>пользователь сможет ввести логин и пароль, чтобы войти. Если он ввёл верные данные, то перейдёт на экран с контактами, иначе - увидит сообщение об ошибке.</p>
25
<p>На <strong>экране авторизации</strong>пользователь сможет ввести логин и пароль, чтобы войти. Если он ввёл верные данные, то перейдёт на экран с контактами, иначе - увидит сообщение об ошибке.</p>
26
<p>На <strong>экране с контактами</strong>видны имена других пользователей, с которыми ведётся переписка. Чат открывается при нажатии на имя другого пользователя.</p>
26
<p>На <strong>экране с контактами</strong>видны имена других пользователей, с которыми ведётся переписка. Чат открывается при нажатии на имя другого пользователя.</p>
27
<p>На <strong>экране с чатом</strong>видна переписка с одним конкретным контактом. Пользователь может написать и отправить новое сообщение или вернуться к экрану с контактами.</p>
27
<p>На <strong>экране с чатом</strong>видна переписка с одним конкретным контактом. Пользователь может написать и отправить новое сообщение или вернуться к экрану с контактами.</p>
28
<p>Начнём с определения стилей. В них минимально обозначим, как должны выглядеть элементы, и сразу укажем, что экраны по умолчанию должны быть скрыты:</p>
28
<p>Начнём с определения стилей. В них минимально обозначим, как должны выглядеть элементы, и сразу укажем, что экраны по умолчанию должны быть скрыты:</p>
29
<Style x:Key="Screen"> <Style.Setters> <Setter Property="Border.Visibility" Value="Hidden" /> <Setter Property="Border.Background" Value="#151515" /> </Style.Setters> </Style> <Style x:Key="LoginPanel"> <Style.Setters> <Setter Property="StackPanel.Orientation" Value="Vertical" /> <Setter Property="StackPanel.VerticalAlignment" Value="Center" /> </Style.Setters> </Style> <Style x:Key="TextBoxBase"> <Style.Setters> <Setter Property="TextBox.Background" Value="#333" /> <Setter Property="TextBox.Foreground" Value="#f6f6f6" /> <Setter Property="TextBox.Margin" Value="5"/> <Setter Property="TextBox.Padding" Value="15 10"/> <Setter Property="TextBox.HorizontalAlignment" Value="Center" /> <Setter Property="TextBox.Width" Value="250" /> </Style.Setters> </Style> <Style x:Key="ButtonBase" > <Style.Setters> <Setter Property="Button.Background" Value="#333" /> <Setter Property="Button.Foreground" Value="#f6f6f6" /> <Setter Property="Button.Margin" Value="5"/> <Setter Property="Button.Padding" Value="50 10"/> <Setter Property="Button.HorizontalAlignment" Value="Center" /> <Setter Property="Button.FontSize" Value="14" /> </Style.Setters> </Style><p>Теперь сверстаем сам экран авторизации - он должен быть видимым:</p>
29
<Style x:Key="Screen"> <Style.Setters> <Setter Property="Border.Visibility" Value="Hidden" /> <Setter Property="Border.Background" Value="#151515" /> </Style.Setters> </Style> <Style x:Key="LoginPanel"> <Style.Setters> <Setter Property="StackPanel.Orientation" Value="Vertical" /> <Setter Property="StackPanel.VerticalAlignment" Value="Center" /> </Style.Setters> </Style> <Style x:Key="TextBoxBase"> <Style.Setters> <Setter Property="TextBox.Background" Value="#333" /> <Setter Property="TextBox.Foreground" Value="#f6f6f6" /> <Setter Property="TextBox.Margin" Value="5"/> <Setter Property="TextBox.Padding" Value="15 10"/> <Setter Property="TextBox.HorizontalAlignment" Value="Center" /> <Setter Property="TextBox.Width" Value="250" /> </Style.Setters> </Style> <Style x:Key="ButtonBase" > <Style.Setters> <Setter Property="Button.Background" Value="#333" /> <Setter Property="Button.Foreground" Value="#f6f6f6" /> <Setter Property="Button.Margin" Value="5"/> <Setter Property="Button.Padding" Value="50 10"/> <Setter Property="Button.HorizontalAlignment" Value="Center" /> <Setter Property="Button.FontSize" Value="14" /> </Style.Setters> </Style><p>Теперь сверстаем сам экран авторизации - он должен быть видимым:</p>
30
<Border Style="{StaticResource Screen}" Name="LoginScreen" Visibility="Visible"> <StackPanel Style="{StaticResource LoginPanel}"> <TextBlock Text="Login" Style="{StaticResource HeaderBlock}" /> <TextBox Style="{StaticResource TextBoxBase}" Name="LoginBox" /> <PasswordBox Style="{StaticResource TextBoxBase}" Name="PasswordBox"/> <Button Content="Enter" Style="{StaticResource ButtonBase}" Name="LoginButton" Click="LoginButton_Click" IsDefault="True"/> <TextBlock Text="" Name="LoginMessageBlock" Style="{StaticResource WarningBlock}" Visibility="Hidden"/> </StackPanel> </Border><p>Давайте посмотрим, как это выглядит:</p>
30
<Border Style="{StaticResource Screen}" Name="LoginScreen" Visibility="Visible"> <StackPanel Style="{StaticResource LoginPanel}"> <TextBlock Text="Login" Style="{StaticResource HeaderBlock}" /> <TextBox Style="{StaticResource TextBoxBase}" Name="LoginBox" /> <PasswordBox Style="{StaticResource TextBoxBase}" Name="PasswordBox"/> <Button Content="Enter" Style="{StaticResource ButtonBase}" Name="LoginButton" Click="LoginButton_Click" IsDefault="True"/> <TextBlock Text="" Name="LoginMessageBlock" Style="{StaticResource WarningBlock}" Visibility="Hidden"/> </StackPanel> </Border><p>Давайте посмотрим, как это выглядит:</p>
31
<a></a><p>Теперь напишем обработчик для кнопки<em>Enter</em>:</p>
31
<a></a><p>Теперь напишем обработчик для кнопки<em>Enter</em>:</p>
32
//Обработчик нажатия на кнопку Login private void LoginButton_Click(object sender, RoutedEventArgs e) { //Пока используем тестовые данные if(LoginBox.Text == "admin" && PasswordBox.Password == "12345") { //Если логин и пароль верные, то переходим на другой экран Open(ContactsScreen); } else { //Иначе выводим сообщение об ошибке авторизации LoginMessageBlock.Text = "Wrong login or password!"; LoginMessageBlock.Visibility = Visibility.Visible; } } //Метод для открытия другого экрана private void Open(Border screen) { //Делаем все экраны невидимыми LoginScreen.Visibility = Visibility.Hidden; ContactsScreen.Visibility = Visibility.Hidden; ChatScreen.Visibility = Visibility.Hidden; //Делаем видимым необходимый экран screen.Visibility = Visibility.Visible; }<p>У вас должны быть экраны с именами<em>ContactsScreen</em>и <em>ChatScreen</em>, чтобы метод<em>Open ()</em>работал корректно. Для этого достаточно создать два пустых элемента<em>Border</em>.</p>
32
//Обработчик нажатия на кнопку Login private void LoginButton_Click(object sender, RoutedEventArgs e) { //Пока используем тестовые данные if(LoginBox.Text == "admin" && PasswordBox.Password == "12345") { //Если логин и пароль верные, то переходим на другой экран Open(ContactsScreen); } else { //Иначе выводим сообщение об ошибке авторизации LoginMessageBlock.Text = "Wrong login or password!"; LoginMessageBlock.Visibility = Visibility.Visible; } } //Метод для открытия другого экрана private void Open(Border screen) { //Делаем все экраны невидимыми LoginScreen.Visibility = Visibility.Hidden; ContactsScreen.Visibility = Visibility.Hidden; ChatScreen.Visibility = Visibility.Hidden; //Делаем видимым необходимый экран screen.Visibility = Visibility.Visible; }<p>У вас должны быть экраны с именами<em>ContactsScreen</em>и <em>ChatScreen</em>, чтобы метод<em>Open ()</em>работал корректно. Для этого достаточно создать два пустых элемента<em>Border</em>.</p>
33
<p>Вот как выглядит экран авторизации при вводе неверных данных:</p>
33
<p>Вот как выглядит экран авторизации при вводе неверных данных:</p>
34
<a></a><p>Дальше сверстаем экран с контактами.</p>
34
<a></a><p>Дальше сверстаем экран с контактами.</p>
35
<p>Экран с контактами разделим на два ряда с помощью Grid - заголовок в первом ряду и список контактов во втором. Список - это элемент<em>ListBox</em>, в котором перечислены контакты.</p>
35
<p>Экран с контактами разделим на два ряда с помощью Grid - заголовок в первом ряду и список контактов во втором. Список - это элемент<em>ListBox</em>, в котором перечислены контакты.</p>
36
<Border Name="ContactsScreen" Style="{StaticResource Screen}"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="50" /> <RowDefinition /> </Grid.RowDefinitions> <Border Grid.Row="0" Style="{StaticResource HeaderBorder}"> <TextBlock Style="{StaticResource HeaderBlock}" Text="Contacts" VerticalAlignment="Center"/> </Border> <Border Grid.Row="1"> <ListBox ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True" Style="{StaticResource ContactsList}" Name="ContactsList" SelectionChanged="ContactsList_SelectionChanged"> <ListBox.ItemTemplate> <DataTemplate> <ListBoxItem> <DockPanel LastChildFill="True"> <Image Style="{StaticResource ContactImage}" DockPanel.Dock="Left"></Image> <TextBlock Text="{Binding Name}" Style="{StaticResource ContactName}" DockPanel.Dock="Right"/> </DockPanel> </ListBoxItem> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Border> </Grid> </Border><p>Здесь описаны не все элементы списка, а только шаблон для них, который будет использоваться во время привязки данных. Этот шаблон понадобится в готовом приложении, но для удобства вёрстки вы можете подготовить тестовые данные.</p>
36
<Border Name="ContactsScreen" Style="{StaticResource Screen}"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="50" /> <RowDefinition /> </Grid.RowDefinitions> <Border Grid.Row="0" Style="{StaticResource HeaderBorder}"> <TextBlock Style="{StaticResource HeaderBlock}" Text="Contacts" VerticalAlignment="Center"/> </Border> <Border Grid.Row="1"> <ListBox ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True" Style="{StaticResource ContactsList}" Name="ContactsList" SelectionChanged="ContactsList_SelectionChanged"> <ListBox.ItemTemplate> <DataTemplate> <ListBoxItem> <DockPanel LastChildFill="True"> <Image Style="{StaticResource ContactImage}" DockPanel.Dock="Left"></Image> <TextBlock Text="{Binding Name}" Style="{StaticResource ContactName}" DockPanel.Dock="Right"/> </DockPanel> </ListBoxItem> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Border> </Grid> </Border><p>Здесь описаны не все элементы списка, а только шаблон для них, который будет использоваться во время привязки данных. Этот шаблон понадобится в готовом приложении, но для удобства вёрстки вы можете подготовить тестовые данные.</p>
37
<p>Зададим стили:</p>
37
<p>Зададим стили:</p>
38
<Style x:Key="ContactsList" BasedOn="{StaticResource TextBlockBase}"> <Style.Setters> <Setter Property="ListBox.Background" Value="#151515"/> <Setter Property="ListBox.BorderThickness" Value="0"/> </Style.Setters> </Style> <Style x:Key="ContactImage" BasedOn="{StaticResource TextBlockBase}"> <Style.Setters> </Style.Setters> </Style> <Style x:Key="ContactName" BasedOn="{StaticResource TextBlockBase}"> <Style.Setters> <Setter Property="TextBlock.HorizontalAlignment" Value="Left"/> </Style.Setters> </Style> <Style x:Key="HeaderBorder"> <Style.Setters> <Setter Property="Border.Background" Value="#222" /> </Style.Setters> </Style><p>Теперь остаётся написать метод, который открывает чат с контактом. Этот метод вызывается, когда пользователь выберет пункт из списка.</p>
38
<Style x:Key="ContactsList" BasedOn="{StaticResource TextBlockBase}"> <Style.Setters> <Setter Property="ListBox.Background" Value="#151515"/> <Setter Property="ListBox.BorderThickness" Value="0"/> </Style.Setters> </Style> <Style x:Key="ContactImage" BasedOn="{StaticResource TextBlockBase}"> <Style.Setters> </Style.Setters> </Style> <Style x:Key="ContactName" BasedOn="{StaticResource TextBlockBase}"> <Style.Setters> <Setter Property="TextBlock.HorizontalAlignment" Value="Left"/> </Style.Setters> </Style> <Style x:Key="HeaderBorder"> <Style.Setters> <Setter Property="Border.Background" Value="#222" /> </Style.Setters> </Style><p>Теперь остаётся написать метод, который открывает чат с контактом. Этот метод вызывается, когда пользователь выберет пункт из списка.</p>
39
private void ContactsList_SelectionChanged(object sender, SelectionChangedEventArgs e) { //Метод вызывается, когда меняется индекс выделенного элемента //При выделении элемент списка будет подсвечиваться //Чтобы убрать это, мы будем менять индекс на -1 //Чтобы метод не срабатывал повторно, мы проверяем, чтобы индекс был больше или равен 0 if(ContactsList.SelectedIndex >= 0) { //Тут будет код загрузки сообщений из чата //Сбрасываем индекс ContactsList.SelectedIndex = -1; Open(ChatScreen); } }<p>Чат похож на экран с контактами, но немного дополненный:</p>
39
private void ContactsList_SelectionChanged(object sender, SelectionChangedEventArgs e) { //Метод вызывается, когда меняется индекс выделенного элемента //При выделении элемент списка будет подсвечиваться //Чтобы убрать это, мы будем менять индекс на -1 //Чтобы метод не срабатывал повторно, мы проверяем, чтобы индекс был больше или равен 0 if(ContactsList.SelectedIndex >= 0) { //Тут будет код загрузки сообщений из чата //Сбрасываем индекс ContactsList.SelectedIndex = -1; Open(ChatScreen); } }<p>Чат похож на экран с контактами, но немного дополненный:</p>
40
<a></a><p><em>Grid</em>делит экран на три части: заголовок, чат и поле ввода. В заголовке - имя собеседника и кнопка "Назад". В чате выведены сообщения с помощью<em>ListBox</em>, а внизу находятся поле ввода и кнопка отправки.</p>
40
<a></a><p><em>Grid</em>делит экран на три части: заголовок, чат и поле ввода. В заголовке - имя собеседника и кнопка "Назад". В чате выведены сообщения с помощью<em>ListBox</em>, а внизу находятся поле ввода и кнопка отправки.</p>
41
<Border Name="ChatScreen" Style="{StaticResource Screen}"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="50" /> <RowDefinition /> <RowDefinition Height="50"/> </Grid.RowDefinitions> <Border Grid.Row="0" Style="{StaticResource HeaderBorder}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*" /> <ColumnDefinition Width="6*" /> <ColumnDefinition Width="1*" /> </Grid.ColumnDefinitions> <Button Style="{StaticResource NavButton}" Grid.Column="0" Name="BackButton" Content="←" Click="BackButton_Click"/> <TextBlock Style="{StaticResource HeaderBlock}" Text="" VerticalAlignment="Center" Name="ChatName" Grid.Column="1"/> </Grid> </Border> <Border Grid.Row="1"> <ListBox ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True" Style="{StaticResource ContactsList}" Name="MessagesList" Focusable="False" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"> <ListBox.ItemTemplate> <DataTemplate> <ListBoxItem> <Border Style="{StaticResource MessageBorder}" HorizontalAlignment="{Binding Alignment}"> <StackPanel Orientation="Vertical"> <TextBlock Text="{Binding Text}" Style="{StaticResource MessageText}"/> <TextBlock Text="{Binding Date}" Style="{StaticResource MessageDate}"/> </StackPanel> </Border> </ListBoxItem> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Border> <Border Grid.Row="2" Style="{StaticResource HeaderBorder}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="6*" /> <ColumnDefinition Width="1*" /> </Grid.ColumnDefinitions> <TextBox Name="MessageBox" Style="{StaticResource MessageBox}" Grid.Column="0"/> <Button Style="{StaticResource NavButton}" Grid.Column="1" Name="SendButton" Content="→" Click="SendButton_Click"/> </Grid> </Border> </Grid> </Border><p>Тут, как на экране с контактами, пока нет данных -- только шаблон для их вывода. Немного стилей:</p>
41
<Border Name="ChatScreen" Style="{StaticResource Screen}"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="50" /> <RowDefinition /> <RowDefinition Height="50"/> </Grid.RowDefinitions> <Border Grid.Row="0" Style="{StaticResource HeaderBorder}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*" /> <ColumnDefinition Width="6*" /> <ColumnDefinition Width="1*" /> </Grid.ColumnDefinitions> <Button Style="{StaticResource NavButton}" Grid.Column="0" Name="BackButton" Content="←" Click="BackButton_Click"/> <TextBlock Style="{StaticResource HeaderBlock}" Text="" VerticalAlignment="Center" Name="ChatName" Grid.Column="1"/> </Grid> </Border> <Border Grid.Row="1"> <ListBox ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True" Style="{StaticResource ContactsList}" Name="MessagesList" Focusable="False" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"> <ListBox.ItemTemplate> <DataTemplate> <ListBoxItem> <Border Style="{StaticResource MessageBorder}" HorizontalAlignment="{Binding Alignment}"> <StackPanel Orientation="Vertical"> <TextBlock Text="{Binding Text}" Style="{StaticResource MessageText}"/> <TextBlock Text="{Binding Date}" Style="{StaticResource MessageDate}"/> </StackPanel> </Border> </ListBoxItem> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Border> <Border Grid.Row="2" Style="{StaticResource HeaderBorder}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="6*" /> <ColumnDefinition Width="1*" /> </Grid.ColumnDefinitions> <TextBox Name="MessageBox" Style="{StaticResource MessageBox}" Grid.Column="0"/> <Button Style="{StaticResource NavButton}" Grid.Column="1" Name="SendButton" Content="→" Click="SendButton_Click"/> </Grid> </Border> </Grid> </Border><p>Тут, как на экране с контактами, пока нет данных -- только шаблон для их вывода. Немного стилей:</p>
42
<Style x:Key="MessageBorder"> <Style.Setters> <Setter Property="Border.Background" Value="#555" /> <Setter Property="Border.CornerRadius" Value="13" /> <Setter Property="Border.MinWidth" Value="100" /> <Setter Property="Border.MaxWidth" Value="300" /> <Setter Property="Border.Padding" Value="2" /> </Style.Setters> </Style> <Style x:Key="MessageText" BasedOn="{StaticResource TextBlockBase}"> <Style.Setters> <Setter Property="TextBlock.TextWrapping" Value="Wrap" /> <Setter Property="TextBlock.Margin" Value="0" /> </Style.Setters> </Style> <Style x:Key="MessageDate" BasedOn="{StaticResource TextBlockBase}"> <Style.Setters> <Setter Property="TextBlock.HorizontalAlignment" Value="Right" /> <Setter Property="TextBlock.FontSize" Value="8" /> <Setter Property="TextBlock.Margin" Value="0" /> </Style.Setters> </Style><p>Остаётся только обработать события для кнопок отправки и навигации:</p>
42
<Style x:Key="MessageBorder"> <Style.Setters> <Setter Property="Border.Background" Value="#555" /> <Setter Property="Border.CornerRadius" Value="13" /> <Setter Property="Border.MinWidth" Value="100" /> <Setter Property="Border.MaxWidth" Value="300" /> <Setter Property="Border.Padding" Value="2" /> </Style.Setters> </Style> <Style x:Key="MessageText" BasedOn="{StaticResource TextBlockBase}"> <Style.Setters> <Setter Property="TextBlock.TextWrapping" Value="Wrap" /> <Setter Property="TextBlock.Margin" Value="0" /> </Style.Setters> </Style> <Style x:Key="MessageDate" BasedOn="{StaticResource TextBlockBase}"> <Style.Setters> <Setter Property="TextBlock.HorizontalAlignment" Value="Right" /> <Setter Property="TextBlock.FontSize" Value="8" /> <Setter Property="TextBlock.Margin" Value="0" /> </Style.Setters> </Style><p>Остаётся только обработать события для кнопок отправки и навигации:</p>
43
private void SendButton_Click(object sender, RoutedEventArgs e) { string text = ""; if(!string.IsNullOrEmpty(MessageBox.Text)) { text = MessageBox.Text.Trim(); } if(!string.IsNullOrEmpty(text)) { bool result = true; if(result) { MessageBox.Text = ""; } } } private void BackButton_Click(object sender, RoutedEventArgs e) { Open(ContactsScreen); }<p>У нас получился довольно минималистичный дизайн, который вы можете доработать как вам нравится. Дальше нам предстоит написать серверную часть приложения, а потом связать их вместе.</p>
43
private void SendButton_Click(object sender, RoutedEventArgs e) { string text = ""; if(!string.IsNullOrEmpty(MessageBox.Text)) { text = MessageBox.Text.Trim(); } if(!string.IsNullOrEmpty(text)) { bool result = true; if(result) { MessageBox.Text = ""; } } } private void BackButton_Click(object sender, RoutedEventArgs e) { Open(ContactsScreen); }<p>У нас получился довольно минималистичный дизайн, который вы можете доработать как вам нравится. Дальше нам предстоит написать серверную часть приложения, а потом связать их вместе.</p>
44
<a>Практический курс: "Веб-вёрстка" Узнать о курсе</a>
44
<a>Практический курс: "Веб-вёрстка" Узнать о курсе</a>