Статьи

Создание клиента Twitter для Windows Phone — Создание основного пользовательского интерфейса

Настало время перейти к одной из самых важных частей MangoTree — базовому интерфейсу, который будет использоваться для отображения следующих данных одним касанием:

  • График
  • Упоминания
  • DMs
  • ретвитов
  • Поисковые

Это пять основных возможностей, которые должны быть размещены в пяти отдельных разделах. Вопрос — какой контроль использовать? Мы могли бы пойти в метро и воспользоваться панорамой или панелью управления. Это было бы хорошо, но в этом случае будет избыточное пойло (например , получение от временной шкалы к зеркалам потребуются , по крайней мере , два пойла). Мы также могли бы создать список на главной странице, чем уже занимаются такие клиенты Twitter, как Beezz . Это неудобно, потому что это потребует от пользователя постоянно перемещаться между различными страницами, чтобы попасть в нужный раздел.

В конце — я решил использовать TabControl . Официально он не поддерживается в Windows Phone, но, тем не менее, работает, так как является основным компонентом Silverlight.

TabControl? Но это не Метро!

Используйте то, что лучше для текущей ситуации. Вам нравится Панорама или Пивот ? Не стесняйтесь реализовать это таким образом. С точки зрения удобства использования, TabControl является лучшим выбором для этого случая . Это на самом деле не такая уж большая проблема, как это делают другие люди .

Шаг 1: Добавьте правильную ссылку на пространство имен и измененный стиль

Я надеюсь, что после прочтения предыдущей статьи из этой серии вы уже ознакомились со статьей, которую я написал об использовании TabControl в приложении для Windows Phone. Если нет, пожалуйста, перейдите туда сначала .

Я назвал свой стиль MetroTabItem и поместил его в файл App.xaml . Затем я приступил к созданию TabControl как части основной рабочей сетки в WorkPage.xaml .

Ваш код объявления TabControl должен выглядеть следующим образом:

<cc:TabControl Background="Transparent">
    <cc:TabItem Style="{StaticResource MetroTabItem}" Height="80" Width="89" Header="Tab 1" Foreground="Black"></cc:TabItem>
    <cc:TabItem Style="{StaticResource MetroTabItem}" Height="80" Width="89" Header="Tab 2" Foreground="Black"></cc:TabItem>
    <cc:TabItem Style="{StaticResource MetroTabItem}" Height="80" Width="89" Header="Tab 3" Foreground="Black"></cc:TabItem>
    <cc:TabItem Style="{StaticResource MetroTabItem}" Height="80" Width="89" Header="Tab 4" Foreground="Black"></cc:TabItem>
    <cc:TabItem Style="{StaticResource MetroTabItem}" Height="80" Width="89" Header="Tab 5" Foreground="Black"></cc:TabItem>
</cc:TabControl>

Убедитесь , что вы установили Фоновую свойство для TabControl в прозрачный , поэтому мы не будем иметь дело с белым фоном (набор по умолчанию).

Теперь ваш главный экран должен выглядеть примерно так:

Я отредактировал стиль по умолчанию, поэтому он работает в соответствии с выбранной в данный момент темой Windows Phone — если я узнал кое-что о приложениях, использующих фиксированные цвета, то это то, что иногда они не работают достаточно хорошо, когда пользователь решает переключить тема от темного (по умолчанию) до светлого, особенно если в самом приложении разработчик смешал статические цвета с зависимыми от темы. 

Вот модифицированный XAML:

<Style x:Key="MetroTabItem" TargetType="cc:TabItem">
    <Setter Property="IsTabStop" Value="False"/>
    <Setter Property="Background" Value="{StaticResource PhoneBackgroundBrush}"/>
    <Setter Property="BorderBrush" Value="#FFA3AEB9"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Padding" Value="6,2,6,2"/>
    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
    <Setter Property="MinWidth" Value="5"/>
    <Setter Property="MinHeight" Value="5"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="cc:TabItem">
                <Grid x:Name="Root">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualStateGroup.Transitions>
                                <VisualTransition GeneratedDuration="0"/>
                                <VisualTransition GeneratedDuration="0:0:0.1" To="MouseOver"/>
                            </VisualStateGroup.Transitions>
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="MouseOver">
                                <Storyboard>
                                    <DoubleAnimationUsingKeyFrames BeginTime="0" Duration="00:00:00.001" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="FocusVisualTop">
                                        <SplineDoubleKeyFrame KeyTime="0" Value="0"/>
                                    </DoubleAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Disabled">
                                <Storyboard>
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="DisabledVisualTopSelected">
                                        <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="DisabledVisualTopUnSelected">
                                        <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="SelectionStates">
                            <VisualState x:Name="Unselected"/>
                            <VisualState x:Name="Selected"/>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="FocusStates">
                            <VisualState x:Name="Focused">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="FocusVisualTop">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Unfocused">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="FocusVisualElement">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Grid x:Name="TemplateTopSelected" Visibility="Collapsed" Canvas.ZIndex="1">
                        <Border BorderBrush="{StaticResource PhoneForegroundBrush}" BorderThickness="1,1,1,0" Background="{StaticResource PhoneForegroundBrush}" CornerRadius="3,3,0,0" Margin="-2,-2,-2,0">
                            <Border BorderBrush="{StaticResource PhoneForegroundBrush}" BorderThickness="1" CornerRadius="1,1,0,0">
                                <Grid>
                                    <Rectangle Fill="{StaticResource PhoneBackgroundBrush }" Margin="0,0,0,-2"/>
                                    <ContentControl x:Name="HeaderTopSelected" Cursor="{TemplateBinding Cursor}" Foreground="{StaticResource PhoneForegroundBrush}" FontSize="{TemplateBinding FontSize}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" IsTabStop="False" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalAlignment}"/>
                                </Grid>
                            </Border>
                        </Border>
                        <Border x:Name="FocusVisualTop" BorderBrush="{StaticResource PhoneForegroundBrush}" BorderThickness="1,1,1,0" CornerRadius="3,3,0,0" IsHitTestVisible="false" Margin="-2,-2,-2,0" Visibility="Collapsed"/>
                        <Border x:Name="DisabledVisualTopSelected" Background="{StaticResource PhoneDisabledBrush}" CornerRadius="3,3,0,0" IsHitTestVisible="false" Margin="-2,-2,-2,0" Opacity="0"/>
                    </Grid>
                    <Grid x:Name="TemplateTopUnselected" Visibility="Collapsed">
                        <Border x:Name="BorderTop" BorderBrush="{StaticResource PhoneForegroundBrush}" BorderThickness="1" Background="{StaticResource PhoneDisabledBrush}" CornerRadius="3,3,0,0">
                            <Grid>
                                <ContentControl x:Name="HeaderTopUnselected" Cursor="{TemplateBinding Cursor}" Foreground="{StaticResource PhoneForegroundBrush}" FontSize="{TemplateBinding FontSize}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" IsTabStop="False" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalAlignment}"/>
                            </Grid>
                        </Border>
                        <Border x:Name="DisabledVisualTopUnSelected" Background="{StaticResource PhoneDisabledBrush}" CornerRadius="3,3,0,0" IsHitTestVisible="false" Opacity="0"/>
                    </Grid>
                    <Border x:Name="FocusVisualElement" BorderBrush="White" BorderThickness="1" CornerRadius="3,3,0,0" IsHitTestVisible="false" Margin="-1" Visibility="Collapsed"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Если вам нужна ссылка на существующие кисти и цвета, пожалуйста, прочитайте эту статью . Теперь вы можете легко сравнить, что мы сделали:

Выглядит намного лучше — над этим еще предстоит поработать, но мы добираемся.

Шаг 2: Установите заголовки TabItem

Следующим шагом будет правильная маркировка заголовков TabItem , чтобы мы знали, что связано с каждым из них. Также нам, вероятно, понадобятся иконки. Я использовал значки из Templarian , добавив белый штрих для совместимости тем, и я в итоге установил шаблон заголовка для каждого TabItem :

<cc:TabControl Background="Transparent">
    <cc:TabItem Style="{StaticResource MetroTabItem}" Height="80" Width="89" Header="Tab 1">
        <cc:TabItem.HeaderTemplate>
            <DataTemplate>
                <Image Source="/Images/appbar.timeline.png"></Image>
            </DataTemplate>
        </cc:TabItem.HeaderTemplate>
    </cc:TabItem>
    <cc:TabItem Style="{StaticResource MetroTabItem}" Height="80" Width="89" Header="Tab 2">
        <cc:TabItem.HeaderTemplate>
            <DataTemplate>
                <Image Source="/Images/appbar.mentions.png"></Image>
            </DataTemplate>
        </cc:TabItem.HeaderTemplate>
    </cc:TabItem>
    <cc:TabItem Style="{StaticResource MetroTabItem}" Height="80" Width="89" Header="Tab 3">
        <cc:TabItem.HeaderTemplate>
            <DataTemplate>
                <Image Source="/Images/appbar.message.png"></Image>
            </DataTemplate>
        </cc:TabItem.HeaderTemplate>
    </cc:TabItem>
    <cc:TabItem Style="{StaticResource MetroTabItem}" Height="80" Width="89" Header="Tab 4">
        <cc:TabItem.HeaderTemplate>
            <DataTemplate>
                <Image Source="/Images/appbar.repeat.png"></Image>
            </DataTemplate>
        </cc:TabItem.HeaderTemplate>
    </cc:TabItem>
    <cc:TabItem Style="{StaticResource MetroTabItem}" Height="80" Width="89" Header="Tab 5">
        <cc:TabItem.HeaderTemplate>
            <DataTemplate>
                <Image Source="/Images/appbar.magnify.png"></Image>
            </DataTemplate>
        </cc:TabItem.HeaderTemplate>
    </cc:TabItem>
</cc:TabControl>

Вот что вы в конечном итоге получите:

Приложение, наконец, приобретает нужную форму. Пришло время разработать собственный TextBox, который будет использоваться для публикации обновления пользователя.

Шаг 3: Подготовьте элемент управления вводом «обновление состояния»

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

Я использую обычный элемент управления TextBox с прикрепленным к нему пользовательским стилем.

<TextBox MaxLength="140" TextWrapping="Wrap" Height="140" Style="{StaticResource StatusTextBox}"></TextBox>

Счетчик символов отсутствует, поэтому может показаться, что пользователь может невольно превысить ограничение в 140 символов на твит. Чтобы избежать этого, я вручную устанавливаю свойство MaxLength для TextBox .

Стиль определяется так:

<Style x:Key="StatusTextBox" TargetType="TextBox">
<Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilyNormal}"/>
<Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMediumLarge}"/>
<Setter Property="Background" Value="{StaticResource PhoneTextBoxBrush}"/>
<Setter Property="Foreground" Value="{StaticResource PhoneTextBoxForegroundBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource PhoneTextBoxBrush}"/>
<Setter Property="SelectionBackground" Value="{StaticResource PhoneAccentBrush}"/>
<Setter Property="SelectionForeground" Value="{StaticResource PhoneTextBoxSelectionForegroundBrush}"/>
<Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Grid Background="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver"/>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="EnabledBorder">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="DisabledOrReadonlyBorder">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="ReadOnly">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="EnabledBorder">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="DisabledOrReadonlyBorder">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="DisabledOrReadonlyBorder">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneTextBoxBrush}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="DisabledOrReadonlyBorder">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneTextBoxBrush}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="DisabledOrReadonlyContent">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneTextBoxReadOnlyBrush}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="EnabledBorder">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneTextBoxEditBackgroundBrush}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="EnabledBorder">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneTextBoxEditBorderBrush}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="EnabledBorder" Background="{TemplateBinding Background}" Margin="{StaticResource PhoneTouchTargetOverhang}">
<Grid>
<ContentControl x:Name="ContentElement" BorderThickness="0" HorizontalContentAlignment="Stretch" Margin="8,2,68,2" Padding="{TemplateBinding Padding}" VerticalContentAlignment="Stretch">
</ContentControl>
<Button x:Name="StatusImage" Opacity=".6" Height="64" HorizontalAlignment="Right" VerticalAlignment="Top" Padding="0" BorderThickness="0">
<Image Height="64" Source="Images/appbar.right.png" Stretch="Fill" Width="64" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Button>
<Button x:Name="PhotoImage" Opacity=".6" Height="64" HorizontalAlignment="Right" VerticalAlignment="Bottom" Padding="0" BorderThickness="0">
<Image Height="64" Source="Images/appbar.camera.png" Stretch="Fill" Width="64" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Button>
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Обратите внимание, что в конце у меня объявлены два экземпляра Button , оба с изображениями внутри. Эти кнопки не имеют границ и подсвечиваются при нажатии. 

Ваш пользовательский интерфейс теперь должен выглядеть примерно так:

Поздравляем! Теперь у вас есть пользовательский интерфейс, обычно готовый для основного рабочего экрана.