По умолчанию, возможности показа всплывающих сообщений в Windows Store приложениях достаточно ограниченны. Открыв MSDN, мы увидим, что существует всего 2 перегрузки. Про него можно почитать здесь. Причем, среди этих перегрузок нет ни одной, которая позволяет показать хотя бы стандартную картинку. Не говоря уж о том, чтобы при удалении картинки, задавая вопрос, точно ли удаляем эту картинку, показать ее уменьшенное изображение.
Как сделать свой компонент для показа всплывающего сообщения, причем с возможностью указать картинку, мы и поговорим подкатом.
Еще раз определимся с заданием. Нам необходимо показывать всплывающее окно, которое будет содержать текст, изображение и несколько кнопок. Причем, количество кнопок может меняться. При клике пользователем на любую из кнопок. Должно происходить закрытие окна и вызов метода, который ассоциирован с кнопкой. Начнем с универсального варианта, в котором мы будем передавать текст, изображение и массив имен кнопок и обработчиков кликов на них. Для передачи в метод информации о кнопке, мы воспользуемся вот таким классом:
public string Title { get; private set; }
if (string.IsNullOrEmpty(p_title))
{
throw new ArgumentException("Заголовок кнопки не может быть null или пустой строкой");
}
Title = p_title;
ButtonClick = p_buttonClickAction;
}
}
Для показа сообщения добавим в проект UserControl с разметкой вида:
<UserControl
x:Class="PopupMessage.Message"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PopupMessage"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
Height="100"
HorizontalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="100" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="1*" MinWidth="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Rectangle Grid.RowSpan="4" Grid.ColumnSpan="4" Fill="White" Opacity="0.2" />
<Rectangle Grid.Row="1" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="4" Fill="Black" Stroke="White" StrokeThickness="4" Margin="5,-5,5,-5" />
<Image x:Name="imMessageImage" MaxHeight="100" Grid.Column="1" Grid.Row="1" />
<TextBlock TextWrapping="Wrap" x:Name="tbMessage" Grid.Column="2" Grid.Row="1" Margin="7" FlowDirection="LeftToRight" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="2" Grid.ColumnSpan="4" x:Name="spButtons" Margin="5" />
</Grid>
</UserControl>
Обратили внимание на два Rectangle? Второй, обводит рамкой все поле с текстом, картинкой и кнопками. А вот зачем первый, который занимает все пространство? Дело в том, что Popup, при помощи которого мы и будем показывать всплывающее окно, не блокирует компоненты которые из под него выступают. Т.е. вы показываете Popup, из под него торчат элементы управления, и пользователь имеет к ним доступ. Т.к. мы делаем аналог ShowMessage, то будем блокировать все что лежит под нашим Popup-ом посредством этого прямоугольника.
Код контрола, интересен ради свойств, которые отвечают за инициализацию:
public Message()
{
this.InitializeComponent();
}
set
{
imMessageImage.Source = value;
}
}
public string MessgaeText
{
set
{
tbMessage.Text = value;
}
}
set
{
spButtons.Children.Clear();
if (value != null)
{
foreach (var button in value)
{
spButtons.Children.Add(button);
}
}
}
}
}
Ну и теперь, к самому методу. Добавляем в проект класс с именем ShowPopupMessage, а в него метод, для показа всплывающего сообщения:
public static void Show(string p_message, BitmapImage p_image, params PopupButton[] p_buttons)
{
// Создаем Popup
Popup popup = new Popup();
// Создаем кнопки
List<Button> buttons = new List<Button>();
if (p_buttons != null)
{
foreach (var button in p_buttons)
{
Button createdButton = new Button() { Margin = new Thickness(5), Content = button.Title };
createdButton.Click += (s, e) => popup.IsOpen = false;
if (button.ButtonClick != null)
{
createdButton.Click += (s, e) => button.ButtonClick();
}
buttons.Add(createdButton);
}
}
// Инициализируем внешний вид:
Message msg = new Message();
msg.MessgaeText = p_message;
msg.Bitmap = p_image;
msg.Buttons = buttons;
msg.Width = Window.Current.Bounds.Width;
msg.Height = Window.Current.Bounds.Height;
// Помещаем наш UserControl в popup
popup.Child = msg;
// Показываем popup
popup.IsOpen = true;
// Если есть кнопки, устанавливаем фокус на первую из них
if (buttons.Count > 0)
{
buttons.First().Focus(FocusState.Programmatic);
}
}
}
Так как все действия прокоментированны в коде, то дополниьтельно останавливаться не буду.
Для демонстрации воспользуюсь приложением, в окне которого размещу вот такой XAML:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PopupTester"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel >
<Button Content="Показать сообщение без картинки, с одной кнопкой" Click="Button_Click_1" />
<TextBlock x:Name="tbResult" />
</StackPanel>
</Grid>
</Page>
Ну и обработчик клика на кнопку:
ShowPopupMessage.Show(
"Привет!",
new BitmapImage(new Uri("http://lh5.googleusercontent.com/-xqEMlkTQlNU/AAAAAAAAAAI/AAAAAAAAA38/-AC-DOM0IIA/s512-c/photo.jpg")),
new PopupButton("Ок!", () => tbResult.Text = "Нажали Ок"),
new PopupButton("Отмена", () => tbResult.Text = "Нажали отмена")
);
}
Запускаем приложение:
Кликаем на кнопку:
Закрываем окно кликом по Ок:
Конечно, предложенный компонент необходимо дорабатывать. Например, если не передали ни одной кнопки, добавлять кнопку Ок, с единственной функцией - закрыть приложение. Можно еще поиграться с дизайном (особенно со шрифтами) и т.д.
Как сделать свой компонент для показа всплывающего сообщения, причем с возможностью указать картинку, мы и поговорим подкатом.
Еще раз определимся с заданием. Нам необходимо показывать всплывающее окно, которое будет содержать текст, изображение и несколько кнопок. Причем, количество кнопок может меняться. При клике пользователем на любую из кнопок. Должно происходить закрытие окна и вызов метода, который ассоциирован с кнопкой. Начнем с универсального варианта, в котором мы будем передавать текст, изображение и массив имен кнопок и обработчиков кликов на них. Для передачи в метод информации о кнопке, мы воспользуемся вот таким классом:
public class PopupButton
{public string Title { get; private set; }
public Action ButtonClick { get; private set; }
public PopupButton(string
p_title, Action p_buttonClickAction)
{if (string.IsNullOrEmpty(p_title))
{
throw new ArgumentException("Заголовок кнопки не может быть null или пустой строкой");
}
Title = p_title;
ButtonClick = p_buttonClickAction;
}
}
Для показа сообщения добавим в проект UserControl с разметкой вида:
<UserControl
x:Class="PopupMessage.Message"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PopupMessage"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
Height="100"
HorizontalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="100" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="1*" MinWidth="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Rectangle Grid.RowSpan="4" Grid.ColumnSpan="4" Fill="White" Opacity="0.2" />
<Rectangle Grid.Row="1" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="4" Fill="Black" Stroke="White" StrokeThickness="4" Margin="5,-5,5,-5" />
<Image x:Name="imMessageImage" MaxHeight="100" Grid.Column="1" Grid.Row="1" />
<TextBlock TextWrapping="Wrap" x:Name="tbMessage" Grid.Column="2" Grid.Row="1" Margin="7" FlowDirection="LeftToRight" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="2" Grid.ColumnSpan="4" x:Name="spButtons" Margin="5" />
</Grid>
</UserControl>
Обратили внимание на два Rectangle? Второй, обводит рамкой все поле с текстом, картинкой и кнопками. А вот зачем первый, который занимает все пространство? Дело в том, что Popup, при помощи которого мы и будем показывать всплывающее окно, не блокирует компоненты которые из под него выступают. Т.е. вы показываете Popup, из под него торчат элементы управления, и пользователь имеет к ним доступ. Т.к. мы делаем аналог ShowMessage, то будем блокировать все что лежит под нашим Popup-ом посредством этого прямоугольника.
Код контрола, интересен ради свойств, которые отвечают за инициализацию:
public sealed partial class Message : UserControl
{public Message()
{
this.InitializeComponent();
}
public BitmapImage Bitmap
{set
{
imMessageImage.Source = value;
}
}
public string MessgaeText
{
set
{
tbMessage.Text = value;
}
}
public IEnumerable<Button> Buttons
{set
{
spButtons.Children.Clear();
if (value != null)
{
foreach (var button in value)
{
spButtons.Children.Add(button);
}
}
}
}
}
Ну и теперь, к самому методу. Добавляем в проект класс с именем ShowPopupMessage, а в него метод, для показа всплывающего сообщения:
public class ShowPopupMessage
{public static void Show(string p_message, BitmapImage p_image, params PopupButton[] p_buttons)
{
// Создаем Popup
Popup popup = new Popup();
// Создаем кнопки
List<Button> buttons = new List<Button>();
if (p_buttons != null)
{
foreach (var button in p_buttons)
{
Button createdButton = new Button() { Margin = new Thickness(5), Content = button.Title };
createdButton.Click += (s, e) => popup.IsOpen = false;
if (button.ButtonClick != null)
{
createdButton.Click += (s, e) => button.ButtonClick();
}
buttons.Add(createdButton);
}
}
// Инициализируем внешний вид:
Message msg = new Message();
msg.MessgaeText = p_message;
msg.Bitmap = p_image;
msg.Buttons = buttons;
msg.Width = Window.Current.Bounds.Width;
msg.Height = Window.Current.Bounds.Height;
// Помещаем наш UserControl в popup
popup.Child = msg;
// Показываем popup
popup.IsOpen = true;
// Если есть кнопки, устанавливаем фокус на первую из них
if (buttons.Count > 0)
{
buttons.First().Focus(FocusState.Programmatic);
}
}
}
Так как все действия прокоментированны в коде, то дополниьтельно останавливаться не буду.
Для демонстрации воспользуюсь приложением, в окне которого размещу вот такой XAML:
<Page
x:Class="PopupTester.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PopupTester"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel >
<Button Content="Показать сообщение без картинки, с одной кнопкой" Click="Button_Click_1" />
<TextBlock x:Name="tbResult" />
</StackPanel>
</Grid>
</Page>
Ну и обработчик клика на кнопку:
private void
Button_Click_1(object sender, RoutedEventArgs e)
{ShowPopupMessage.Show(
"Привет!",
new BitmapImage(new Uri("http://lh5.googleusercontent.com/-xqEMlkTQlNU/AAAAAAAAAAI/AAAAAAAAA38/-AC-DOM0IIA/s512-c/photo.jpg")),
new PopupButton("Ок!", () => tbResult.Text = "Нажали Ок"),
new PopupButton("Отмена", () => tbResult.Text = "Нажали отмена")
);
}
Запускаем приложение:
Кликаем на кнопку:
Закрываем окно кликом по Ок:
Конечно, предложенный компонент необходимо дорабатывать. Например, если не передали ни одной кнопки, добавлять кнопку Ок, с единственной функцией - закрыть приложение. Можно еще поиграться с дизайном (особенно со шрифтами) и т.д.
Спасибо, у вас получилось все это гораздо лучше чем у меня ;)
ОтветитьУдалитьНе за что. Мы просто для SilverLight уже делали всплывающее окно на основе Popup-а, все что мне оставалось, это просто идеи от туда перенести на ограничения Windows Store приложений.
УдалитьСегодня не обещаю, но в течении недели постараюсь привести пример, как на основе этого универсального метода сделать несколько специфических...