Je suis à l'aide .NET et suis entrain de créer une application de bureau/service qui va afficher des notifications dans le coin de mon Bureau lorsque certains événements sont déclenchés. Je ne veux pas utiliser un Message de la Boîte b/c qui pourrait être trop intrusif. Je veux des notifications à glisser dans la vue et fade out après quelques secondes. Je pense à quelque chose qui va agir comme Outlook alertes que l'on obtient lors de l'arrivée d'un nouveau message. La question est: dois-je utiliser WPF pour cela? Je n'ai jamais rien fait avec WPF, mais sera heureux de l'essayer si c'est le meilleur moyen pour la fin. Est-il un moyen pour accomplir cette réguliers .Bibliothèques NET?
Réponses
Trop de publicités?WPF rend absolument trivial: Il serait proably prendre dix minutes ou moins. Voici les étapes:
- Créer une Fenêtre, définissez AllowsTransparency="true" et ajouter une Grille à elle
- Définir la Grille de l'RenderTransform à un ScaleTransform à l'origine de 0,1
- Créer une animation sur la grille qui anime le ScaleX 0 à 1 puis plus tard anime l'Opacité de 1 à 0
- Dans le constructeur de calculer Fenêtre.Supérieur et de la Fenêtre.Gauche pour placer la fenêtre dans le coin inférieur droit de l'écran.
C'est tout là est à lui.
À l'aide de Expression Blend il a fallu environ 8 minutes à moi pour générer suivants du code qui fonctionne:
<Window
x:Class="NotificationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Notification Popup" Width="300" SizeToContent="Height"
WindowStyle="None" AllowsTransparency="True" Background="Transparent">
<Grid RenderTransformOrigin="0,1" >
<!-- Notification area -->
<Border BorderThickness="1" Background="Beige" BorderBrush="Black" CornerRadius="10">
<StackPanel Margin="20">
<TextBlock TextWrapping="Wrap" Margin="5">
<Bold>Notification data</Bold><LineBreak /><LineBreak />
Something just happened and you are being notified of it.
</TextBlock>
<CheckBox Content="Checkable" Margin="5 5 0 5" />
<Button Content="Clickable" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<!-- Animation -->
<Grid.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:2" Value="1"/>
<SplineDoubleKeyFrame KeyTime="0:0:4" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Grid.RenderTransform>
<ScaleTransform ScaleY="1" />
</Grid.RenderTransform>
</Grid>
</Window>
Avec le code derrière:
using System;
using System.Windows;
using System.Windows.Threading;
public partial class NotificationWindow
{
public NotificationWindow()
{
InitializeComponent();
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
{
var workingArea = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
var transform = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;
var corner = transform.Transform(new Point(workingArea.Right, workingArea.Bottom));
this.Left = corner.X - this.ActualWidth - 100;
this.Top = corner.Y - this.ActualHeight;
}));
}
}
Depuis WPF est l'un des réguliers .Bibliothèques NET, la réponse est oui, il est possible de réaliser cela avec la "régulière .Bibliothèques NET".
Si vous vous demandez si il y a un moyen de le faire sans l'aide de WPF, la réponse est toujours oui, mais c'est extrêmement complexe, et prendre plus que 5 jours plus de 5 minutes.
Je suis allé de l'avant et a créé un site de CodePlex pour ce que comprend "Toast Popups" et de "contrôle à l'Aide de Ballons". Ces versions ont plus de fonctionnalités que ce qui est décrit ci-dessous. https://toastspopuphelpballoon.codeplex.com.
Ce fut un excellent point de départ pour la solution que je cherchais. J'ai fait quelques modifications pour répondre à mes exigences:
- J'ai voulu arrêter l'animation au survol de la souris.
- "Reset" de l'animation lorsque la souris congé.
- Fermer la Fenêtre lorsque l'opacité à 0.
- Pile le Toast (je n'ai pas résolu le problème si le nombre de fenêtres dépasse la hauteur de l'écran)
- Appel de la Charge de mon ViewModel
Voici mon code XAML
<Window x:Class="Foundation.FundRaising.DataRequest.Windows.NotificationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NotificationWindow" Height="70" Width="300" ShowInTaskbar="False"
WindowStyle="None" AllowsTransparency="True"
Background="Transparent">
<Grid RenderTransformOrigin="0,1" >
<Border BorderThickness="2" Background="{StaticResource GradientBackground}" BorderBrush="DarkGray" CornerRadius="7">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="24"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Image Grid.Column="0"
Grid.RowSpan="2"
Source="Resources/data_information.png"
Width="40" Height="40"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
<Image Grid.Column="2"
Source="Resources/error20.png"
Width="20"
Height="20"
VerticalAlignment="Center"
ToolTip="Close"
HorizontalAlignment="Center"
Cursor="Hand" MouseUp="ImageMouseUp"/>
<TextBlock Grid.Column="1"
Grid.Row="0"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontWeight="Bold" FontSize="15"
Text="A Request has been Added"/>
<Button Grid.Column="1"
Grid.Row="1"
FontSize="15"
Margin="0,-3,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="Click Here to View"
Style="{StaticResource LinkButton}"/>
</Grid>
</Border>
<!-- Animation -->
<Grid.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard x:Name="StoryboardLoad">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(UIElement.Opacity)" From="0.0" To="1.0" Duration="0:0:2" />
<DoubleAnimation Storyboard.TargetProperty="(UIElement.Opacity)" From="1.0" To="0.0" Duration="0:0:8" BeginTime="0:0:5" Completed="DoubleAnimationCompleted"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<RemoveStoryboard BeginStoryboardName="StoryboardLoad"/>
<RemoveStoryboard BeginStoryboardName="StoryboardFade"/>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<BeginStoryboard x:Name="StoryboardFade">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(UIElement.Opacity)" From="1.0" To="0.0" Duration="0:0:8" BeginTime="0:0:2" Completed="DoubleAnimationCompleted"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Grid.RenderTransform>
<ScaleTransform ScaleY="1" />
</Grid.RenderTransform>
</Grid>
Le Code-Behind
public partial class NotificationWindow : Window
{
public NotificationWindow()
: base()
{
this.InitializeComponent();
this.Closed += this.NotificationWindowClosed;
}
public new void Show()
{
this.Topmost = true;
base.Show();
this.Owner = System.Windows.Application.Current.MainWindow;
this.Closed += this.NotificationWindowClosed;
var workingArea = Screen.PrimaryScreen.WorkingArea;
this.Left = workingArea.Right - this.ActualWidth;
double top = workingArea.Bottom - this.ActualHeight;
foreach (Window window in System.Windows.Application.Current.Windows)
{
string windowName = window.GetType().Name;
if (windowName.Equals("NotificationWindow") && window != this)
{
window.Topmost = true;
top = window.Top - window.ActualHeight;
}
}
this.Top = top;
}
private void ImageMouseUp(object sender,
System.Windows.Input.MouseButtonEventArgs e)
{
this.Close();
}
private void DoubleAnimationCompleted(object sender, EventArgs e)
{
if (!this.IsMouseOver)
{
this.Close();
}
}
}
L'appel à partir de ce Dernier:
private void ShowNotificationExecute()
{
App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(
() =>
{
var notify = new NotificationWindow();
notify.Show();
}));
}
Les Styles référencés dans le code XAML:
<Style x:Key="LinkButton" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock>
<ContentPresenter />
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="Blue"/>
<Setter Property="Cursor" Value="Hand"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextDecorations="Underline" Text="{TemplateBinding Content}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<LinearGradientBrush x:Key="GradientBackground" EndPoint="0.504,1.5" StartPoint="0.504,0.03">
<GradientStop Color="#FFFDD5A7" Offset="0"/>
<GradientStop Color="#FFFCE79F" Offset="0.567"/>
</LinearGradientBrush>
Mise à JOUR: j'ai ajouté ce gestionnaire d'événement lorsque le formulaire est fermé à la "chute" de l'autre windows.
private void NotificationWindowClosed(object sender, EventArgs e)
{
foreach (Window window in System.Windows.Application.Current.Windows)
{
string windowName = window.GetType().Name;
if (windowName.Equals("NotificationWindow") && window != this)
{
// Adjust any windows that were above this one to drop down
if (window.Top < this.Top)
{
window.Top = window.Top + this.ActualHeight;
}
}
}
}
public partial class NotificationWindow : Window
{
DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
public NotificationWindow()
: base()
{
this.InitializeComponent();
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
{
var workingArea = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
var transform = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;
var corner = transform.Transform(new Point(workingArea.Right, workingArea.Bottom));
this.Left = corner.X - this.ActualWidth;
this.Top = corner.Y - this.ActualHeight;
}));
timer.Interval = TimeSpan.FromSeconds(4d);
timer.Tick += new EventHandler(timer_Tick);
}
public new void Show()
{
base.Show();
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
//set default result if necessary
timer.Stop();
this.Close();
}
}
Le code ci-dessus est la version raffinée @Ray Brûlures approche. Ajoutés avec le temps de l'intervalle de code. De sorte que la fenêtre de Notification de fermer au bout de 4 secondes..
Appeler la Fenêtre,
NotificationWindow nfw = new NotificationWindow();
nfw.Show();
Trouvé exception: le thread appelant doit être sta car de nombreux composants de l'interface utilisateur, l'exigent. lors de l'écriture de code suivant dans le système.les minuteries.minuterie événement écoulé
Window1 notifyWin = new Window1();
bool? isOpen = notifyWin.ShowDialog();
if (isOpen != null && isOpen == true)
{
notifyWin.Close();
}
System.Threading.Thread.Sleep(1000);
notifyWin.ShowDialog();
en vertu de window1 constructeur:
public Window1()
{
InitializeComponent();
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() => {
var workingArea = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
var transform = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;
var corner = transform.Transform(new Point(workingArea.Right, workingArea.Bottom));
this.Left = corner.X - this.ActualWidth - 100;
this.Top = corner.Y - this.ActualHeight;
}));
}