[WPF] AnimatedContentControl

    • Beta
    • Open Source

    Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von Nofear23m.

      [WPF] AnimatedContentControl

      Name:
      AnimatedContentControl

      Beschreibung:
      Ein WPF ContentControl welches beim ändern dessen Contents animiert wird. Ändert man den Inhalt welchen das Control anzeigen soll wird der alte Content "rausanimiert" und der neue "reinanimiert". Dabei kann die Zeitspanne´und die Richtung der Animation (nach links, nach rechts, nach oben, nach unten) über Properties gewählt werden. Auch die Dauer der Animation und ob ein Easeing Animiert werden soll kann über Properties bestimmt werden. Alle Properties wurde als Dependency Properties implementiert und sind somit alle für Binding geeignet wodurch das Controls MVVM tauglich ist.
      Auf die Idee brachte mich @MichaHo in folgendem Beitrag. Das hat mich irgendwie interessiert.

      Screenshot(s):
      Falls interessant reiche ich einen YouTube Link nach.

      Verwendete Programmiersprache(n) und IDE(s):
      VS 2019, VisualBasic, WPF .Net Full Framework 4.6

      Systemanforderungen:
      .Net Framework 4.6

      Systemveränderungen:
      keine

      Download(s):
      Projekt auf GitHub: github.com/NoFear23m/AnimatedContentControl
      NuGet Page: nuget.org/packages/AnimatedContentControl/
      NuGet Package Manager: Install-Package AnimatedContentControl



      Lizenz/Weitergabe:
      Open Source, ausser dem weiterverkauf könnt ihr damit machen was ihr wollt ;)

      Schöne Grüße
      Sascha
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Hi,

      hab mir mal dein Animated Control runter geladen und getestet, funktioniert super.
      Habs dann in C# .NetCore nachgebaut und in mein Projekt eingefügt.
      Find ich echt Klasse, das spart mir eine Menge Arbeit mit den ganzen Pages...
      Das schalten der "Seiten" funktioniert auch von den einzelnen ViewModels aus, dazu reiche ich den MainWorkspace (bei Dir das SwitchContentViewModel) zu den ViewModels durch.
      Dateien
      • PagesTest.zip

        (16,18 kB, 179 mal heruntergeladen, zuletzt: )
      "Hier könnte Ihre Werbung stehen..."

      MichaHo schrieb:

      Find ich echt Klasse, das spart mir eine Menge Arbeit mit den ganzen Pages...

      Und selbst gemacht, du hast also die Kontrolle. Ja, das Rad muss man nicht immer neu erfinden, aber wenn man für etwas ein ganzes Framework benötigt ist mir das zu viel. Da baue ich sowas gerne nach und wie man sieht ist es nicht soo viel Arbeit. Wie du gesehen hast habe ich es ja nun anders gelöst aber es funktioniert.

      Nun kannst du weitergehen und mit Services weitermachen (in einen anderen Thread) damit du auch Messageboxen und der gleichen regeln kannst.

      Grüße
      Sascha
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Nofear23m schrieb:

      Nun kannst du weitergehen und mit Services weitermachen (in einen anderen Thread) damit du auch Messageboxen und der gleichen regeln kannst.


      Ich bau erst mal ne Solution, wo man das alles einbauen kann. Meld mich wieder
      "Hier könnte Ihre Werbung stehen..."
      @Nofear23m Eine Frage hab ich dann doch noch zum AnimatedControl.

      Der Style muss zwingend in der Datei Generic.xaml sein, richtig?
      Hatte im TestProhejt den Style umbenannt und das Control funktionierte nicht mehr.
      Ich konnte nur nicht heraus finden, wo er danach sucht.
      "Hier könnte Ihre Werbung stehen..."

      MichaHo schrieb:

      Ich konnte nur nicht heraus finden, wo er danach sucht.

      Genau in Themes/Generic.xaml, zumindest solange man den Style nicht berschreibt.

      Grüße
      Sascha
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Das verstehe ich jetzt nicht. Na in der generic.xaml. Der Style des Controls ist genau dort festgelegt. Willst du was ändern machst du es genau dort. Oder was meinst du?
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Ja, das ist ja klar, nee, ich meinte die Datei selbst muss zwingend Generic.xaml heissen, sonst klappt es nicht. Und da wollte ich wissen wo das definiert ist. Ändere ich Generic.xaml zum Beispiel in AnimatedStyle.xaml funktioniert es nicht mehr und ich hab nirgendwo nen Verweis auf Generic.xaml gefunden. Also warum muss die Datei für den Style Generic.xaml heissen?
      "Hier könnte Ihre Werbung stehen..."

      MichaHo schrieb:

      Also warum muss die Datei für den Style Generic.xaml heissen?

      Weil das bei CustomControls unter WPF so gedacht ist das man für jedes Theme (Windows Theme) ein eigenes "Default" Aussehen dafinieren kann. Generic ist dann quasi der FallBack wenn es kein XAML zum aktuellen Windows Theme gibt. Meines wissens nach kann das auch nicht geändert werden.
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Sollte normal hinhauen, solange du dann in der Generic.xaml diese Datei als Resource einbindest. Aber wozu Frage ich mich da.
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Hallo,
      beim schalten von Views aus dem ViewModel heraus machte mir immer Kopfzerbrechen, wie ich das geeignete ViewModel zur View bekomme.
      Hab in den letzten Tage viel darüber gelesen und stieß immer wieder auf das Zauberwort Messenger.
      Also hab ich das Beispiel hier noch einmal zur Hand genommen und darin versucht, dies umzusetzen.
      Ziel war es, von jedem ViewModel aus, ein anderes ViewModel über einen Command aufzurufen.
      Durch das Zuweisen von Datatemplates, weis dann das ViewModel, zu welcher View es gehört.
      Beispiel:
      in der App.xaml oder auch in einem eigenen ResourceDictionary kann man ein DataTemplate erstellen:

      XML-Quellcode

      1. <DataTemplate DataType="{x:Type viewmodel:FirstViewModel}">
      2. <control:FirstControl/>
      3. </DataTemplate>

      Hier sage ich, das das DataTemplate vom Typ FirstViewModel ist und als Content das FirstControl beinhaltet.
      Somit ist für die WPF klar, wenn jemand das FirstViewModel haben will, das dann das FirstControl präsentiert werden muss und als DatenContext das FirstViewModel hat.

      OK, weiter...
      Jede WPF Anwendung hat meist ein EinstiegsViewModel, @Nofear23m nennt diese meist Workspace, bei mir ist es meist das ApplicationViewModel, das ViewModel welches sich um die gesamte Application kümmert.
      Ob dies nun eine statische Klasse über DI ist, oder ein "normales ViewModel", oder oder, ist im Grunde egal.

      Vorbereitungen:
      Damit ein ViewModel geschaltet werden kann, sollte man sich hier ein Interface erstellen, welches ich jedem ViewModel zuweise, das geschaltet werden soll.
      Es gibt ja durchaus auch ViewModel, die über keine Commands verfügen, daher erstelle ich hier ein Interface und nehme nicht die ViewModelBase dafür.
      Das interface hat den weiteren Vorteil, das ich jedem ViewModel, welches davon erbt, gleich ein paar Properties mitgeben kann, z.Bsp. einen DisplayText, oder einen Command
      Mein IContentViewModel sieht so aus:

      C#-Quellcode

      1. public interface IContentViewModel
      2. {
      3. string DisplayText { get; set; }
      4. ICommand BackCommand { get; set; }
      5. }


      DesWeiteren benötigen wir irgendetwas, was wir aufrufen können, um aus einem x-beliebigen ViewModel ein bestimmtes, anderes ViewModel aufzurufen.
      Hier kommt der Messenger ins Spiel. der Messenger registriert einen String und eine Action dazu, heißt, wer immer diesen String aufruft und etwas übergibt, erhält die dazugehörige Action, die dann ausgeführt wird.
      Wird keine Action übergeben, wird die Action ausgeführt, die dem String hinterlegt wurde.
      Hier erstmal stumpf die Messenger Klasse:

      C#-Quellcode

      1. public static class Messenger
      2. {
      3. private static IDictionary<string, List<Action<object>>> _keyValues = new Dictionary<string, List<Action<object>>>();
      4. public static void Register(string key, Action<object> actionValue)
      5. {
      6. if(!_keyValues.ContainsKey(key))
      7. {
      8. var list = new List<Action<object>>();
      9. list.Add(actionValue);
      10. _keyValues.Add(key, list);
      11. }
      12. else
      13. {
      14. foreach (var action in _keyValues[key])
      15. {
      16. if (!(action.Method.ToString() == actionValue.Method.ToString())) _keyValues[key].Add(actionValue);
      17. }
      18. }
      19. }
      20. public static void UnRegister(string key, Action<object> actionValue)
      21. {
      22. if (_keyValues.ContainsKey(key)) _keyValues[key].Remove(actionValue);
      23. }
      24. public static void Notify(string key, object param = null)
      25. {
      26. if(_keyValues.ContainsKey(key))
      27. {
      28. foreach (var action in _keyValues[key])
      29. {
      30. action(param);
      31. }
      32. }
      33. }
      34. }


      Als erstes erstellen wir uns ein Dictionary vom Typ string und List<Action<object>>. Das Dictionary nimmt also einen String (der Name der Action) und eine Liste von Actionen die als parameter ein Object haben kann.
      Dann erstellen wir uns 3 statische Methoden Register, Unregister und Notify.
      Die Methode Register nimmt also einen String, der den Namen unserer Nachricht darstellt, und eine Action, die ausgeführt werden soll, wenn diese Nachricht aufgerufen wird.
      Zuerst prüfen wir, ob unser Dictionary bereits diese Nachricht enthält, is dem nicht so, erstellen wir uns eine Liste von Actionen (man könnte ja mehrere Actionen bei einer Nachricht ausführen wollen) fügen dieser Liste unsere übergebene Action hinzu und fügen dann unserem Dictionary die Nachricht, sowie die Liste der Actionen hinzu.
      Gibt es bereits diese Nachricht, dann prüfen wir ob die Nachricht bereits die übergebene Action enthält, wenn nicht, fügen wir zu dieser Nachricht, die Aktion hinzu.

      Die UnRegister Methode entfernt einfach bei der übergebenen Nachricht die übergebene Aktion.
      Die Methode Notify:
      Hier prüfen wir, ob die übergebene Nachricht existiert und füren dann alle Aktionen aus, die dieser Nachricht zugrunde liegen.

      OK, unsere Messenger Klasse steht. Nun erstellen wir uns in unserem HauptEingangspunkt (ApplicationViewModel oder MainWorkspace) eine Methode, mit der wir (in diesem Beispiel) ViewModels des Contents ändern können

      Als erstes benötigen wir ein Property vom Typ IContentViewModel

      C#-Quellcode

      1. private IContentViewModel _currentContentViewModel;
      2. public IContentViewModel CurrentContentViewModel { get => _currentContentViewModel; set => SetValue(ref _currentContentViewModel, value); }


      Im Konstruktor könnten wir diesem Property bereits ein ViewModel zuweisen

      C#-Quellcode

      1. CurrentContentViewModel = new MainViewModel();


      Dann erstellen wir uns eine Methode in der wir dem CurrentContentViewModel ein neues ViewModel übergeben, da der Parameter vom Typ object ist, müssen wir dies in unser IContentViewModel casten

      C#-Quellcode

      1. private void ChangeContentViewModel(object param)
      2. {
      3. CurrentContentViewModel = (IContentViewModel)param;
      4. }


      In unserem Konstructor müssen wir dann diese Methode registrieren:

      public ApplicationViewModel()
      {
      Messenger.Register("SwitchContent", ChangeContentViewModel);
      }

      In unserem MainViewModel können wir nun einen Command erstellen, der ein anderes ViewModel aufruft (auch hier können ja dann Parameter übergeben werden)

      C#-Quellcode

      1. public ICommand FirstPageCommand { get; private set; }
      2. Public MainViewModel()
      3. {
      4. FirstPageCommand = new RelayCommand(f => Messenger.Notify("SwitchContent", new FirstViewModel("Erste Seite von Hauptseite")));
      5. }


      Nun brauchen wir nur noch in unserem MainWindow einen ContentPresenter oder ein ContentControl das wir an CurrentContentViewModel binden und schoon switchen die ViewModels mit dazugehörigen Controls.

      Die Messenger Klasse ist sicherlich nicht das ultimative, aber ich hab sie so erstellt, wie ich Messages verstanden habe.
      Für Verbesserungsvorschläge bin ich natürlich immer offen.
      "Hier könnte Ihre Werbung stehen..."

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „MichaHo“ ()

      Hallo

      MichaHo schrieb:

      beim schalten von Views aus dem ViewModel heraus machte mir immer Kopfzerbrechen, wie ich das geeignete ViewModel zur View bekomme.

      Dafür braucht es aber das ganze Messenger Zeugs garnicht. Due hast doch deine ViewModels und hast du Content auf ein Property gebunden wird auch über die DataTemplates das richtige UserControl gerendert. Hat jetzt mit dem Messenger nix zu tun. Und für alles andere gibt es die Services. Messenger kann man für gewisse Dinge hernehmen, ich nehme Sie allerdings ungern her. Da verliert man in größeren Projekten gerne mal den "Faden".

      Aber nur mal so: Hast du dich im Thread geirrt? Soll ich das verschieben?

      Grüße
      Sascha
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Hi,
      Nee, der thread war schon der richtige, hatte das ja in Pagestest eingebaut.

      das mit den Templates weis ich ja und funktioniert ja grundsätzlich auch, aber... ich möchte ja den content von jedem x-beliebigen ViewModel aus ändern. Und wenn das Property nur im MainWorkspace drinn habe, müsste ich diesen Workspace ja in jedem ViewModel übergeben um von einem Viewmodel aus auf das Property zugreifen zu können. Da fand ich das mit dem Messenger einfacher. Man kann bestimmt aber auch einen Service dafür nutzen.

      wenn ich mit Dialogen arbeiten würde, wäre es sicherlich einfacher, aber dialoge mag ich nicht so.

      Ich kanns nicht gut erklären, aber sagen wir, wir haben eine Anwendung mit 1 Fenster.
      Der Content des Fensters soll unterschiedliche Controls beinhalten. Startet die Anwendung ist das CurrentConten zum Beispiel das Login ontrol. Bei Klick auf anmelden soll dann zum HauptControl gewechselt werden. Vom Hauptcontrol könnte man dann zum Beispiel zu einem DetailControl wechseln und vom Detail nochmal zu einem anderen Control. Und das alles innerhalb des MainWindow.
      Das Mainwindow hat einen ContentPresenter der an ein Property im MainViewModel gebunden ist. Wie setze ich jetzt das Property vom DetailViewModel aus ohne das MainViewModel bis runter ins DetailViewModel zu übergeben?

      ich kanns wirklich schwer erklären, vielleicht mach ich morgen ne BeispielAnwendung in einem neuen Thread, ist ja auch ein Interessantes Thema für andere.
      "Hier könnte Ihre Werbung stehen..."
      Die Beispielanwendung hast du ja schon erstellt.

      Ich verstehe dein Problem mit dem übergeben/durchreichen aber in 90% der Fälle ist es ja so das es nicht stört wenn man im jeweiligem ViewModel eine neue Instanz des aufzurufenden ViewModels erstellt bzw. ist es nach meiner Erfahrung sogar so das man dies meißt sogar möchte. Denn die Daten könnten ja veraltet sein, also erstelle ich sowieso eine neue Instanz wodurch auch der Ladevorgang der Daten neu von statten geht. Da sehe ich kein Problem.
      Es muss also nicht immer alles durchgereicht werden.

      Grüße
      Sascha
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##