2017-01-08 7 views
0

Ich erstellte ein benutzerdefiniertes Steuerelement "Toast Notification" in WPF und ich möchte es in MVVM verwenden, um Nachrichten anzuzeigen, aber ich weiß nicht, wo implementieren (MainWindow oder jede Seite) und wie senden Nachrichten von jeder Seite, um auf Benachrichtigung zu stossen. Bitte helfen Sie mir, dieses Problem zu beheben.Toastbenachrichtigung in WPF verwenden MVVM

my toast notification control

public class Toast:Control 
{ 
    static Toast() 
    { 
     DefaultStyleKeyProperty.OverrideMetadata (typeof (Toast),new FrameworkPropertyMetadata (typeof (Toast))); 
    } 

    public override void OnApplyTemplate() 
    { 
     base.OnApplyTemplate(); 
     ChangeVisualState(); 
    } 

    public static readonly DependencyProperty TextProperty= 
     DependencyProperty.Register ("Text",typeof (string),typeof (Toast),new PropertyMetadata ("Sample Text")); 

    public static readonly DependencyProperty ToastIconProperty= 
     DependencyProperty.Register ("ToastIcon",typeof (ToastIconType),typeof (Toast),new PropertyMetadata (ToastIconType.None,OnToastIconChanged)); 

    public static readonly DependencyProperty IsToastVisibleProperty= 
     DependencyProperty.Register ("IsToastVisible",typeof (bool),typeof (Toast),new PropertyMetadata (false,OnIsToastVisibleChanged)); 

    public static readonly DependencyProperty DurationProperty= 
     DependencyProperty.Register ("Duration",typeof (TimeSpan),typeof (Toast),new PropertyMetadata (TimeSpan.FromSeconds (10),OnDurationChanged)); 

    public static readonly DependencyProperty ImageGeometryProperty= 
     DependencyProperty.Register ("ImageGeometry",typeof (Geometry),typeof (Toast)); 

    public string Text 
    { 
     get { return (string)GetValue (TextProperty); } 
     set { SetValue (TextProperty,value); } 
    } 

    public bool IsToastVisible 
    { 
     get { return (bool)GetValue (IsToastVisibleProperty); } 
     set { SetValue (IsToastVisibleProperty,value); ChangeVisualState(); } 
    } 

    public ToastIconType ToastIcon 
    { 
     get { return (ToastIconType)GetValue (ToastIconProperty); } 
     set { SetValue (ToastIconProperty,value); } 
    } 

    public TimeSpan Duration 
    { 
     get { return (TimeSpan)GetValue (DurationProperty); } 
     set { SetValue (DurationProperty,value); } 
    } 

    public Geometry ImageGeometry 
    { 
     get { return (Geometry)GetValue (ImageGeometryProperty); } 
     set { SetValue (ImageGeometryProperty,value); } 
    } 

    private static void OnDurationChanged(DependencyObject d,DependencyPropertyChangedEventArgs e) 
    { 
     var control=(Toast)d; 
     var value=(TimeSpan)e.NewValue; 
     control.Duration=value; 
    } 

    private static void OnIsToastVisibleChanged(DependencyObject d,DependencyPropertyChangedEventArgs e) 
    { 
     var c=(Toast)d; 
     var value=(bool)e.NewValue; 
     c.IsToastVisible=value; 
    } 

    private void ChangeVisualState() 
    { 
     if (IsToastVisible) 
     { 
      DoubleAnimation da=new DoubleAnimation { From=1,To=0,Duration=TimeSpan.FromSeconds (Duration.Seconds) }; 

      CubicEase cubicEase=new CubicEase(); 
      cubicEase.EasingMode=EasingMode.EaseInOut; 

      da.EasingFunction=cubicEase; 

      da.Completed+=(sender,e) => IsToastVisible=false; 
      BeginAnimation (OpacityProperty,da); 
     } 
     else 
     { 
      Opacity=0; 
     } 
    } 

    private static void OnToastIconChanged(DependencyObject d,DependencyPropertyChangedEventArgs e) 
    { 
     var control=(Toast)d; 
     var value=(ToastIconType)e.NewValue; 

     switch (value) 
     { 
      case ToastIconType.Information: 
       control.ImageGeometry=Geometry.Parse ("M3.6069946,1.8659973C2.6459963,1.8659973,1.8660278,2.6480103,1.8660278,3.6080017L1.8660278,20.546021C1.8660278,21.507019,2.6459963,22.288025,3.6069946,22.288025L11.647035,22.288025 9.6170056,27.481018 18.038026,22.288025 28.124027,22.288025C29.085025,22.288025,29.865054,21.507019,29.865054,20.546021L29.865054,3.6080017C29.865054,2.6480103,29.085025,1.8659973,28.124027,1.8659973z M3.6069946,0L28.124027,0C30.11304,0,31.731998,1.6190186,31.731998,3.6080017L31.731998,20.546021C31.731998,22.536011,30.11304,24.154022,28.124027,24.154022L18.567018,24.154022 5.8439948,32 8.9130261,24.154022 3.6069946,24.154022C1.618042,24.154022,-2.120687E-07,22.536011,0,20.546021L0,3.6080017C-2.120687E-07,1.6190186,1.618042,0,3.6069946,0z"); 
       break; 

      case ToastIconType.Warning: 
       control.ImageGeometry=Geometry.Parse ("M13.950004,24.5L13.950004,28.299988 17.950004,28.299988 17.950004,24.5z M13.950004,10.399963L13.950004,21.699951 17.950004,21.699951 17.950004,10.399963z M15.950004,0C16.349998,0,16.750007,0.19995117,16.950004,0.69995117L31.750011,30.099976C31.949993,30.5 31.949993,31 31.750011,31.399963 31.549999,31.799988 31.150005,32 30.750011,32L1.1499981,32C0.75000406,32 0.34999478,31.799988 0.14999761,31.399963 -0.049999204,31 -0.049999204,30.5 0.14999761,30.099976L14.950004,0.69995117C15.150001,0.19995117,15.549995,0,15.950004,0z"); 
       break; 
     } 
    } 

    public enum ToastIconType 
    { 
     None=0, 
     Information=1, 
     Warning=2 
    } 
} 

und mein Toast Stil:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:local="clr-namespace:MetroCustomControls"> 
    <Style TargetType="{x:Type local:Toast}"> 
     <Style.Resources> 
      <SolidColorBrush x:Key="Toast.Static.Background" Color="LightGray" /> 
      <SolidColorBrush x:Key="Toast.Static.BorderBrush" Color="Gray" /> 
      <SolidColorBrush x:Key="Toast.Static.Foreground" Color="DimGray" /> 
     </Style.Resources> 
     <Setter Property="Background" Value="{StaticResource Toast.Static.Background}" /> 
     <Setter Property="BorderBrush" Value="{StaticResource Toast.Static.BorderBrush}" /> 
     <Setter Property="BorderThickness" Value="1" /> 
     <Setter Property="Foreground" Value="{StaticResource Toast.Static.Foreground}" /> 
     <Setter Property="FontFamily" Value="Segoe UI Light" /> 
     <Setter Property="FontSize" Value="14" /> 
     <Setter Property="HorizontalAlignment" Value="Center" /> 
     <Setter Property="HorizontalContentAlignment" Value="Center" /> 
     <Setter Property="VerticalAlignment" Value="Bottom" /> 
     <Setter Property="VerticalContentAlignment" Value="Center" /> 
     <Setter Property="Margin" Value="0,0,0,10" /> 
     <Setter Property="Focusable" Value="False" /> 
     <Setter Property="SnapsToDevicePixels" Value="True" /> 
     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="{x:Type local:Toast}"> 
        <Grid x:Name="template_Root"> 
         <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" 
           CornerRadius="2" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}" Margin="0,0,0,20"> 
          <StackPanel Orientation="Horizontal"> 
           <Path x:Name="path" Margin="6,0,0,0" Fill="{TemplateBinding Foreground}" Stretch="Uniform" Width="12" Height="12" Data="{TemplateBinding ImageGeometry}" /> 
           <TextBlock Text="{TemplateBinding Text}" Foreground="{TemplateBinding Foreground}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" Padding="5" /> 
          </StackPanel> 
         </Border> 
        </Grid> 
        <ControlTemplate.Triggers> 
         <DataTrigger Binding="{Binding ToastIcon,RelativeSource={RelativeSource Self}}" Value="None"> 
          <Setter TargetName="path" Property="Visibility" Value="Collapsed" /> 
         </DataTrigger> 
        </ControlTemplate.Triggers> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 
</ResourceDictionary> 
+0

Wo verwende ich Toast-Steuerelement? im Hauptfenster oder verwenden Sie es auf jeder Seite? –

+0

Sie finden eine große Implementierung eines gemeinsamen Ansatzes (DialogService) unter https://www.codeproject.com/articles/36745/showing-dialogs-when-using-the-mvvm-pattern – Fruchtzwerg

+0

tnx Fruchtzwerg, ich benutze keine Dialoge Ich möchte, dass mein Toast-Steuerelement andere Steuerelemente überlagert und immer oben ist, wie Android Toast-Benachrichtigung. –

Antwort

2

Dies ist auf die Frage des Dialogs in MVVM ähnlich ist.

Die ideale Lösung besteht darin, die Funktionalität in eine DialogService oder in Ihrem Fall ToastService zu verpacken, die Sie in ViewModels einspeisen können, die dies erfordern.

So hat Ihr MainWindow oder root ViewModel die Kontrolle und die Fähigkeit, Toasts anzuzeigen. Dann haben Sie eine IToastService, die etwa wie folgt aussieht:

public class ToastService 
{ 
    public event Action<String> ToastMessageRecieved; 

    public void ShowToast(string message) 
    { 
     ToastMessageRecieved(message); 
    } 
} 

Der Verbraucher der ShowToast Aktion (die Sache, die den Toast tatsächlich ist angezeigt), sagen die RootViewModel können abonnieren Sie den ToastMessageRecieved Aktion:

public MyRootViewModel(ToastService toastService) 
{ 
    //keep it as a dependency in case we want to show toasts 
    this.toastService = toastService; 

    toastService.ToastMessageRecieved += (message) => 
    { 
     //here's where you actually show your toast, however that's done 
     //MyRootViewModel has the actual UI element reference. It only 
     //appears in this one place. 
     Toast.Message = message; 
    }; 
} 

und andere Viewmodels, die ein Toast verwenden Sie dann den Dienst angezeigt werden soll:

public MyRandomToastGeneratingViewModel(ToastService toastService) 
{ 
    //our service is inejcted as a dependency 
    this.toastService = toastService; 
} 

public void ShowAToastButtonPressed() 
{ 
    toastService.ShowToast("My great toast!"); 
} 

Im Idealfall werden Sie Irgendeine Art von Abhängigkeitsinjektion zu verwenden - aber es lohnt sich, für diese Art von Sache zu implementieren. Dialog-Dienste, Dinge wie Toasts, Fabriken und Datenzugriff passen gut zu MVVM und DI.

Um die eigentliche Kontrolle (die einzige Instanz, die Sie haben) über alles andere, im Fenster oder RootView können Sie es einfach die gleiche Rasterzelle wie der normale Inhalt besetzen. Dies ist, wie ich normalerweise überlappende Steuerelemente mache:

<Window x:Class="MyApp.MainWindow" 
     ...> 
    <Grid> 
     //my normal content 
     <myControls:Toast VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="20"/> 
    </Grid> 
</Window> 

Sie werden den gleichen Platz belegen, in der Reihenfolge, die Sie sie hinzufügen.

+0

danke Joe, Wie lege ich es auf andere Kontrollen? –

+0

Sie hätten nur einen einzigen Ort mit der Toast Control, direkt an der Wurzel Ihrer Anwendung. Um es über allem zu haben, könntest du es einfach nach deinem üblichen Inhalt platzieren. Ich werde ein Beispiel als Antwort geben. – Joe