Расширяем HyperlinkButton

Posted on Декабрь 28, 2010

7


Добрый день всем!
При написании приложения на Windows Phone 7 я столкнулся с HyperlinkButton и оказалось что этот контрол весьма ограничен в своих функциях. Я привык, что когда нажимаешь на почтовый адрес, открывается почтовый клиент в котором уже вбит адрес и необходимо только написать письмо и отправить. Стандартный функционал не позволяет это сделать, потому сегодня мы будем создавать слегка модифицированный контрол, который сможет отправлять письма и выполнять еще некоторые Задачи.
Для начала, необходимо подумать, куда мы будем записывать адрес (или что-то еще). Как мне кажется, свойство Tag подходит нам наилучшим образом. Теперь немного стилизуем наш линк:
<Style
    x:Key="AccentHyperlink"
    TargetType="HyperlinkButton">
    <Setter
        Property="Foreground"
        Value="{StaticResource PhoneAccentBrush}" />
    <Setter
        Property="Background"
        Value="Transparent" />
    <Setter
        Property="FontSize"
        Value="{StaticResource PhoneFontSizeMedium}" />
    <Setter
        Property="FontFamily"
        Value="{StaticResource PhoneFontFamilySemiBold}" />
    <Setter
        Property="Padding"
        Value="0" />
    <Setter
        Property="Template">
        <Setter.Value>
            <ControlTemplate
                TargetType="HyperlinkButton">
                <Border
                    Background="{TemplateBinding Background}"
                    Margin="{StaticResource PhoneHorizontalMargin}"
                    Padding="{TemplateBinding Padding}">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup
                            x:Name="CommonStates">
                            <VisualState
                                x:Name="Normal" />
                            <VisualState
                                x:Name="MouseOver" />
                            <VisualState
                                x:Name="Pressed">
                                <Storyboard>
                                    <DoubleAnimation
                                        Duration="0"
                                        Storyboard.TargetName="TextElement"
                                        Storyboard.TargetProperty="Opacity"
                                        To="0.5" />
                                </Storyboard>
                            </VisualState>
                            <VisualState
                                x:Name="Disabled">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames
                                        Storyboard.TargetName="TextElement"
                                        Storyboard.TargetProperty="Foreground">
                                        <DiscreteObjectKeyFrame
                                            KeyTime="0"
                                            Value="{StaticResource PhoneDisabledBrush}" />
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <TextBlock
                        x:Name="TextElement"
                        Text="{TemplateBinding Content}"
                        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                        VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
Этот стиль можно использовать для всех HyperlinkButton. Со стилями разобрались, теперь перейдем к расширению функционала. Для начала создадим класс, в котором будет наш контрол.
Класс ExtendedHyperlinkButton.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Windows.Controls;
using Microsoft.Phone.Tasks;

namespace ExtendedHyperlinkButton
{
	/// <summary>
	/// Расширенный контрол HyperlinkButton, который использует "Tag" для
	/// открытия браузера, написания письма, смс или совершения звонка.
	/// </summary>
	public class ExtendedHyperlinkButton : HyperlinkButton
	{
		/// <summary>
		/// Обработка нажатия
		/// </summary>
		protected override void OnClick()
		{
			base.OnClick();

			Debug.Assert(Tag is string, "Необходимо задать Tag!");
			var tag = Tag as string ?? Tag.ToString();
			IDictionary<string, string> d;
			if (tag.StartsWith("mailto:"))
			{
				Email(tag.Substring(7));
			}
			else
			{
				var wbt = new WebBrowserTask
										{
											URL = (string)Tag,
										};
				wbt.Show();
			}
		}

		private static void Email(string s)
		{
			IDictionary<string, string> d;
			var to = GetAddress(s, out d);

			var ect = new EmailComposeTask
			{
				To = to,
			};

			string cc;
			if (d.TryGetValue("cc", out cc))
			{
				ect.Cc = cc;
			}

			string subject;
			if (d.TryGetValue("subject", out subject))
			{
				ect.Subject = subject;
			}

			string body;
			if (d.TryGetValue("body", out body))
			{
				ect.Body = body;
			}

			ect.Show();
		}

		private static string GetAddress(string input, out IDictionary<string, string> query)
		{
			query = new Dictionary<string, string>(StringComparer.Ordinal);
			int q = input.IndexOf('?');
			string address = input;
			if (q >= 0)
			{
				address = input.Substring(0, q);
				ParseQueryStringToDictionary(input.Substring(q + 1), query);
			}
			return address;
		}

		private static void ParseQueryStringToDictionary(string queryString, IDictionary<string, string> dictionary)
		{
			foreach (string str in queryString.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
			{
				int index = str.IndexOf("=", StringComparison.Ordinal);
				if (index == -1)
				{
					dictionary.Add(HttpUtility.UrlDecode(str), string.Empty);
				}
				else
				{
					dictionary.Add(HttpUtility.UrlDecode(str.Substring(0, index)), HttpUtility.UrlDecode(str.Substring(index + 1)));
				}
			}
		}
	}
}
Единственный минус: вы не сможете проверить работу на эмуляторе. Вот как выглядит код ссылки, отправляющей письмо.

<extended:ExtendedHyperlinkButton Tag="mailto:spam@spam.com?subject=Hello%20World|cc=spammer@spam.com|body=Hey%20spam."
Content="Mail"></extended:ExtendedHyperlinkButton>

Теперь добавим отправку сообщения и звонок. Модифицируем функцию нажатия в  ExtendedHyperlinkButton.cs:

		/// <summary>
		/// Обработка нажатия
		/// </summary>
		protected override void OnClick()
		{
			base.OnClick();

			Debug.Assert(Tag is string, "Необходимо задать Tag!");
			var tag = Tag as string ?? Tag.ToString();
			IDictionary<string, string> d;
			if (tag.StartsWith("mailto:"))
			{
				Email(tag.Substring(7));
			}
			else if (tag.StartsWith("tel:"))
			{
				PhoneCallTask pct = new PhoneCallTask();
				pct.PhoneNumber = GetAddress(tag.Substring(4), out d);

				string name;
				if (d.TryGetValue("displayname", out name))
				{
					pct.DisplayName = name;
				}

				pct.Show();
			}
			else if (tag.StartsWith("sms:"))
			{
				SmsComposeTask sct = new SmsComposeTask();
				sct.To = GetAddress(tag.Substring(4), out d);

				string body;
				if (d.TryGetValue("body", out body))
				{
					sct.Body = body;
				}

				sct.Show();
			}
			else
			{
				var wbt = new WebBrowserTask
				{
					URL = (string)Tag,
				};
				wbt.Show();
			}
		}

Теперь мы сможем совершать звонки и отправлять сообщения. Даже с несколькими модификациями:

                <extended:ExtendedHyperlinkButton Style="{StaticResource AccentHyperlink}"
                    Tag="sms:+380681234567"
                    Content="Сообщение другу" />
                <extended:ExtendedHyperlinkButton Style="{StaticResource AccentHyperlink}"
                    Tag="sms:+380681234567?body=Привет%20Друг!"
                    Content="Сообщение другу с текстом" />

                <extended:ExtendedHyperlinkButton Style="{StaticResource AccentHyperlink}"
                    Tag="tel:+380681234567"
                    Content="Позвонить по номеру"/>
                <extended:ExtendedHyperlinkButton Style="{StaticResource AccentHyperlink}"
                    Tag="tel:+380681234567?displayname=Какая-то%20компания%20"
                    Content="Позвонить по номеру (с именем)"/>

Вот так выглядят ссылки:

При нажатии на Сообщение другу с текстом:

Как видите, русский текст тоже нормально вставляется. И если нажать на «Позвонить по номеру (с именем)»:

Код того что я описал выше можно скачать тут.

Удачи всем с разработкой.

Реклама