View object properties values on selection of Listbox items

Viewed 178

I have a base class, Item, and 3 derived classes: Weapon, Shield, Armor.

I've created a listbox and populated it with all three of the derived classes.

My question is, how can I access the properties values of the class when I select it in the list box? I've tried:

dynamic weapon = Convert.ChangeType(item, typeof(Weapon));

But weapon doesn't give me access to the properties values. I see the same intellisense options as if I do:

var item = LstStoreItems.SelectedItem;

The goal I'm trying to accomplish is selecting an object from the list box, and populating some labels based upon the object selected.

EXAMPLE

A Weapon has a Name, Attack, Type, and Cost. A Shield has Name, Defense, Material, and Cost. Whenever I select a Weapon, I want to populate labels on my form to display this information to the user. When I select a Shield, I want to display that information.

4 Answers

if blocks with method overloading maybe helpful here

private void SelectedItemChangedHandler()
{
    var item = LstStoreItems.SelectedItem;

    if (item is Weapon) SetLabelsFor(item as Weapon);
    else if (item is Shield) SetLabelsFor(item as Shield);
    else if (item is Armor) SetLabelsFor(item as Armor);
    else ClearLabels();
}

private void SetLabelsFor(Weapon weapon)
{
    label1.Text = weapon.Name;
    // etc.
}

private void SetLabelsFor(Shield shield)
{
    label1.Text = shield.Name;
    // etc.
}

private void SetLabelsFor(Armor armor)
{
    label1.Text = armor.Name;
    // etc.
}

Can you use something like :

private void SelectOneItemBasedOnType(){
var selected=TheListBox.SelectedItem;
//Check for example, if the item is a shield.
if(selected is Shield){
Shield shield=(Shield) selected;
MessageBox.Show(shield.cost);
}
//Etc
}

I am not confident on what the best approach for this may be. I am picturing a “master-detail” type scenario. It is unclear how “varied” the different inheritors may be, and looping through the properties may be a challenge depending on how “different” they are between the inheritors.

Given this, one possible solution is to make Item an abstract class and create an abstract method to “force” the inheritors to implement this. Each inheritor of Item will need to implement a SetItemPropertiesLabels method. An example of how it may work… let’s say there is a Panel next to the ListBox of Items. This panel holds all the labels that describe the currently selected Item. When the list box selection changes, the panels current labels are cleared, then the selected Items SetPanelLabels method is called. There the labels and text for the Item properties are set on the panel. Then the “required” SetItemPropertiesLabels(panel) method is called to add the selected items individual properties.

Using this approach, the SelectedIndexChanged event may look something like…

private void listBox1_SelectedIndexChanged(object sender, EventArgs e) {
  Item selectedItem = (Item)listBox1.SelectedItem;
  ItemPanel.Controls.Clear();
  selectedItem.SetPanelLabels(ItemPanel);
}

Item Class

public abstract class Item {
  public string ItemName { get; set; }
  public string Cost { get; set; }

  public Item() {
    ItemName = "";
  }

  public Item(string itemName, string cost) {
    ItemName = itemName;
    Cost = cost;
  }

  public void SetPanelLabels(Panel panel) {
    Label lbl = new Label();
    lbl.AutoSize = true;
    lbl.Text = "Item Name: " + ItemName + "   Cost: " + Cost;
    lbl.Location = new System.Drawing.Point(10, 10);
    panel.Controls.Add(lbl);
    SetItemPropertiesLabels(panel);
  }

  // This is the method that all inheritors must implement.
  public abstract void SetItemPropertiesLabels(Panel panel);
}

Armour class

class Armour : Item {
  public string Type { get; set; }
  public string Material { get; set; }

  public Armour() {
  }

  public Armour(string itemName, string cost) : base(itemName, cost) {
  }

  public override void SetItemPropertiesLabels(Panel panel) {
    Label lbl = new Label();
    lbl.AutoSize = true;
    lbl.Text = "Armour Type: " + Type + "  Material: " + Material;
    lbl.Location = new System.Drawing.Point(15, 50);
    panel.Controls.Add(lbl);
  }
}

Shield Class

class Shield : Item {
  public string Defense { get; set; }
  public string Material { get; set; }

  public Shield(string itemName, string cost) : base(itemName, cost) {
  }

  public override void SetItemPropertiesLabels(Panel panel) {
    Label lbl = new Label();
    lbl.AutoSize = true;
    lbl.Text = "Shield Defense: " + Defense + "  Material: " + Material;
    lbl.Location = new System.Drawing.Point(15, 50);
    panel.Controls.Add(lbl);
  }
}

Weapon class

class Weapon : Item {

  public string Attack { get; set; }
  public string Type { get; set; }

  public Weapon() {
  }

  public Weapon(string itemName, string cost) : base(itemName, cost) {
  }

  public override void SetItemPropertiesLabels(Panel panel) {
    Label lbl = new Label();
    lbl.AutoSize = true;
    lbl.Text = "Weapon Type: " + Type + "  Attack: " + Attack;
    lbl.Location = new System.Drawing.Point(15, 50);
    panel.Controls.Add(lbl);
  }
}

Hope this makes sense.

What worked for me in the end is the following:

        if (!(LstPlayerItems.SelectedItem is Item item)) return;

        if (item.GetType() == typeof(Weapon) && item is Weapon weapon)
        {
            LblAttDef.Content = weapon.MinimumDamage + " to " + weapon.MaximumDamage;
            LblAttDefLabel.Content = "Attack";
        }

This now allows me to reference the Weapon properties on the object weapon and accomplish what I was trying for.

Related