SnippetLibrary

    • Release
    • Open Source

    Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von MichaHo.

      SnippetLibrary

      Name des Programms:
      SnippetLibrary

      Beschreibung:
      SnippetLibrary ist ein Programm in dem man seine Code Snippsel sammeln kann. Man kann diesen Tags hinzufügen, sie werden sortiert und kategorisiert.
      Außerdem kann ein Snippet mehrere Snippet Einträge (sprich mehrere Dateien mit Code) enthalten.Die Snippets können in Dateien gespeichert werden und die Dateien dann weitergegeben werden.

      Screenshots:



      siehe Anhang

      Verwendete Programmiersprache:
      C# (IDE: Visual Studio 2019 Community)

      Systemanforderungen:
      • .NET Framework v.4.7.2

      Bekannte Bugs:
      • Der Cusor springt beim Editieren eines Snippet Eintrages immer an das Ende der Textbox

      ToDo:
      • Export von ausgewählten Snippets

      Downloads:
      SourceCode auf Github
      SourceCode im Anhang (ca. 3,91MB)
      Zip mit EXE im Anhang (ca 635KB)

      Lizenz/Weitergabe:
      Freeware; Open Source
      Eine Namensnennung bei Weiterverbreitung/Weiterentwicklung wäre nett

      -------------

      Dies ist mein erstes richtiges Projekt, was ich versucht habe im MVVM Stil zu programmieren. Ich weiß ich habe z.B. keine Services genutzt und die Fenster einfach so aufgerufen, aber das wäre zu viel gewesen bei so einem kleinen Projekt.
      Vor einem viertel Jahr habe ich bereits ein Programm veröffentlicht, wo ich die Kritik bekommen habe das ich die WPF nicht richtig verwendet habe. Das hoffe ich habe ich diesmal besser hinbekommen.
      Ich würde mich also über konstruktive Kritik/Lob freuen.

      Viele Grüße
      Florian
      Meine Website
      Mein Projekt: SnippetLibrary

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

      Hallo flori

      Erstmal Glückwunsch zu deinem Projekt.
      Ich muss zugeben das ich mir den Code nun noch nicht angesehen habe aber meine erste Frage wäre warum es Adminrechte benötigt? das ist etwas was sehr viele Abschrecken könnte.

      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:

      Ich muss zugeben das ich mir den Code nun noch nicht angesehen habe aber meine erste Frage wäre warum es Adminrechte benötigt? das ist etwas was sehr viele Abschrecken könnte.

      Oh, da sind mir 2 Punkte reingerutscht, die da gar nicht hingehören.
      Dan Programm läuft auch auf 32Bit und benötigt KEINE Adminrechte.

      Sorry für die Verwirrung

      Viele Grüße
      Florian+
      Meine Website
      Mein Projekt: SnippetLibrary
      @flori2212 Im Showroom ist eine kompilierte Version deines Programms erforderlich. Bitte nachreichen (inkl. Dateigröße). Ich kann den Thread aber auch in den Sourcecodeaustausch schieben.
      Besucht auch mein anderes Forum:
      Das Amateurfilm-Forum
      Hallo

      Ich bin nun dazu gekommen es mir anzusehen. Erstmal muss ich sagen das es recht sauber umgesetzt ist. Es ist kein MVVM, das hattest du ja gesagt, aber dennoch hast du darauf geachtet mit Binding zu arbeiten und mit Commands.
      Das hast du auch ganz sauber umgesetzt und für die erste Version ist das schon sehr gut zu brauchen.

      Erstmal was ich gut finde.

      Top das man für ein "Snippet" mehrere Snippets erstellen kann. Also z.b. VB Code und einen weiteren Tab für den XAML. Super Idee die echt sinn macht.
      Gruppierung und Suchfunktion sind richtig umgesetzt.
      Danke für die Nennung meiner Person im About-Dialog.
      Ich finde auch gut das man sich mehrere "Dateien" anlegen kann (so kann man es nochmals unterteilen) obwohl ich es vermutlich "Datendatei" nennen würde, aber das ist ja schnuppe.

      OK, zu den Bugs die ich gefunden habe:

      Beim About Dialog funktioniert der OK Button nicht.
      Sucht man ein Snippet und es gibt keine Treffer ist ja "SelektedSnippet" leer, rechts wird also nichts angezeigt. Hier wäre im View gut wenn man anzeigen würde das kein Snippet merkiert ist. Oder man nützt der Platz für z.b. "Tipp des Tages" oder sowas.


      Und zum Schluss weil ich dich schon ein wenig kenne bist du sicher auch an vorschlägen zur Verbesserung offen:

      Das "--keine--" als vorauswahl bei den Tags finde ich ungünstig. Entweder mit Nothing (Null) vorbelegen oder mit einem Wasserzeichen so das man wenn das Feld den Focus bekommt einfach Tippen kann oder vorher das "--keine--" extra löschen zu müssen.
      Ist eine "Datei" nicht gespeichert sollte eine Speichernachfrage her wenn man die App schliessen möchte da sonst alle änderungen verloren sind.
      Ich fände es auch gut (vieleicht einstellbar) das 1.) automatisch gespeichert wird und 2.) die letzte Datei beim App-Start automatisch geladen wird damit man sich das öffnen der Datei sparen kann.

      Ansonsten mach weiter so, ich finde das für das erste mal auf jeden Fall Top und du hast es in einer sehr kurzen Zeit gut umgesetzt.

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

      Hallo,

      erst einmal vielen Dank für dein ausführliches Feedback und natürlich auch für das Lob.

      Ich gehe mal kurz auf das Feedback ein:

      Nofear23m schrieb:

      Das "--keine--" als vorauswahl bei den Tags finde ich ungünstig. Entweder mit Nothing (Null) vorbelegen oder mit einem Wasserzeichen so das man wenn das Feld den Focus bekommt einfach Tippen kann oder vorher das "--keine--" extra löschen zu müssen.

      Sehr gute Idee, ich werde es mit einer Watermark textbox lösen.

      Nofear23m schrieb:

      Ich fände es auch gut (vieleicht einstellbar) das 1.) automatisch gespeichert wird und 2.) die letzte Datei beim App-Start automatisch geladen wird damit man sich das öffnen der Datei sparen kann.

      Ich baue schon an einem settings Dialog, wo man genau das einstellen können soll.
      Man kann allerdings schon eine Datei mit den Programm öffnen. Also als befehlszeilenargument.

      Nofear23m schrieb:

      Ist eine "Datei" nicht gespeichert sollte eine Speichernachfrage her wenn man die App schliessen möchte da sonst alle änderungen verloren sind

      Das nervt mich auch sehr (ja, ich benutze die App auch selbst), allerdings hatte ich keine Idee, wie ich das ohne Code behind umsetzen sollte. Um einen Tipp wäre ich dankbar.

      Ach ja, du kanntest mich richtig und ich bin immer offen für Feedback.

      Viele Grüße
      Florian
      Meine Website
      Mein Projekt: SnippetLibrary

      flori2212 schrieb:

      allerdings hatte ich keine Idee, wie ich das ohne Code behind umsetzen sollte. Um einen Tipp wäre ich dankbar.

      Eine Idee von mir wäre folgende im Grunde simple Idee.

      Da du ja ViewModels hast, hast du auch die Setter der Properties. Gut. Am Ende vom Konstruktor hast du im Grunde alles initialisiert und es ändert sich nichts mehr an den Werten der Eigenschaften. Solange bis der User etwas ändert und in eine TextBox schreibt.

      Du legst dir also eine private Variable im ViewModel an. z.b. "HasChanges" diese wird am Ende des Konstruktors auf False gesetzt.
      Im Setter eines jeden Properties welches Relevant ist machst du dann HasChanges = True. So weist du immer ob es änderungen gibt.

      Kleiner Tipp, du kannst dir das Field eines Properties sparen wenn du damit ja nur ein Model-Objekt syncronisierst:

      Also statt:

      C#-Quellcode

      1. private string _CreatedBy;
      2. public string CreatedBy
      3. {
      4. get { return _CreatedBy; }
      5. set { _CreatedBy = value; Snippet_Model.CreatedBy = CreatedBy; RaisePropertyChanged(); }
      6. }


      Einfach:

      C#-Quellcode

      1. public string CreatedBy
      2. {
      3. get { return Snippet_Model.CreatedBy; }
      4. set {Snippet_Model.CreatedBy = CreatedBy; RaisePropertyChanged(); }
      5. }


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

      Hallo,

      ich fürchte ich habe mich nicht richtig ausgedrückt, bzw du hast mich nicht richtig verstanden.

      Ich meinte eher wie ich das closed Ereignis des Windows ohne Codebehind anfange.
      Ich kann ja nicht an einen "ClosedCommand" binden.

      Dein anderer Tipp war aber auch sehr hilfreich, darüber habe ich mir noch gar keine Gedanken gemacht. Ich hätte die Frage ob gespeichert werden soll einfach immer gemacht. Auf so kleine Ideen komme ich einfach nicht.

      Viele Grüße
      Florian
      Meine Website
      Mein Projekt: SnippetLibrary
      Hi @flori2212, geht meiner Meinung nach auch nur im Codebehind des Window, es sei denn du hast ein eigenes window gebastelt, dort hättest du ja dann einen Close Command wo du auf HasChanges reagieren könntest. Das HasChanges könntest du dann ins ViewModelBase packen, damit jedes viewmodel das von haus aus hat.
      EDIT: wobei das aber meiner Meinung nach kein Riesenverstoß wäre.
      du würdest ja das Window_closed event abfangen und dort dann (DataContext as DeinViewModeldesFensters).HasChanges abfragen
      "Hier könnte Ihre Werbung stehen..."
      Hallo

      JA, ich bin der selben Meinung wie MichaHo. Du kannst ruig die CodeBehind verwenden da es ja kein MVVM ist, macht das auch nix. Das ist schon ok.
      Wichtig ist nur das du die Logik in der Klasse (ViewModel-Klasse) belässt.

      Also machst du dir eine Methode in deiner Klasse. z.b. "AskForClose As Boolean" oder so. In der CodeBehind machst im Window_Closing sowas wie:

      VB.NET-Quellcode

      1. Dim vm As MeineKlasse = Me.DataContext
      2. if not vm.AskForClose then
      3. e.Cancel = True
      4. End if

      Um das schliessen des Fensters abbrechen zu können wenn nötig. (Weil du vieleicht drei Buttons in der MEssageBox hast (Ja, Nein, Abbrechen))
      Und innerhalb dieser Methode zeigst du wenn notwending die MessageBox an und reagierst drauf. Ist nichts zu speichern gibst du direkt True zurück.

      So wäre mein Gedankengang. Gibt aber sicher mehrere Möglichkeiten.

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

      Hallo flori2212

      ich finde deine Snippet Verwaltung sehr gelungen. Durch deine Vorgehensweise Snippet wiederum mit mehrer Snippets zu speichert kann eine sehr Strukturierte Verwaltung erstellt werden.

      In deinem ersten Screenshots hast du die Einträge VB und C# wie kann ich diese in einer Datei erstellen?

      Folgende Sachen sind mir aufgefallen die ich persönlich vermisse oder nicht schön finde
      - Position und Grösse des Fensters sollten gespeichert werden und beim Starten wieder hergestellt werden.
      - beim Klick auf Snippet->Neu solte das Eingabefeld Name den Forkus erhalten

      Wunsch
      Es wäre super wenn man zu jedem Snippet eine Datei hinterlegen kann.

      Freue mich auf weitere verbesserte Versionen.

      Gruß Norbert
      CCU2 • Raspberry für Wetterstation • ioBroker • HP ProLiant MicroServer Gen8 12GB 2x3TB Server 2012 R2 • PHP • MYSQL • VB.NET (Anfänger)
      Hallo @nobse

      erst einmal auch vielen Dank an dich für dein Feedback.
      Auch bei dir gehe ich mal auf deine Punkte ein:

      nobse schrieb:

      In deinem ersten Screenshots hast du die Einträge VB und C# wie kann ich diese in einer Datei erstellen?

      Das ist einfach eine Gruppierung der Snippets nach Sprache. Wenn du ein neues Snippet anlegst wählst du ja eine Sprache aus.
      Wenn du eine "eigene" Sprache hinzufügen willst kannst du es unter Optionen>Sprachenmanager tun. Ich hoffe das war soweit verständlich :)

      nobse schrieb:

      - Position und Grösse des Fensters sollten gespeichert werden und beim Starten wieder hergestellt werden.

      Sehr guter Vorschlag! :thumbsup: - Werde ich dann in die gleiche Datei wie die geplanten Einstellungen speichern.

      nobse schrieb:

      - beim Klick auf Snippet->Neu solte das Eingabefeld Name den Forkus erhalten

      Gibt Sinn, werde ich auch umsetzen.

      nobse schrieb:

      Es wäre super wenn man zu jedem Snippet eine Datei hinterlegen kann.

      Hier verstehe ich nicht genau was du meinst.
      Ich habe mir schon als weiteres Feature gedacht, dass man den Snippettext direkt in eine Codedatei exportieren kann.
      Könntest du das eventuell nochmal näher erklären.

      Viele Grüße
      Florian
      Meine Website
      Mein Projekt: SnippetLibrary
      Hallo flori2212

      da ich mehrer Klassen habe die ich Universell einsetze hatte ich gedacht hier könnte man statt den kompletten Code in das Snippet zu kopieren einfach die Datei Class_xyz.vb auswählen und diese im Snippet hinterlegen.

      Einfach nur so eine Idee von mir.

      Gruß Norbert
      CCU2 • Raspberry für Wetterstation • ioBroker • HP ProLiant MicroServer Gen8 12GB 2x3TB Server 2012 R2 • PHP • MYSQL • VB.NET (Anfänger)
      Hi @flori2212 sihet ganz gut.
      Eine Sache habe ich, was mir bei meiner Version auch aufgefallen ist.
      Wenn Du im Avalon TextEditor einen Text eingegeben hast und hast mehrere Zeilen und versuchst dann in einer oberen Zeile etwas einzugeben, dann wird das erste Zeichen in der Zeile eingegegebn, aber der Rest wird dann ganz unten weiter geschrieben. Probiers mal aus, Text schreiben, 2-3 mal enter, text schreiben, zurück in eine leere zeile oben drüber springen und schreiben = 1. zeichen wird geschrieben, rest unter der letzten Zeile.

      Ich hab einige Zeit dran rum gebastelt und eine Lösung gefunden. Ändere die AttachedProperty von @Nofear23m folgendermaßen ab:

      C#-Quellcode

      1. private static void AvalonTextProprty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
      2. {
      3. var textEditor = (TextEditor)d;
      4. textEditor.TextChanged -= AvalonText_Changed;
      5. textEditor.TextChanged += AvalonText_Changed;
      6. if (textEditor.Document != null)
      7. {
      8. var caretOffset = textEditor.CaretOffset;
      9. var newValue = e.NewValue;
      10. if (newValue == null) newValue = string.Empty;
      11. textEditor.Document.Text = newValue as string;
      12. textEditor.CaretOffset = Math.Min(caretOffset, (newValue as string).Length);
      13. }
      14. }

      der Rest kann so bleiben.

      Ich habe allerdings dazu noch eine Verständnis Frage:
      Obiger Code funktioniert bestens. wenn ich allerdings die Kurzschreibweisen nutze, funktioniert es nicht mehr:

      C#-Quellcode

      1. private static void AvalonTextProprty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
      2. {
      3. var textEditor = (TextEditor)d;
      4. textEditor.TextChanged -= AvalonText_Changed;
      5. textEditor.TextChanged += AvalonText_Changed;
      6. if (textEditor.Document != null)
      7. {
      8. var caretOffset = textEditor.CaretOffset;
      9. var newValue = e.NewValue;
      10. textEditor.Document.Text = newValue is null ? string.Empty : newValue as string;
      11. textEditor.CaretOffset = Math.Min(caretOffset, (newValue as string).Length);
      12. }
      13. }

      schmeißt eine System NullReference Exeption weil er dann das CaretOffset nicht setzen kann, weil newValue null ist, sollte aber doch String.Empty sein.
      Genauso übrigens bei

      C#-Quellcode

      1. private static void AvalonTextProprty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
      2. {
      3. var textEditor = (TextEditor)d;
      4. textEditor.TextChanged -= AvalonText_Changed;
      5. textEditor.TextChanged += AvalonText_Changed;
      6. if (textEditor.Document != null)
      7. {
      8. var caretOffset = textEditor.CaretOffset;
      9. textEditor.Document.Text = e.NewValue is null ? string.Empty : e.NewValue as string;
      10. textEditor.CaretOffset = Math.Min(caretOffset, (e.NewValue as string).Length);
      11. }
      12. }


      warum ist das so? stehe da grad etwas auf dem Schlauch

      EDIT: ach vergesst es.... bin grad selbst drauf gekommen newValue oder e.NewValue wird ja nicht auf String.Empty gesetzt wenn das CarretOffset gesetzt wird, das ist ja dort immer der Original Inhalt.... Au mann... Wald Bäume

      EDIT2: mit Kurzschreibweise wäre es dann so:

      C#-Quellcode

      1. private static void AvalonTextProprty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
      2. {
      3. var textEditor = (TextEditor)d;
      4. textEditor.TextChanged -= AvalonText_Changed;
      5. textEditor.TextChanged += AvalonText_Changed;
      6. if (textEditor.Document != null)
      7. {
      8. var caretOffset = textEditor.CaretOffset;
      9. var newValue = e.NewValue is null ? string.Empty : e.NewValue as string;
      10. textEditor.Document.Text = newValue;
      11. textEditor.CaretOffset = Math.Min(caretOffset, newValue.Length);
      12. }
      13. }

      "Hier könnte Ihre Werbung stehen..."

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

      Hallo @MichaHo

      auch dir vielen Dank fürs Testen. :)

      Bez. des Fehlers:
      Der ist mir auch schon aufgefallen, habe ich auch im Hauptpost als Bug aufgeführt.

      Deine Lösung hat prima funktioniert, da kam ich mit dem CaretOffset nicht drauf.

      Werde die nächsten Tage ein Update veröffentlichen, in das einige Verbesserungen reinkommen.

      Viele Grüße
      Florian
      Meine Website
      Mein Projekt: SnippetLibrary
      @flori2212 Gerne.
      Wenn Du noch eine Lösung findest, wie man IsModified setzen kann, dann her damit.
      Ich möchte bei mir nämlich das Schließen Icon der TabPage abhängig davon setzen ob der Text im Editor geändert wurde oder nicht.
      es gibt zwar ein textEditor.IsModified, aber das scheint immer zu greifen wenn das Document/TextArea innerhalb des Editors sich ändert. (Also auch beim Laden eines Files in den Editor)
      Ich vermute, ich muss da auch eine Property für basteln :(
      "Hier könnte Ihre Werbung stehen..."
      Hallo @MichaHo

      Ich denke ich werde hier in meinem ViewModel ein Property setzen, wie public bool IsModified {get;set;}, was ich dann einfach auf ​true setze, wenn sich ein anderes entscheidendes Property (wie z.B. ​SnippetText) ändert.
      Ich denke das ist die einfachste Möglichkeit.

      Viele Grüße
      Florian
      Meine Website
      Mein Projekt: SnippetLibrary
      das Property hab ich im ViewModel des Editors, das wird sogar selbsständig gesetzt im Xaml:

      XML-Quellcode

      1. IsModified="{Binding IsModified, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

      es wird sogar geändert wenn sich nur der Text innerhalb des Documents ändert, habs jetzt mehrfach probiert. Nur ändert sich das Icon nicht, da muss ich nochmal schauen wo es da klemmt...

      EDIT: das Icon ist ein Button und sieht im TabControl Xaml so aus:

      XML-Quellcode

      1. <Button Style="{StaticResource TabItemButton}"
      2. Content="{Binding HeaderImage, UpdateSourceTrigger=PropertyChanged}"
      3. Command="{Binding DataContext.CloseTabCommand, ElementName=mainUc }"
      4. CommandParameter="{Binding}" />


      Da IsModified ein Property im EditorViewModel ist, das HeaderImage aber im TabPageViewModel sieht das bei mir so aus:

      im EditorViewModel:

      C#-Quellcode

      1. public bool IsModified { get; set; }


      und im TapPageViewModel dann:

      C#-Quellcode

      1. public EditorViewModel Editor { get; set; }
      2. public string HeaderImage => Editor.IsModified ? "\uf111" : "\uf00d";


      kurz noch zur Erklärung:
      Ich nutze das NuGet Package FoodyWeaver, das setzt OnPropertyChanged selbstständig, deswegen muss ich es nicht im Setter aufrufen.
      HeaderImage ist ein String denn ich nutze FontAwesome um die Icons zu haben (fontawesome.com/cheatsheet)
      das klappt auch soweit, nur eben nicht zur Laufzeit wenn ich den Text ändere...
      "Hier könnte Ihre Werbung stehen..."

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

      OK, Lösung gefunden:
      Ich hab das Property HeaderImage ins EditorViewModel gepackt und setze es dort:

      C#-Quellcode

      1. public bool IsModified { get; set; }
      2. public string HeaderImage => IsModified ? "\uf111" : "\uf00d";


      und im TabControl Hedaer setze ich dann:

      XML-Quellcode

      1. <Button Style="{StaticResource TabItemButton}"
      2. Content="{Binding Editor.HeaderImage, UpdateSourceTrigger=PropertyChanged}"
      3. Command="{Binding DataContext.CloseTabCommand, ElementName=mainUc }"
      4. CommandParameter="{Binding}" />


      Klappt hervorragend.
      Jetzt kümmer ich mich um Undo und Redo...
      "Hier könnte Ihre Werbung stehen..."