Laden...

[Gelöst] Verschiedene Klassen in gleiches "Form" verwenden

Erstellt von elTorito vor 8 Jahren Letzter Beitrag vor 8 Jahren 3.226 Views
elTorito Themenstarter:in
177 Beiträge seit 2009
vor 8 Jahren
[Gelöst] Verschiedene Klassen in gleiches "Form" verwenden

Hi,

ich habe folgende Klassen:


public class MyXGames
{
        public ObservableCollection<X01Game> X01GameList{}
        public ObservableCollection<X10020Game>X10020GameList{}

public class XGame {}
public class X01Person :Person{}
public class X10020Person :Person{}

public class X01Game :XGame
{
         public ObservableCollection<PersonX01> Persons{}
}

public class X10020Game :XGame
{
         public ObservableCollection<PersonX10020> Persons{}
}


Nun habe ich ein Formular (XAML) welches an eine "Person" gebunden war.
Dieses Form(Eingabemöglichkeit) möchte ich nun "Spielübergreifend" nutzen.

beim Aufruf von dem Form , ich nenne es mal MakePerson, steht mir folgendes zur verfügung:

private int _gameIndex;
private string _gameType;
private int _personIndex;

_gametype stellt den Spieltyp da: X01Game oder X10020Game,

_personindex stellt den index der Person in der Liste Persons da (ggf. -1 wenn eine neue Person erstellt werden muss),

gameIndex stellt den Index des Game in der Liste X01GameList oder X10020GameList da.

Wie kann ich hier gewährleisten dass der richtige Spieler dem richtigen Spiel zugeordnet wird?


public class MakepersonForm
{
   private int _gameIndex;
   private string _gameType;
   private int _personIndex;

   private Person currentPerson;
   private XGame currentGame;

   protected override void OnNavigatedTo(NavigationEventArgs e)
   {
        _gameIndex = int.Parse(NavigationContext.QueryString["index"]);
        _personIndex = int.Parse(NavigationContext.QueryString["pId"]);
        _gameType = NavigationContext.QueryString["gt"];

        SetCurrentGameAndPerson();
   }

}

Ich hab da an folgdendes Gedacht:


private void SetCurrentGameAndPerson()
        {
            switch (_gameType)
            {
                case "X01Game":
                    currGame = AllGames.X01Games[_gameIndex];
                    //currentPerson = currGame.Persons.ElementAt(_personIndex);
                    break;
                case "X10020":
                    currGame = AllGames.X10020Games[_gameIndex];
                    //currentPerson = currGame.Persons.ElementAt(_personIndex);
                    break;
                default:
                    break;
            }
        }

Klappt aber irgendwie nicht, bin zwar schon am Debuggen, aber sehe nicht so recht was das Problem ist, läuft aber was krum mit den Indexes

Versteht Ihr was ich meine? Wie könnte ich das lösen?

Danke

3.003 Beiträge seit 2006
vor 8 Jahren

"Klappt irgendwie nicht" ist keine Fehlermeldung. Ich vermute mal, dir würde ein Dictionary helfen.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

elTorito Themenstarter:in
177 Beiträge seit 2009
vor 8 Jahren

"Klappt irgendwie nicht" ist keine Fehlermeldung. Ich vermute mal, dir würde ein
>
helfen.
LaTino

Hi,

ja, "irgendwie nicht" ist keine Fehlermeldung , stimmt schon...

habe nun was konkreteres:

Fehlermeldung:
Unhandled Error
Ambiguous match found.Stack Trace: at System.RuntimeType.GetPropertyImpl(String name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)

at System.Type.GetProperty(String name)
at System.Windows.PropertyAccessPathStep.ConnectToPropertyInSource(Boolean isSourceCollectionViewCurrentItem)
at System.Windows.PropertyAccessPathStep.ConnectToProperty()
at System.Windows.PropertyAccessPathStep.ReConnect(Object newSource)
at System.Windows.PropertyPathListener.ReConnect(Object source)
at System.Windows.Data.BindingExpression.SourceAcquired()
at System.Windows.Data.BindingExpression.System.Windows.IDataContextChangedListener.OnDataContextChanged(Object sender, DataContextChangedEventArgs e)
at System.Windows.Data.BindingExpression.DataContextChanged(Object sender, DataContextChangedEventArgs e)
at System.Windows.FrameworkElement.OnDataContextChanged(DataContextChangedEventArgs e)
at System.Windows.FrameworkElement.OnAncestorDataContextChanged(DataContextChangedEventArgs e)
at System.Windows.FrameworkElement.NotifyDataContextChanged(DataContextChangedEventArgs e)
at System.Windows.FrameworkElement.OnAncestorDataContextChanged(DataContextChangedEventArgs e)
at System.Windows.FrameworkElement.NotifyDataContextChanged(DataContextChangedEventArgs e)
at System.Windows.FrameworkElement.OnTreeParentUpdated(DependencyObject newParent, Boolean bIsNewParentAlive)
at System.Windows.DependencyObject.UpdateTreeParent(IManagedPeer oldParent, IManagedPeer newParent, Boolean bIsNewParentAlive, Boolean keepReferenceToParent)
at MS.Internal.FrameworkCallbacks.ManagedPeerTreeUpdate(IntPtr oldParentElement, IntPtr parentElement, IntPtr childElement, Byte bIsParentAlive, Byte bKeepReferenceToParent, Byte bCanCreateParent)

Scheint sich also irgendwas zu "überschneiden"

1.040 Beiträge seit 2007
vor 8 Jahren

An welcher Stelle wird die Exception denn ausgelöst?

elTorito Themenstarter:in
177 Beiträge seit 2009
vor 8 Jahren

An welcher Stelle wird die Exception denn ausgelöst?

Hi,

scheint mir so als würde die Exception ausgelöst beim Zugriff auf die ObservableCollection<PersonX10020>


public class MyXGames
{
        public ObservableCollection<X01Game> X01GameList{}
        public ObservableCollection<X10020Game>X10020GameList{}

public class XGame 
{
        public ObservableCollection<Person> Persons{}
}
public class X01Person :Person{}
public class X10020Person :Person{}

public class X01Game :XGame
{
         public ObservableCollection<PersonX01> Persons{}
}

public class X10020Game :XGame
{
         public ObservableCollection<PersonX10020> Persons{}
}

Zu Testzwecken:


 X10020 demogame = new X10020();
demogame.Name = "Demo";
PersonX10020 person1 = new PersonX10020()
{
   Name = "Peter",
   Gender = "male",
};

AllGames.X10020Games.Add(demogame);
demogame.Persons.Add(person1);

demogame.Persons ist an eine ListBox gebunden

Beim Zugriff auf die Seite wo die ListBox ist, ist demogame.Persons leer.

Scheint sich ins Gehege zu kommen mit XGame.Persons?

Benenne ich XGame.Persons um, ist demogame.Persons leer, mit XGame.Persons, ist demogame.Persons gefüllt, aber es kommt der Fehler.

Danke

1.040 Beiträge seit 2007
vor 8 Jahren

Also die Klassen die du gepostet hast, sind nicht die Klassen die du nutzt, die Namen passen nicht zusammen (dadurch ist es auch sehr schwierig dir zu folgen).

MyGames == AllGames?
X10020GameList == X10020Games?
X10020Game == X10020?
X10020Person == PersonX10020?

Bitte einmal korrigieren...

Weiterhin sollte es ziemlich klar sein, an welcher Stelle eine Exception ausgelöst wird.
Ggf. dazu: [Artikel] Debugger: Wie verwende ich den von Visual Studio?

Funktionieren deine Klassen, wenn du im ersten Schritt auf das Binding verzichtest?

elTorito Themenstarter:in
177 Beiträge seit 2009
vor 8 Jahren

Hi,

ja, sorry, ich versuchs nochmal genauer.

Also ist eine Win Phone App, die Exception kommt bei :


public partial class App : Application
{

      private void Application_Launching(object sender, LaunchingEventArgs e)
      {
          LoadGamesList();
      }

     private void Application_UnhandledException(object sender,  ApplicationUnhandledExceptionEventArgs e)
     {
           string errorString = "Message: " + e.ExceptionObject.Message + "Stack Trace: " + e.ExceptionObject.StackTrace;
     }

}

Klassen:


public class GameStore : INotifyPropertyChanged
{
     public static MyGames AllGames 
        { 
            get
            {
                if (gamesList == null)
                    gamesList = new MyGames();
                return gamesList;
            }
        }
}

public class MyGames : INotifyPropertyChanged
{
     private Games _g = null;        

     public Games Gs
        {
            get
            {
                if (_g == null)
                    _g = new Games();
                return _g;
            }
}

public class Games : INotifyPropertyChanged
{
   public ObservableCollection<X01Game> X01Games{}
   public ObservableCollection<X10020> X10020Games{}
}

public partial class XGame : INotifyPropertyChanged
{
    [DataMember]
     public ObservableCollection<Person> Persons{}
}

public partial class X10020 : XGame, INotifyPropertyChanged
{
    [DataMember]
     public ObservableCollection<PersonX10020> Persons
}

public partial class X01Game : XGame, INotifyPropertyChanged
{
     [DataMember]
     public ObservableCollection<X01Person> Persons{}
}

public class Person : INotifyPropertyChanged
{
}
public partial class X01Person : Person
{
}
public partial class PersonX10020 : Person
{
}


Demo:


 X10020 demogame = new X10020();
demogame.Name = "Demo";

PersonX10020 person1 = new PersonX10020()
{
  Name = "Peter ",
  Gender = "male",
};

GameStore.AllGames.Gs.X10020Games.Add(demogame);
demogame.Persons.Add(person1);

Auf der Seite wo ich binde :


protected override void OnNavigatedTo(NavigationEventArgs e)
{
   currGame = GameStore.AllGames.Gs.X10020Games[_gameIndex];
   this.DataContext = currGame;
}

Nehme ich this.DataContext = currGame; raus kommt der Fehler nicht. Die Persons Liste ist gefüllt.

Ich hatte bei der Anwendung bisher nur eine Klasse Spiel welche eine Persons Liste hatte, war alles gut, nun möchte ich mehrere Spiele mit jeweils einer Personen Liste implementieren. Wenn die Struktur der Klassen so okay ist, dann kann es eigentlich nur sein dass sich irgendwo beim binden noch auf eine "alte" Person Liste zugegriffen wird.

Ambigous heißt ja so viel wie irgendwas ist mehrdeutig.

Hmm Okay. Der Fehler ist weg wenn ich ein [DataMember] weg nehme, 2 x DataMember Persons scheint also Ambigous...

Ich überprüf nochmal die Datenstruktur.... 😁

1.040 Beiträge seit 2007
vor 8 Jahren

Naja, schaue dir mal deinen Aufbau an:

public partial class XGame : INotifyPropertyChanged
{
    [DataMember]
     public ObservableCollection<Person> Persons{}
}

public partial class X10020 : XGame, INotifyPropertyChanged
{
    [DataMember]
     public ObservableCollection<PersonX10020> Persons
}

public partial class X01Game : XGame, INotifyPropertyChanged
{
     [DataMember]
     public ObservableCollection<X01Person> Persons{}
}

Die Basisklasse hat dieselbe Property wie die abgeleiteten Klassen, da kommt er zwangsläufig durcheinander.
In der Hoffnung jetzt nichts falsches zu sagen: Der Compiler weiß beim Zugriff auf X10020.Persons nicht, ob er auf die Property der Klasse X10020 oder XGame zugreifen soll.

Du solltest mit generischen Klassen arbeiten:

public abstract class XGame<TPerson> where TPerson : Person
{
    [DataMember]
     public ObservableCollection<TPerson> Persons{}
}

public class X10020 : XGame<PersonX10020>
{
}

public class X01Game : XGame<X01Person>
{
}

Dadurch ist die Struktur von vornherein klar und typsicher.

Ich vermute aber - ohne dir nahe treten zu wollen - dass du dir nochmal die Grundlagen von C# angucken solltest: [FAQ] Wie finde ich den Einstieg in C#?
Fast alle deine Klassen sind partial, was nicht notwendig ist (vermute ich).
Alle implementieren INotifyPropertyChanged - auch nicht unbedingt notwendig.

Auch ist z.B. die Klasse MyGames auf den ersten Blick überflüssig, du kannst im GameStore gleich eine Instanz der Games-Klasse zurückliefern.

elTorito Themenstarter:in
177 Beiträge seit 2009
vor 8 Jahren

Hi,

ja Grundlagen kann man sich nicht oft genug anschauen 🙂

Partial class brauche ich doch wenn ich eine Klasse erweitere?
Also es gibt eine Person, und eine erweiterte Person (PersonX10020 ) welche die Person um Merkmale erweitert die nur im Spiel X10020 wichtig sind. (Okay ginge wahrscheinlich auch indem man der Person eine Spiel Eigenschaft anhängt oder so)

INotifyPropertyChanged brauch ich (geht wahrscheinlich auch anders...) um meine UI zu aktualisieren, nachdem dies oder das passiert.

MyGames benötige ich aus diversen Gründen um die Klasse Serialisiert zu speichern, ich starte die App, MyGames wird initialisiert, und dann erst werden bei Bedarf Daten aus dem IsolatedStorage geladen.

Ich versuchs mal mit Generics. Hatte jetzt ein "Workaround" aber nicht Typsicher.

Danke für deine Hilfe 👍

1.040 Beiträge seit 2007
vor 8 Jahren

Nein, partielle Klassen benötigst du, wenn du eine Klasse teilen möchtest (z.B. über mehrere .cs-Dateien).

Ableitungen kannst du ganz einfach ohne Schlüsselwörter anlegen:

public class MyBaseClass
{}

public class MyClass : MyBaseClass
{}

Wenn du deine Basisklasse zusätzlich mit dem Schlüsselwort abstract versiehst, kannst du davon keine Instanz anlegen. Das ist in einigen Fällen sinnvoll (z.B. in deinem 😄)

elTorito Themenstarter:in
177 Beiträge seit 2009
vor 8 Jahren

Nein,
>
benötigst du, wenn du eine Klasse teilen möchtest (z.B. über mehrere .cs-Dateien).

Ableitungen kannst du ganz einfach ohne Schlüsselwörter anlegen:

Ja, habe das nun auch nochmal nachgelesen. Sind manchmal so Kleinigkeiten wo man denkt dass ist/muss so, und ist aber Fehlerhaft, und verursacht dadurch weitere Fehler 🙂

Mit der ObservableCollection <T> das funktioniert gut !

Nun Frage ich mich ob ich auch einen "Generischen Context" hinbekomme... Damit ich einfacher auf die Objekte zugreifen kann, welche sich in den Observable Collections befinden.

Auf ein Game greife ich derzeit wie folgt zu:

Im Falle von X01Game


// Da wo der Game Typ nicht bekannt ist , z.b auf der MakePerson.xaml Page
 
private object currGame;
currGame= GameStore.AllGames.Gs.X01Games[_gameIndex]; 

// wenn bekannt , z.B. in der Game Engine
X01Game currentGame = currGame= GameStore.AllGames.Gs.X01Games[_gameIndex]; 

Im Falle von X10020


currGame= GameStore.AllGames.Gs.X10020Games[_gameIndex]; 

Dann die Person(en).


Person currentPerson = GameStore.AllGames.Gs.X01Games[_gameIndex].Persons.ElementAt(_personIndex);

// oder 

Person currentPerson = GameStore.AllGames.Gs.X10020Games[_gameIndex].Persons.ElementAt(_personIndex);

Das ist ja so nicht wirklich "sexy" ... 🙁

Wenn ich auf der MakePerson.xaml Page ankomme, stehen mir die String Parameter "gameType" zur Verfügung ("X01Game" oder "X10020"), wo ich dann mittels Switch momentan die passenden Variablen setze(Generische XAML Page geht glaube ich nicht, sonst würde ich es wie hier machen) :


private object currGame;
private Person currentPerson;

private void SetCurrentGameAndPerson()
        {
            switch (_gameType)
            {
                case "X01Game":
                    currGame = GameStore.AllGames.Gs.X01Games[_gameIndex];
                    if (_personIndex != -1)
                        currentPerson = GameStore.AllGames.Gs.X01Games[_gameIndex].Persons.ElementAt(_personIndex);
                    else
                        currentPerson = new X01Person();
                    break;
                case "X10020":
                    currGame = GameStore.AllGames.Gs.X10020Games[_gameIndex];
                    if (_personIndex != -1)
                        currentPerson = GameStore.AllGames.Gs.X10020Games[_gameIndex].Persons.ElementAt(_personIndex);
                    else
                        currentPerson = new PersonX10020();
                    break;
                default:
                    break;
            }
        }

Wie könnte ich hier auch ein Spiel Generisch machen? Habe die Struktur mal vereinfacht zum Testen:


    public class Games
    {
        public Games() { }

        public ObservableCollection<X01Game> X01Games { get; set; }
        public ObservableCollection<X10020Game> X10020Games { get; set; }
    }


    public class Person
    {
        public string Name { get; set; }
    }
    public class PersonX01 : Person
    {
        public string X01PersonProperty { get; set; }
    }
    public class PersonX10020 : Person
    {
        public string X10020PersonProperty { get; set; }
    }
    

    public abstract class GameBase<TPerson> where TPerson : Person
    {
        public string GameStatus { get; set; }
        public string GameDuration { get; set; }
        public ObservableCollection<TPerson> Persons { get; set; }
    }

    public class X01Game : GameBase<PersonX01>
    {
        public X01Game(){}
        public string X01GameProperty { get; set; }
    }

    public class X10020Game : GameBase<PersonX10020>
    {
        public X10020Game(){}
        public string X10020GameProperty { get; set; }
    }

     public class TestGenerics
    {
        Games _games;
        public TestGenerics()
        {
            Start();
        }

        private void Start()
        {
            _games = new Games();

            _games.X01Games.Add(new X01Game { GameStatus = "finished", GameDuration = "10hrs" });
            _games.X01Games[0].Persons.Add(new PersonX01 { Name = "Peter" });
            _games.X01Games[0].Persons.Add(new PersonX01 { Name = "Paul" });

            _games.X01Games.Add(new X01Game { GameStatus = "Started", GameDuration = "1hrs" });
            _games.X01Games[1].Persons.Add(new PersonX01 { Name = "Julia" });
            _games.X01Games[1].Persons.Add(new PersonX01 { Name = "Jennifer" });


            _games.X10020Games.Add(new X10020Game { GameStatus = "started", GameDuration = "1hrs" });
            _games.X10020Games[0].Persons.Add(new PersonX10020 { Name = "Peter" });
            _games.X10020Games[0].Persons.Add(new PersonX10020 { Name = "Paul" });

            SelectAX01Game(1);
        }

        private void SelectAX01Game(int index)
        {
            X01Game x = _games.X01Games[index];
            Person p = x.Persons.ElementAt(0);

            // Wünschenswert: 
            GenericGame game = _games.X01Games[index];
            Person p = game.Persons.ElementAt(0);
        }
    }

Interface?

Danke

elTorito Themenstarter:in
177 Beiträge seit 2009
vor 8 Jahren

Interface?

Hab den Eindruck das die Lösung aller meiner Probleme meistens an fehlenden Interface liegt... 📗

 public interface IGame
    {
        ObservableCollection<Person> Persons{ get; set; }
    }

 private IGame _currGame = (IGame)GameStorage.AllGames.Gs.X01Games[_gameIndex];

 private IGame _currGame = (IGame)GameStorage.AllGames.Gs.X10020Games[_gameIndex];

Was natürlich nicht geht weil nicht gecastet werden kann und ich es erst zur Laufzeit gemerkt hab ... hehe ..

Schönes Wochenende !