Autor Beitrag
Terrenay
Hält's aus hier
Beiträge: 15



BeitragVerfasst: So 09.02.14 19:42 
Hallo, ich will morgen damit anfangen, ein Karteikasten-Programm zu programmieren. Falls ihr das nicht kennt, in der Realität funktioniert das so, dass man auf die Vorderseit einer Karte zB das Deutsche Wort schreibt, und auf die Hinterseite die englische Übersetzung. Die Karte landet dann in Box 1 des Kastens.
Irgendwann testet man alle Karten durch und schaut, welche man weiss und welche nicht. Falls man das Wort richtig übersetzt hat, kommt es eine Box weiter, ansonsten landet es wieder in Box 1.

Nun zum Programm (welches noch nicht existiert xD)
Das Grundgerüst hab ich im Kopf, es sollte ja auch nicht alllll zu schwer sein, jedoch weiss ich nicht, wo und vor allem WIE ich dann die eingegebenen Wörter speichern soll.
Weil man soll ja dann jedes Wort, das man eingegeben hat, auch beim Nächsten Programmstart noch zur Verfügung haben.

Tut mir Leid, wenn die Frage (wieder mal) total einfach zu beantworten ist...

Soll ich das mit 5 .txt-Dateien machen, also eine pro Box? Wüsste aber nicht, wie ich das dann auslese...
Oder mit einer Access-Verbindung? Hab keinen Schimmer, wie man das macht^^
Oder vielleicht mir XMLSerialization? Habe ich aber auch noch nie gemacht, und ich weiss nicht einmal, ob das überhaupt das ist, was ich brauche...


Kann mir jemand sagen, wie ich die Wörter speichern kann? :)
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: So 09.02.14 21:43 
Ich würde dir eine Klasse Card empfehlen, der du eine Property spendierst:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
class Card
{
  public string word { get; set; }
  public string translation { get; set; }
  public int box { get; set; }

}

Das kannst du dann in einer List<Card> verwalten und mit dem xmlSerializer schreiben und lesen:
ausblenden C#-Quelltext
1:
private List<Card> vokabelliste = new List<Card>();					


Das Subset der Karten in der dritten Box erhältst du mit vokabelliste.Where(x => x.box == 3)

Ist jetzt nicht besonders performant, aber einfach :wink:
Terrenay Threadstarter
Hält's aus hier
Beiträge: 15



BeitragVerfasst: So 09.02.14 22:55 
Sorry, aber ich hab noch nie mit Listen oder mit dem Serializer gearbeitet... Kannst du mir das bitte genauer erklären? :)

PS: Ich hab das jetzt eigentlich so geplant, dass ich eben 5 .txt-Dateien habe und dann einfach die Wörter nach dem Schema "Wort-Übersetzung-Wort-Übersetzung-" speichere. Dann kann ich mit meiner Funktion "MoveWord(string PATHfromBox, string PATHtoBox, string Word);" das Wort, falls der Benutzer es nicht wusste, von der aktuellen Box zu Box 1 verschieben und sonst von der aktuellen zu der Nächst höheren ^^

Klingt das auch vernünftig?
Hier ist die Funktion, an der ich den ganzen Abend gearbeitet habe xD (Sie verschiebt das Wort noch nicht, aber kann es immerhin schon löschen)

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
public void DeleteWord(string path, string word)
        {
            if (File.Exists(path)) //Prüfen, ob die übergebene Datei vorhanden ist
            {
            string x = File.ReadAllText(path); //In x ist der ganze Text der übergebenen Datei
            string[] splitoperator = new string[] { "-" }; //Ein Trennstrich ist der Splitter
            string[] y; //Array y erstellen
            y = x.Split(splitoperator, StringSplitOptions.RemoveEmptyEntries); //Zerschnipsle den Text der übergebenen Datei in Array-Indexe
            string Schreiben = ""//Der Text, der später wieder in die Datei geschrieben wird
            for (int i = 0; i < y.Length; i++) //Selbsterklärend ^^
            {
                if (y[i] == word) //Wenn das Wort auf dem aktuellen Array-Index gleich ist wie das, welches man entfernen will
                    y[i] = string.Empty; //ersetze das Wort mit string.Empty
                Schreiben = Schreiben + y[i] + "-"//Der in die Datei zu schreibende Text + das Wort auf dem aktuellen Array-Index + ein Trennstrich, damit die Funktion auch das nächste mal geht
            }
            File.WriteAllText(path, Schreiben); //Wenn der Array komplett durchlaufen wurde, schreibe den neuen Text zurück in die Datei
            }
            else //Wenn die übergebene Datei nicht vorhanden ist, gib Fehlermeldung aus
                MessageBox.Show("Ein Fehler im Programmcode ist aufgetreten. Der an die Methode DeleteWord übergebene Pfad ist ungültig!");
        }



Wäre das zu umständlich oder zu abwegig, um es im Programm zu benutzen? Weil das, was du mir vorgeschlagen hast, klingt auch vernünftig ^^
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Mo 10.02.14 03:28 
Ja, es ist schon ein wenig umständlich weil jedes Mal die Datei gelesen und geschrieben wird. Du solltest idealerweise nur einmal beim Start lesen, dann im Speicher arbeiten und beim Schließen des Programms wieder speichern.

Ich bin mal so nett ....

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
using System.Xml.Serialization;

namespace Test_2
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private List<Card> mylist;

        public MainWindow()
        {
            InitializeComponent();

            // mylist wird hier initialisiert, aber es kommt halt imme rdas gleiche hinein
            mylist = new List<Card>() { new Card("Tier""animal"1),
                new Card("lächerlich""preposterous"2),
                new Card("seltsam""peculiar"2)};
        }

        private void SaveBtn_Click(object sender, RoutedEventArgs e)
        {
            // ggf. durch savedialog festlegen
            var filename = "../../data.xml";
            var xser = new XmlSerializer(mylist.GetType());
            using (var fs = new FileStream(filename, FileMode.Create))
            {
                xser.Serialize(fs, mylist); // Speichern
            }
        }

        private void LoadBtn_Click(object sender, RoutedEventArgs e)
        {
            // ggf. durch opendialog festlegen
            var filename = "../../data.xml";
            var xser = new XmlSerializer(mylist.GetType());
            using (var fs = new FileStream(filename, FileMode.Open))
            {
                mylist = xser.Deserialize(fs) as List<Card>; // Laden
            }
        }

        private void btnPrint_Click(object sender, RoutedEventArgs e)
        {

            // Alle Karten aus Box 2 werden ausgegeben
            foreach (var item in mylist.Where(c => c.Box == 2))
            {
                Console.WriteLine(item);
            }
        }
    }

    public class Card
    {
        public string Word { get; set; }
        public string Translation { get; set; }
        public int Box { get; set; }

        public Card() { }

        public Card(string word, string trans, int box = 0)
        {
            Word = word;
            Translation = trans;
            Box = box;
        }

        public override string ToString()
        {
            return Word + "==" + Translation;
        }
    }
}


Das funktioniert schon und sollte dir eine Idee geben. Aufgrund der forgeschrittenen Zeit muss ich aber auch mal weg ;-)

Das XAML:
ausblenden XML-Daten
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
<Window x:Class="Test_2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button x:Name="LoadBtn" Content="Laden" HorizontalAlignment="Left" Margin="51,48,0,0" VerticalAlignment="Top" Width="75" Click="LoadBtn_Click" />
        <Button x:Name="SaveBtn" Content="Speichern" HorizontalAlignment="Left" Margin="51,92,0,0" VerticalAlignment="Top" Width="75" Click="SaveBtn_Click" />
        <Button x:Name="btnPrint" Content="Box 2" HorizontalAlignment="Left" Margin="191,92,0,0" VerticalAlignment="Top" Width="75" Click="btnPrint_Click"/>
    </Grid>
</Window>

Für diesen Beitrag haben gedankt: Terrenay
Terrenay Threadstarter
Hält's aus hier
Beiträge: 15



BeitragVerfasst: Mo 10.02.14 17:45 
Danke ^^
Ich werds wohl jetzt mal so probieren, dass ich eben einfach beim Start die Wörter aus den Dateien in 5 Arrays speichere und sie halt am Ende wieder in die Dateien speichere :)
Wäre doch schon besser als jedes mal zu laden und zu speichern, oder? xD
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Mo 10.02.14 19:17 
Ja, das klingt schon besser :-) Aber warum möchtest du denn überhaupt Arrays verwenden? Da sich ja quasi per Definition die Menge an Wörtern in jeder Box ändern kann, wäre Listen hier viel geeigneter.

Eigentlich brauchst du sehr selten Arrays. Nämlich nur dann, falls du eine Menge von Werten hast, deren Anzahl sich nie ändert. Man könnte auch sagen: Arrays sind Datenstrukturen, die einfach für den Computer sind und Listen sind Datenstrukturen die einfach für den Menschen sind. Du brauchst dich mit Listen viel weniger um das hin- und herkopieren kümmern und hast noch ein paar andere nette Dinge die dir Arbeit abnehmen.

Die Grundzüge habe ich dir ja oben schon gezeigt. Falls dazu noch Fragen auftauchen kannst du die gerne hier stellen.
Terrenay Threadstarter
Hält's aus hier
Beiträge: 15



BeitragVerfasst: Di 11.02.14 18:40 
Hat doch nicht so geklappt, wie ich es wollte, und ich hab das Programm jetzt leider doch mit Auslesen und Speichern bei jeder Änderung geschrieben... Wenigstens funktioniert es so, wie ich es gerne hätte, und erfüllt eigentlich meine Bedingungen :)

Zu der Frage, wieso ich keine Listen verwende: Ich verstehe die noch nicht ganz, ich programmiere erst seit knapp 2 Wochen mit Windows Forms (musste mich also, nachdem ich vorher immer die Konsole benutzt hatte, ziemlich umgewöhnen) und im Lehrbuch, mit dem ich die Sprache gelernt habe, wurden Listen nicht einmal richtig eingeführt. Kannst du mir vlt erklären, was
ausblenden C#-Quelltext
1:
mylist.Where(c => c.Box == 2)					

bedeutet? Die Wörter der Liste, die in Box 2 sind, auslesen? Aber was bedeuetet dann c? ^^

lg
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Di 11.02.14 20:36 
Hallo,
das ist ein Lambda-Ausdruck. Zugegebenermaßen ein komplexeres Sprachkonstrukt.

Die Signatur von Where schaut so aus:
ausblenden C#-Quelltext
1:
2:
3:
4:
public static IEnumerable<TSource> Where<TSource>(
  this IEnumerable<TSource> source,
  Func<TSource, bool> predicate
)


Erläutert:
1. Kann auf eine beliebige Liste angewendet werden
2. gibt wieder eine Liste des gleichen Typs zurück
3. Benötigt eine callback-Funktion zur Auswahl der Elemente die ein Element entgegennimmt und einen Boolean zurückgibt.

Und das c => c.Box == 2 Ist eine Kurzversion von einer anonymen Methode die das hier macht:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
bool callback(Card element)
{
  if element.Box == 1
    return true;
  else
    return false;
  // Das if ist ja equivalent zu
  return element.Box == 1;
}
(Ich weiß, das ist nicht die richtige syntax für ein anonymes delegat)
aber auf das wesentliche verkürzt: Parametername und code. Der Parameter muss benannt werrden, da führt kein weg drum herum. Das passiert linkt von dem Pfeil. Der Ausdruck der zurück gegeben wird ist das rechts neben dem Pfeil. alsoelement => return element.Box == 1 nur dass man auch das return weglassen muss. Eben auf das allernötigste komprimiert.

Die Hilfeseite dazu ist hier: msdn.microsoft.com/d...ibrary/bb397687.aspx Aber ich denke das ist schwer für einen Anfänger zu verstehen.

Dieses callback wird an Where() übergeben. Und zurück kommen alle Elemente, für die das Callback true zurück gegeben hat.


Hier vielleicht noch ein kleines Beispiel:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Func<intint> quadrieren = (x => x * x);
            Func<intint> hoch3 = (x => x * x * x);

            Console.WriteLine("Das Quadrat von {0} ist {1}."5, quadrieren(5));
            Console.WriteLine("{0} hoch drei ist {1}."4, hoch3(4));

            /* Ausgabe:
             * Das Quadrat von 5 ist 25.
             * 4 hoch drei ist 64.
             */

        }

Du kannst dir das ziemlich gut als "minimal kurze" Deklaration einer Funktion vorstellen. Oft macht man das, weil es ein wirklich kleiner Ausdruck ist und man dafür keine extra private Methode machen möchte.
Terrenay Threadstarter
Hält's aus hier
Beiträge: 15



BeitragVerfasst: Mi 12.02.14 19:24 
So, hab mich jetzt dank euren Tipps in die Listen "einprogrammiert" und versteh sie jetzt schon ein bisschen besser :)
Ihr habt Recht, sie sind wirklich viel einfacher zu benutzen als Arrays und ich kann mir vorstellen, dass es nicht sehr schwer wird (zumindest nicht schwerer als das, was ich vorgestern gemacht habe), meinen Karteikasten damit noch einmal zu programmieren ^^

Also Danke nochmal an jfheins und an Palladin007 (du hast mir ja per PN geholfen ^^)

LG!