2010-06-14 10 views
58

Ich verwende .NET und erstelle eine Desktop-App/einen Dienst, der Benachrichtigungen in der Ecke meines Desktops anzeigt, wenn bestimmte Ereignisse ausgelöst werden. Ich möchte keine normale Message Box b/c verwenden, die zu aufdringlich wäre. Ich möchte, dass Benachrichtigungen sichtbar werden und nach einigen Sekunden ausgeblendet werden. Ich denke an etwas, das den Outlook-Alarmen sehr ähnlich ist, die man erhält, wenn eine neue Nachricht eintrifft. Die Frage ist: Soll ich WPF dafür verwenden? Ich habe noch nie etwas mit WPF gemacht, werde es aber gerne ausprobieren, wenn das bis zum Ende am besten ist. Gibt es eine Möglichkeit, dies mit regulären .NET-Bibliotheken zu erreichen?Erstellen Sie Popup- "Toaster" -Benachrichtigungen in Windows mit .NET

+1

Ich denke, dass irgendwie ist es möglich, mit regelmäßigen .Net, verwaltet Infragistics, es zu tun: http://www.infragistics.com/dotnet/netadvantage/winforms/windesktopalert.aspx#Overview –

+1

Yup, genau das meinte ich. Leute, bitte teile deine Gedanken. Ich suche nur nach einer allgemeinen Richtung, nicht nach einer detaillierten Anleitung. Danke. – Antony

+0

hier ist eine, die für eine Weile auf CodeProject gewesen ist. http://www.codeproject.com/KB/misctrl/taskbarnotifier.aspx dauert weniger als 5 Tage. – quimbo

Antwort

102

WPF macht dies absolut trivial: Es würde wahrscheinlich zehn Minuten oder weniger dauern. Hier sind die Schritte:

  1. ein Fenster zu erstellen, setzen AllowsTransparency = „true“ und ein Gitter, um es hinzuzufügen
  2. den Render der Grid Set zu einer Scaletransform mit Ursprung von 0,1
  3. eine Animation erstellen auf Das Raster, das ScaleX animiert 0 zu 1, animiert später die Opazität von 1 bis 0
  4. Im Konstruktor berechnen Window.Top und Window.Left, um das Fenster in der unteren rechten Ecke des Bildschirms zu platzieren.

Das ist alles, was es ist.

Mit Expression Blend es ca. 8 Minuten dauerte den folgenden Arbeits-Code zu generieren:

<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> 

Mit Code hinter:

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; 
    })); 
    } 
} 

Da WPF ist eine der regulären .NET-Bibliotheken, die Antwort ist ja, es ist möglich, dies mit den "regulären .NET-Bibliotheken" zu erreichen.

Wenn Sie fragen, ob es einen Weg gibt, dies ohne WPF zu tun, ist die Antwort immer noch ja, aber es ist extrem komplex und dauert mehr als 5 Tage als 5 Minuten.

+0

Das ist großartig. Klappt wunderbar. Vielen Dank! Beeindruckend, wie wenig Code es brauchte. – Antony

+0

+ Cool, du hast mir gerade auch Zeit gespart! Danke) –

+17

Nicht sicher, ob dies erforderlich ist, aber ich fügte ein Completed-Ereignis zur Opazitätsanimation hinzu, und im Code dahinter fügte ich "this.Close();" hinzu. Sonst würde das Fenster immer offen bleiben. Kann eine App, die aufgrund des letzten Schließens geschlossen wird, durcheinander bringen. – Paul

0

Beachten Sie, dass der aufrufende Thread sta werden müssen, da viele Komponenten ui dies erfordern, während Schreiben unter System.Timers.Timer folgenden Code abgelaufen Ereignis

Window1 notifyWin = new Window1(); 
bool? isOpen = notifyWin.ShowDialog(); 
if (isOpen != null && isOpen == true) 
{ 
    notifyWin.Close(); 
} 
System.Threading.Thread.Sleep(1000); 
notifyWin.ShowDialog(); 

unter window1 Konstruktor:

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; 
    })); 
} 
5
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(); 
    } 

} 

Der obige Code ist verfeinerte Version @Ray Burns Ansatz. Hinzugefügt mit Zeitintervallcode. So dass Benachrichtigungsfenster würde nach 4 Sekunden schließen ..

Rufen Sie das Fenster wie

NotificationWindow nfw = new NotificationWindow(); 
nfw.Show(); 
+0

Aaaaaaaaaaan. . – Gopichandar

+0

@Gopichandar Ich konnte Gopi nicht verstehen –

16

Ich ging weiter und schuf ein CodePlex-Website für diese, die „Toast Popups“ und Kontrolle „Help Balloons“ enthält . Diese Versionen haben mehr Funktionen als die unten beschriebenen. https://toastspopuphelpballoon.codeplex.com.

Dies war ein großartiger Ausgangspunkt für die Lösung, die ich suchte.Ich habe ein paar Änderungen vorgenommen, um meine Anforderungen zu erfüllen:

  • Ich wollte die Animation auf Maus über stoppen.
  • "Reset" Animation beim Verlassen der Maus.
  • Schließen Sie das Fenster, wenn Opazität erreicht 0.
  • Stapel der Toast (ich habe das Problem, wenn die Anzahl der Fenster nicht gelöst, um die Bildschirmhöhe überschreitet)
  • Anruf Laden von meinem Viewmodel

meine hier

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> 

den Code hinter

012.351.
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(); 
     } 
    } 
} 

Der Aufruf aus dem Viewmodel:

 <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> 

UPDATE:

private void ShowNotificationExecute() 
    { 
     App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(
      () => 
      { 
       var notify = new NotificationWindow(); 
       notify.Show(); 
      })); 
    } 

Die in der XAML referenzierten Styles ich diesen Event-Handler hinzugefügt, wenn das Formular „Drop geschlossen ist "Die anderen Fenster.

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; 
       } 
      } 
     } 
    } 
+1

Wie funktioniert das NotificationWindowClosed? Es ist nirgendwo sonst im Code referenziert – HoKy22

+0

Sorry darüber. Ich habe den fehlenden Code hinzugefügt. Das war ein work in progress, und das habe ich im letzten Update verpasst. – LawMan

+0

Funktioniert perfekt! Kannst du etwas mehr über die Bedeutung von "Ich habe diesen Event-Handler hinzugefügt, als das Formular geschlossen wurde, um die anderen Fenster zu löschen". Was meinst du mit "fallen lassen"? – HoKy22

2
NotifyIcon notifyIcon = new NotifyIcon(); 
Stream iconStream = System.Windows.Application.GetResourceStream(new Uri("pack://application:,,,/Assets/ic_instant_note_tray.ico")).Stream; 
notifyIcon.Icon = new System.Drawing.Icon(iconStream); 
notifyIcon.Text = string.Format(Properties.Resources.InstantNoteAppName, Constants.Application_Name); 
notifyIcon.Visible = true; 
notifyIcon.ShowBalloonTip(5000, "tooltiptitle", "tipMessage", ToolTipIcon.Info); 
notifyIcon.Visible = false; 
notifyIcon.Dispose(); 
Verwandte Themen