Author Topic: Programming Advice  (Read 10342 times)

0 Members and 1 Guest are viewing this topic.

Offline Steve Walmsley (OP)

  • Aurora Designer
  • Star Marshal
  • S
  • Posts: 11675
  • Thanked: 20470 times
Re: Programming Advice
« Reply #30 on: October 13, 2010, 11:47:34 AM »
OO design question, as I have been VB-corrupted over the last few years :)

On program start, all the ships are loaded into a collection within my MapObjectContainer class. I intended to also add all missiles, system bodies, etc to this same collection. However, I have a function to load the names of all the ships of a particular race into a listbox. Of course, my MapObject idea doesn't hold up so well now because I only need to use one value from some of the Ship objects (derived from MapObject). In Aurora this is simply loaded straight from the DB but my new program I intend to keep DB access to an absolute minimum for performance reasons. So if I cycle though the Directory class that is holding my MapObjects, including Ships, and try to use the following code:

            // loop through every map object
            foreach (KeyValuePair<int,MapObject> objShip in mCol)
            {
                // add a value from the object to the listbox
                lstShip.Items.Add(objShip.Value.ShipName);
            }

It doesn't work because the only directly accessible properties are those of the base class (currently Xcor, Ycor and SystemID). As the collections are reference-based, would it be acceptable OO practice to create multiple Directionaries within the container class. One Dictionary for Ships, one for Missiles, one for SystemBodies, etc. but also combined lists such as a Dictionary containing all MapObjects, or all PoweredObjects. In that way, I could perform operations on a specific subset of MapObjects when I needed to but also access them all in a single foreach loop when that was appropriate too. Or is there a more elegant way to achieve this?

EDIT: Here is the code for the above. It's working fine but I don't want to be an OO heretic :)

Code: [Select]
namespace Damocles
{

    public class MapObject
    {
        // data fields
        public Decimal Xcor;
        public Decimal Ycor;
        public int SystemID;

        // default constructor
        public MapObject()
        {
            Xcor = 0;
            Ycor = 0;
            SystemID = 0;
        }

        // constructor that accepts absolute starting location values
        public MapObject(Decimal X, Decimal Y, int SysID)
        {
            Xcor = X;
            Ycor = Y;
            SystemID = SysID;
        }
    
    }

    public class Ship : MapObject
    {

        // data fields
        public int ShipID;
        public int ShipClassID;
        public int PoliticalID;
        public string ShipName;
        public decimal Mass;

        // default constructor
        public Ship()
        {
        }

        // constructor
        public Ship(int intShipID, string strShipName, int intPoliticalID, Decimal X, Decimal Y)
        {
            Xcor = X;
            Ycor = Y;
            ShipID = intShipID;
            ShipName = strShipName;
            PoliticalID = intPoliticalID;
        }

        // renames the ship
        public void Rename(string strShipName)
        {
            if (strShipName.Length > 0)      
                ShipName = strShipName;
        }

    }

    // container class for MapObjects
    public class MapObjectContainer
    {

        // create an instance of the Dictionary class to hold all the MapObjects
        Dictionary<int, MapObject> MapObjectList = new Dictionary<int, MapObject>();
        Dictionary<int, Ship> ShipList = new Dictionary<int, Ship>();

        // default constructor
        public MapObjectContainer()
        {
        }

        // function to load ships from database
        public void LoadDBShips()
        {
            // create an instance of the table adaptor for the Ship table
            DamoclesDataSetTableAdapters.ShipTableAdapter ShipTable = new DamoclesDataSetTableAdapters.ShipTableAdapter();

            // this code creates a new data table by calling the get data function of the table adaptor
            DamoclesDataSet.ShipDataTable ShipData = ShipTable.GetDataByGame(1);

            foreach (DamoclesDataSet.ShipRow ShipRow in ShipData)
            {
                Ship objShip = new Ship();
                objShip.ShipID = ShipRow.ShipID;
                objShip.ShipName = ShipRow.ShipName;
                objShip.PoliticalID = ShipRow.PoliticalID;
                objShip.ShipClassID = ShipRow.ShipClassID;
                objShip.Mass = ShipRow.Mass;
                objShip.Xcor = ShipRow.XCor;
                objShip.SystemID = ShipRow.SystemID;
                objShip.Ycor = ShipRow.YCor;

                // add a reference to the ship object to both the ship-specific collection and the overall MapObject collection
                MapObjectList.Add(objShip.ShipID, objShip);
                ShipList.Add(objShip.ShipID, objShip);
            }
        }

        // populate a listbox with ship information, based on PoliticalID
        public void PopulateShipList(ListBox lstShip, int PoliticalID)
        {
            // loop through every ship object
            foreach (KeyValuePair<int,Ship> objShip in ShipList)
            {
                // add a value from the object to the listbox
                if (objShip.Value.PoliticalID == PoliticalID)
                    lstShip.Items.Add(objShip.Value.ShipName);
            }
        }
    }
}

Steve
« Last Edit: October 13, 2010, 12:16:46 PM by Steve Walmsley »
 

Offline ndkid

  • Warrant Officer, Class 1
  • *****
  • n
  • Posts: 86
  • Thanked: 4 times
Re: Programming Advice
« Reply #31 on: October 13, 2010, 11:52:20 AM »
I assume that C# programs are setup by default to run in \ProjectDirectory\bin\debug and that is where it expects to find the DB. I could possibly have a DB in a sub-directory but not in a higher directory. What I really need to be able to do is change the datasource from the DB in \ProjectDirectory to the DB in the \ProjectDirectory\bin\debug directory so I don't end up maintaining two versions of the same DB. At the moment though I can't work out how to do that :)

So, a couple of things.
1) It's not that they're set up to run there by default, per se; it's that you're probably running through the IDE (I'm guessing), and it's probably set by default to its "Debug" configuration which, also by default, builds and compiles and executes from bin\debug. All of those are changable options at the project level.
2) You can reference up from the root directory... "..\" would mean to find the file in \bin, and "..\..\" would send it to the project directory. You could use those to go all the way back out to your initial drive, I'd wager, and then mine down to wherever you want. I still suspect you could put a full path where the file name is, as well.
 

Offline ndkid

  • Warrant Officer, Class 1
  • *****
  • n
  • Posts: 86
  • Thanked: 4 times
Re: Programming Advice
« Reply #32 on: October 13, 2010, 12:12:15 PM »
OO design question, as I have been VB-corrupted over the last few years :)

On program start, all the ships are loaded into a collection within my MapObjectContainer class. I intended to also add all missiles, system bodies, etc to this same collection. However, I have a function to load the names of all the ships of a particular race into a listbox. Of course, my MapObject idea doesn't hold up so well now because I only need to use one value from some of the Ship objects (derived from MapObject). In Aurora this is simply loaded straight from the DB but my new program I intended to keep DB access to an absolute minimum for performance reasons. So if I cycle though the Directory class that is holding my MapObjects, including Ships, and try to use the following code:

            // loop through every map object
            foreach (KeyValuePair<int,MapObject> objShip in mCol)
            {
                // add a value from the object to the listbox
                lstShip.Items.Add(objShip.Value.ShipName);
            }

It doesn't work because the only directly accessible properties are those of the base class (currently Xcor, Ycor and SystemID). As the collections are reference-based, would it be acceptable OO practice to create multiple Directionaries within the container class. One Dictionary for Ships, one for Missiles, one for SystemBodies, etc. but also combined lists such as a Dictionary containing all MapObjects, or all PoweredObjects. In that way, I could perform operations on a specific subset of MapObjects when I needed to but also access them all in a single foreach loop when that was appropriate too. Or is there a more elegant way to achieve this?

Steve

It's certainly true that you could have an object that tracks each type of object separately. And, if the objects share behavior, it probably still makes sense to have a class hierarchy to them. It's also true that, because we're mostly storing references to objects, you could have a MapObject dictionary that contains a ship #2 and also have a ship dictionary that contains ship #2. (Obviously, if ship #2 gets blown up, you'd have to make sure you clean it out of all the necessary lists.)

I'm not sure I have enough data on what you're picturing to give a strong suggestion of what sort of design pattern to use here. My knee-jerk inclination is that, if you have a single MapObject collection, and it hands back mapobjects when asked based on the ID, if you know you're looking for ship #2, and you go ask the mapObjectcollection for MapObject #2, it's perfectly acceptable to then cast that object as a ship and use its ship-specific behaviors. Then, the collection doesn't have to care that it's a ship... just the thing that wants to treat it in a ship-like way does.
 

Offline sloanjh

  • Global Moderator
  • Admiral of the Fleet
  • *****
  • Posts: 2805
  • Thanked: 112 times
  • 2020 Supporter 2020 Supporter : Donate for 2020
    2021 Supporter 2021 Supporter : Donate for 2021
Re: Programming Advice
« Reply #33 on: October 13, 2010, 02:39:19 PM »
Found it, although it just has the DB set up as the root directory, which the program thinks is bin\debug. I assume that C# programs are setup by default to run in \ProjectDirectory\bin\debug and that is where it expects to find the DB. I could possibly have a DB in a sub-directory but not in a higher directory. What I really need to be able to do is change the datasource from the DB in \ProjectDirectory to the DB in the \ProjectDirectory\bin\debug directory so I don't end up maintaining two versions of the same DB. At the moment though I can't work out how to do that :)

Steve
Try messing around with the project properties for the project that has your main() in it.  You should find ways to e.g. set the execution path etc.

John
 

Offline Steve Walmsley (OP)

  • Aurora Designer
  • Star Marshal
  • S
  • Posts: 11675
  • Thanked: 20470 times
Re: Programming Advice
« Reply #34 on: October 17, 2010, 05:15:30 AM »
I have started a new project file as I have decided to use WPF instead of Windows Forms. Might as well learn everything at once :).

I have imported my class code from the initial Windows Forms project but the error checker doesn't like TableAdapters. Is that because they are not used in WPF applications or am I just missing a reference somewhere?

Steve
 

Offline ndkid

  • Warrant Officer, Class 1
  • *****
  • n
  • Posts: 86
  • Thanked: 4 times
Re: Programming Advice
« Reply #35 on: October 17, 2010, 07:01:06 AM »
I have imported my class code from the initial Windows Forms project but the error checker doesn't like TableAdapters. Is that because they are not used in WPF applications or am I just missing a reference somewhere?

My first guess: a missing "using" reference in one of the codebehinds. Did you bring the TableAdapter implementation file over directly? If so, what namespace was it using for your table adapter? Is that namespace being used by whatever is creating an instance of that table adapter?
 

Offline Steve Walmsley (OP)

  • Aurora Designer
  • Star Marshal
  • S
  • Posts: 11675
  • Thanked: 20470 times
Re: Programming Advice
« Reply #36 on: October 17, 2010, 07:09:31 AM »
The book I am reading at the moment (Visual C# Step by Step by John Sharp) says Don't Use Hungarian Notation. I was just wondering why. Most C# examples I have seen use a variable name format that starts with lower case and has any other words starting with a capital, such as starSystem or systemBody that don't indicate the data type. I am much more comfortable with strPlanetName or intSystemID, or if I was going to avoid a data type prefix I would simply use StarSystem or SystemBody. The wiki entry on Hungarian notes that it was highlighted in Charles Petzold's "Programming Windows" book. This was my Bible for several years so I guess that must be where I picked it up. I was just wondering why it seems so out of favour now and why the lack of capitalization for variable names

I have also ordered "Pro WPF in C# 2010: Windows Presentation Foundation in .NET 4" by Matthew MacDonald so I will be starting to work my way through that as well.

Steve
 

Offline Steve Walmsley (OP)

  • Aurora Designer
  • Star Marshal
  • S
  • Posts: 11675
  • Thanked: 20470 times
Re: Programming Advice
« Reply #37 on: October 17, 2010, 07:16:25 AM »
My first guess: a missing "using" reference in one of the codebehinds. Did you bring the TableAdapter implementation file over directly? If so, what namespace was it using for your table adapter? Is that namespace being used by whatever is creating an instance of that table adapter?

The missing using reference was my first thought too but I compared them and they were all the same. My class definitions weren't being picked up either as declarations in the form code were ignoring class definitions in the class files. Something seriously wrong. I have created another new project :) and I will add files normally and copy some of the text rather than transferring whole files. WPF is very different to Windows forms so I am going to work through some books on the client before getting back into the data access.

Steve
 

Offline ndkid

  • Warrant Officer, Class 1
  • *****
  • n
  • Posts: 86
  • Thanked: 4 times
Re: Programming Advice
« Reply #38 on: October 17, 2010, 01:05:35 PM »
The book I am reading at the moment (Visual C# Step by Step by John Sharp) says Don't Use Hungarian Notation. I was just wondering why. Most C# examples I have seen use a variable name format that starts with lower case and has any other words starting with a capital, such as starSystem or systemBody that don't indicate the data type. I am much more comfortable with strPlanetName or intSystemID, or if I was going to avoid a data type prefix I would simply use StarSystem or SystemBody. The wiki entry on Hungarian notes that it was highlighted in Charles Petzold's "Programming Windows" book. This was my Bible for several years so I guess that must be where I picked it up. I was just wondering why it seems so out of favour now and why the lack of capitalization for variable names

So, a few different philosophical principals are going on here, I think, and probably not all of them are applicable to you.

One is readability-to-others. Most programmers are working in environments where they're not the only developer on a project, so there's a need for different developers to be able to share conventions. For those who don't have that as the case, a lion's share are in situations where, when they leave, someone else is going to have to pick up that code base. As a result, there's a certain democracy to naming conventions. Cocoa, as I've been made to understand by my developer friends who use it, has a community which has developed a sense of "best practices" when it comes to naming convention and usage that tries very hard to be intuitive and consistent.

C#, in particular, has a bit of a muddled life, because it's a endpoint for developers from drastically different backgrounds. The "standard" VB6 habits I noticed when I was doing VB tended to differ greatly from the way-too-shorthand notations that C developers tended to prefer, both of which looked rather different from the semi-Hungarian (that you seem to like) that was most common with C++ developers who'd grown up with MFC and/or COM, which was different still from the it-doesn't-matter-what-the-object-is mentality that came to C++ distilled from the Java folks. Now, C# tends to be the language *all* of those folks (well, except the blue-blood java folks) filter into eventually. (And don't get me started with the code that people who were weaned on old-style ASP write...)

The root of the anti-Hungarian positions, as I understand them, is that naming a variable with a mnemonic attached to a particular Class/Interface is bad for a few particular reasons. It makes the code less readable (in part by making variable names longer). Intellisense has made it less necessary (in a world where you can always alt-space to see what you can call on an object, you don't need the visual prompts).
Perhaps most importantly, it makes it harder to refactor. This is especially true of the various interface glue. Something starts as a checkbox. Then it becomes a radio button. Then it becomes a combo box. In each of those cases, the variable would have to be renamed, or the variable now becomes a mine that will blow up in the face of people who trust it. This is even worse when you design controls that are used in multiple places that expose their members... if you have a form with a
Code: [Select]
public Checkbox ChkAutosave {get;set;}

and you decide to make it a radiobutton, now not only do you have to make that decision inside that form class, you have to go though the same decision process with all of the external files that consume that form. Whereas, if you just have

Code: [Select]
public Checkbox Autosave {get;set;}

changing it from a Checkbox to a radiobutton is far less problematic. (In fact, this example could be improved further, because there's often a push to pass objects via the least necessary interface/class possible, so something lower-level than Checkbox would probably be better to expose, or just the piece of data within the checkbox that actually wants to be accessed.)

For myself, I've had many cases, especially when writing game-logic code, where I've wanted to minimize memory footprints for an object, so I tend to start by assuming that my numeric values can be bytes for any unsigned integer values, shorts for anything signed, and floats for anything rational. Then, as I design, some of those variables end up needing greater range, and get changed upward to ushorts, ints, etc. I'd have to do more find-replace work if I Hugarian-ized those variables, and, frankly, in 95% of the cases that I'm interacting with those variables, their size is irrelevant. (After all, when I start manipulating them, I'm either going to have to check for overflow or assume there is none, or put the operation result in a larger variable type.)
 

Offline sloanjh

  • Global Moderator
  • Admiral of the Fleet
  • *****
  • Posts: 2805
  • Thanked: 112 times
  • 2020 Supporter 2020 Supporter : Donate for 2020
    2021 Supporter 2021 Supporter : Donate for 2021
Re: Programming Advice
« Reply #39 on: October 17, 2010, 11:59:00 PM »
The book I am reading at the moment (Visual C# Step by Step by John Sharp) says Don't Use Hungarian Notation. I was just wondering why. Most C# examples I have seen use a variable name format that starts with lower case and has any other words starting with a capital, such as starSystem or systemBody that don't indicate the data type. I am much more comfortable with strPlanetName or intSystemID, or if I was going to avoid a data type prefix I would simply use StarSystem or SystemBody. The wiki entry on Hungarian notes that it was highlighted in Charles Petzold's "Programming Windows" book. This was my Bible for several years so I guess that must be where I picked it up. I was just wondering why it seems so out of favour now and why the lack of capitalization for variable names

I have also ordered "Pro WPF in C# 2010: Windows Presentation Foundation in .NET 4" by Matthew MacDonald so I will be starting to work my way through that as well.

Steve
Here's a post from Joel (On Software) that presents his take on the Hungarian issue.  Note that if you mouse-over a variable (IIRC), Intellisense will tell you the type (if this doesn't work, you can right-click then go to definition), plus the auto-conversions are a lot stricter in C# so Intellisense will also tell you if you try to use a variable incorrectly.

As for books, the two that I like are Hejlsberg's C# book http://www.amazon.com/C-Programming-Language-3rd/dp/0321562992 (I prefer the 3rd edition, since it breaks the new stuff out in a separate section, unfortunately it doesn't have the 4.0 stuff) and (as far as naming conventions etc. go) the Framework Design Guidelines book from the .NET folks http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321246756

I'm not sure where the convention comes from, but a fairly standard naming convention is to use PascalCase for public methods, properties, and classes, camelCase for variables, and a _leadingUnderscore for data members (or sometimes private members - data or function).  IIRC the Framework book discusses this, except they don't like the underscore.  I also disagree with them on when interfaces vs. abstract base classes should be used - I already described my philiosphy in another post.

One more thing - "refactor/rename" is a WONDERFUL feature of the IDE - it makes renames a lot easier and more robust.

John
 

Offline sloanjh

  • Global Moderator
  • Admiral of the Fleet
  • *****
  • Posts: 2805
  • Thanked: 112 times
  • 2020 Supporter 2020 Supporter : Donate for 2020
    2021 Supporter 2021 Supporter : Donate for 2021
Re: Programming Advice
« Reply #40 on: October 18, 2010, 12:26:23 AM »
OO design question, as I have been VB-corrupted over the last few years :)

On program start, all the ships are loaded into a collection within my MapObjectContainer class. I intended to also add all missiles, system bodies, etc to this same collection. However, I have a function to load the names of all the ships of a particular race into a listbox. Of course, my MapObject idea doesn't hold up so well now because I only need to use one value from some of the Ship objects (derived from MapObject). In Aurora this is simply loaded straight from the DB but my new program I intend to keep DB access to an absolute minimum for performance reasons. So if I cycle though the Directory class that is holding my MapObjects, including Ships, and try to use the following code:
*SNIP*

Sorry - I didn't see this one when it first went by.  Two comments on OO orthodoxy (or my view of it :-) ):

1)  If you find yourself writing a switch statement, you're doing something non-OO.  The function that gets executed inside the switch statement should probably be a method of an interface (i.e. polymorphic).

2)  If you find yourself writing a lot of functions that have the same code with different types of objects, then you're probably doing something non-OO.  I'm not a big fan of XP (too extreme :-) ), but there's a principle called "Once and Only Once" that is supposed to drive refactorings - when you find yourself writing the same code in two different places, you should do a refactoring so that the shared lines of code show up in a single construct.  The benefit of this is that the third time you need to do it, you'll typically be able to take advantage of the shared code by defining a new class type, rather than by copying the code.

If your particular case, the thing that I suspect is a little stinky (in the "code smells" sense) is that you're going to have a bunch of dictionaries that all look very similar in terms of the way that they're used, but which you have explicitly enumerated.

I just re-read what you want to do, and it sounds to me like you want to do is have certain interfaces (e.g. IMapObject, or IShip) have a dictionary as a static data member, and have each object which implements such an interface register with the dictionary so that it can be looped over (so a ship would register both with IMapObject and with IShip).  This sounds reasonable to me, but it runs into a slight technical flaw in the language - unfortunately (IIRC) C# doesn't allow static members of an interface (tightly coupled to not allowing static virtuals).  This is because of an unfortunate interpretation of "virtual" as meaning "late-bound" on the part of the designers - this leads them to not understand why people are asking for it.  If you follow the pattern I mentioned before of having an "A"(bstract) class for every "I"(nterface), then you can do this in the base class.

Let me know if you want more specific advice on this one (you can PM me if you want) and I can think it through more carefully - probably by rattling off some sample code :-)

John
 

Offline Steve Walmsley (OP)

  • Aurora Designer
  • Star Marshal
  • S
  • Posts: 11675
  • Thanked: 20470 times
Re: Programming Advice
« Reply #41 on: October 22, 2010, 06:40:11 AM »
New problem :)

I spent all morning trying to figure out the C# / WPF equivalent of the following code in VB6:

    lstFleet.AddItem rsFleet!FleetName
    lstFleet.ItemData(lstFleet.NewIndex) = rsFleet!FleetID

In the above, rsFleet is a DAO recordset object that contains the result of a query and lstFleet is a listbox. ItemData is a property associated with each listbox entry that allows you to look up based on key rather than value. After two hours of searching online I realised there was no equivalent and figured out that databinding a WPF listbox to a C# collection was the modern way to handle this.

So now I have an object called FleetsList which contains the Fleet data from the database as well as various methods to manipulate that information. Within the FleetsList object is a Dictionary collection called Fleets that holds the actual data. So my code is as follows:

            FleetsList.LoadFleets();
            lstFleet.ItemsSource = FleetsList.Fleets;
            lstFleet.DisplayMemberPath = "FleetName";
            lstFleet.SelectedValuePath = "FleetID";
            MessageBox.Show(lstFleet.Items.Count.ToString());

The Fleet information is loaded without any problems and the Fleets Dictionary object is populated. I set the ItemsSource for the lstFleets Listbox to the Dictionary object and also set the DisplayMemberPath to indicate what should appear in the listbox and the SelectedValuePath to FleetID. Unfortunately, the listbox is blank. However, it does contain the items because the messagebox at the end is showing the correct number of fleets. Also when I add a breakpoint to the following code, I can use debug to look at a selected item by clicking on the listbox and all the data for the entire Fleet record is there. The line fails though due to lstFleet.SelectedValue being null.

        private void lstFleet_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            MessageBox.Show(lstFleet.SelectedValue.ToString());
        }

So my question is, what I am doing wrong? The compiler is happy to accept the syntax but for some reason the DisplayMemberPath and SelectedValuePath are not being set? Also, is there anyway to filter what is displayed in the Listbox so that certain fleets can be excluded, on the basis of RaceID for example.

The relevant line of XAML for the above listbox is:

        <ListBox DataContext="{Binding}" Height="187" ItemsSource="{Binding}" Margin="754,196,27,116" Name="lstFleet" Width="120" SelectionChanged="lstFleet_SelectionChanged" />

Help!!

EDIT: I have managed to display something by overriding the ToString function of Fleet. However, instead of returning just the FleetName it returns in the format of: [8842, Cargo Task Group], where 8842 is the FleetID. No idea why.

        public override string ToString()
        {
            return this.FleetName;
        }

EDIT2: If I remove the override function and also remove the calls to DisplayMemberPath and SelectedValuePath I get the listbox populated with the [8842, Cargo Task Group] format. The overidden function was being called as I put a breakpoint on it but it would seem it is being ignored in favour of the base class implementation for some reason. Now even more confused.

EDIT3: If I put the override back in but replace the FleetName return with "Hello World", the listbox is populated with [8842, Hello World]. I guess the default ouput of a listbox linked to a Dictionary collection must be a KeyValue pair and I am only overriding the Value part. Perhaps there is another function I can overwrite to prevent that but I am reluctant to do so in case I screw up the returning of KeyValue pairs when I really need them. Experimentation contines...

EDIT4: lstFleet.SelectedValuePath = "Key"; instead of lstFleet.SelectedValuePath = "FleetID" successfully returns the FleetID when I select one of the listbox items. Using lstFleet.DisplayMemberPath = "Value" instead of lstFleet.DisplayMemberPath = "FleetName" results in a listbox filled with "Diaspora.Fleet" where Diaspora is the name of the project.

EDIT5: I have tried a data template in XAML instead. Still getting the output of a list of "Diaspora.Fleet". I assume the syntax of Binding Path=Value is the problem. What do I need to use in its place? Really frustrating morning.

        <ListBox DataContext="{Binding}" Height="187" ItemsSource="{Binding}" Margin="637,196,27,116" Name="lstFleet" SelectionChanged="lstFleet_SelectionChanged" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=Value}"></TextBlock>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

Steve
« Last Edit: October 22, 2010, 08:53:27 AM by Steve Walmsley »
 

Offline ndkid

  • Warrant Officer, Class 1
  • *****
  • n
  • Posts: 86
  • Thanked: 4 times
Re: Programming Advice
« Reply #42 on: October 22, 2010, 09:33:00 AM »
New problem :)

Okay, the problem here is that you're giving the listbox the dictionary named Fleets. As a result, the listbox can only access the member properties of Dictionary, not the member properties of a Fleet object. If you instead use FleetsList.Fleets.Values, that might get you closer to where you're trying to go, though the ordering may not be what you want. In your place, I'd probably be tempted to structure the code differently, with FleetLists being a class that inherits from the normal dictionary and can then expose the keys and the values differently than the default Dictionary implementation.

When you made "Value" the SelectedValuePath, that meant the type that had ToString called on it was the type of the value of the Dictionary: in this case, Fleet. What you want to do is add an additional function to fleet:

Code: [Select]
public override string ToString()
{
    return this.FleetName;
}

At that point,

Code: [Select]
           FleetsList.LoadFleets();
            lstFleet.ItemsSource = FleetsList.Fleets;
            lstFleet.DisplayMemberPath = "Value";
            lstFleet.SelectedValuePath = "Key";
            MessageBox.Show(lstFleet.Items.Count.ToString());

should probably work. But really, this feels like a bit of a shoehorn. Out of curiosity, do you tend to write design docs or anything, or do you just sort of code as you go? If the latter, do you tend to refactor your data structure as you change what they're expected to do?


EDIT: probably even more readable would be:
Code: [Select]
            FleetsList.LoadFleets();
            lstFleet.ItemsSource = FleetsList.Fleets.Values;
            lstFleet.DisplayMemberPath = "FleetName";
            lstFleet.SelectedValuePath = "FleetId";
            MessageBox.Show(lstFleet.Items.Count.ToString());
« Last Edit: October 22, 2010, 09:38:22 AM by ndkid »
 

Offline Steve Walmsley (OP)

  • Aurora Designer
  • Star Marshal
  • S
  • Posts: 11675
  • Thanked: 20470 times
Re: Programming Advice
« Reply #43 on: October 22, 2010, 09:58:47 AM »
Okay, the problem here is that you're giving the listbox the dictionary named Fleets. As a result, the listbox can only access the member properties of Dictionary, not the member properties of a Fleet object. If you instead use FleetsList.Fleets.Values, that might get you closer to where you're trying to go, though the ordering may not be what you want. In your place, I'd probably be tempted to structure the code differently, with FleetLists being a class that inherits from the normal dictionary and can then expose the keys and the values differently than the default Dictionary implementation.

When you made "Value" the SelectedValuePath, that meant the type that had ToString called on it was the type of the value of the Dictionary: in this case, Fleet. What you want to do is add an additional function to fleet:

Code: [Select]
public override string ToString()
{
    return this.FleetName;
}

At that point,

Code: [Select]
           FleetsList.LoadFleets();
            lstFleet.ItemsSource = FleetsList.Fleets;
            lstFleet.DisplayMemberPath = "Value";
            lstFleet.SelectedValuePath = "Key";
            MessageBox.Show(lstFleet.Items.Count.ToString());

should probably work. But really, this feels like a bit of a shoehorn. Out of curiosity, do you tend to write design docs or anything, or do you just sort of code as you go? If the latter, do you tend to refactor your data structure as you change what they're expected to do?

I had already tried the above, with the overidden function (see EDIT above), or at least I thought so. I just coded it up again and it worked! I get the list of fleet names. Perhaps I hadn't tried the combination of the overriden function and using Value and Key instead of FleetName and FleetID.


Quote
EDIT: probably even more readable would be:
Code: [Select]
            FleetsList.LoadFleets();
            lstFleet.ItemsSource = FleetsList.Fleets.Values;
            lstFleet.DisplayMemberPath = "FleetName";
            lstFleet.SelectedValuePath = "FleetId";
            MessageBox.Show(lstFleet.Items.Count.ToString());

This option doesn't work. I get the blank listbox again, although as before the items are in there and you can select them. I think the reason I hadn't got it working previously was that I hadn't tried the overidden function at the same time as I tried Value and Key.

Thanks for your help though! Now the next question is how I exclude certain items from the list, or more accurately only include those matching a set of criteria, and how do I order them? Should I do the sorting and selecting in code and then link the listbox to a temporary collection?

Steve
 

Offline Steve Walmsley (OP)

  • Aurora Designer
  • Star Marshal
  • S
  • Posts: 11675
  • Thanked: 20470 times
Re: Programming Advice
« Reply #44 on: October 22, 2010, 10:03:21 AM »
Quote
Out of curiosity, do you tend to write design docs or anything, or do you just sort of code as you go? If the latter, do you tend to refactor your data structure as you change what they're expected to do?

I code as I go and have the overall design in my head. Not exactly the way I would work in a group though :). I will rewrite large chunks of code midway through a project if required and refactor the data structures as necessary. For example, last year I spent four months completely rewriting the movement, combat and detection code to use collections in memory rather than regular data access.

One of the reasons for the code as I go approach is I will often have new ideas halfway through a project and rewrite large sections on the fly :)

Steve