简体   繁体   中英

How to update text in a listview without clearing it

I am making a hack for a game that reads all of the enemies information (location, health, etc.) and stores it accordingly in a listview. I am using this method currently:

        listView1.Items.Clear();
        string[] arr = new string[6];
        ListViewItem item;

        for (int i = 0; i < engine.maxPlayers; i++)
       {
           engine.enemy[i].readInfo(i);
        arr[0] = i.ToString()
        arr[1] = engine.enemy[i].name.ToString();
        arr[2] = engine.enemy[i].getRank(csgo.enemy[i].compRank);
        arr[3] = engine.enemy[i].Wins.ToString();
        arr[4] = engine.enemy[i].health.ToString();
        arr[5] = engine.enemy[i].armor.ToString();

        item = new ListViewItem(arr);
        listView2.Items.Add(item);
        }

This is being done every 200 ms to assure real time information. This works to an extent. It does show everything correctly. However, it has 2 big flaws. 1: it flickers constantly when it clears and rewrites the data. 2: If my listview is only 10 columns long and i need to read data for 20 players, i can't scroll down to see the last 10 players because each time it clears, it resets the scroll bar position.

So is it possible to ONLY update the text of a specific text? Say i only want to update enemy[3]'s health and leave the rest. Can this be done? I don't need to redraw some information like wins and rank because they won't be changing during the game.

Yes, you can use the indexer to directly reference the item you wish to update. For example:

listView2.Items[playerIndex] = new ListViewItem(arr);

As for accomplishing this relative to your game engine, you'll want to use the Reconciliation Design Pattern, which involves the following given game engine list (Master), and list view (Target):

  1. Create a temporary of items or keys in Target. Call this ToDelete.
  2. Step through Master, add any entries that aren't in Target, update those that are. Delete from ToDelete for each match to Target.
  3. Delete from Target all items remaining in ToDelete.

Update: as Slai mentions, you can update via the SubItems member if you don't want to even replace an entire row.

I believe you have to first initially fill the list view with the data from the first call, and then every 500ms-1000ms (I think that 200ms offers nothing), refresh the list view with fresh data.
Something like:

        private void InitialPopulation()
        {
            var listOfItems = new List<ListViewItem>();

            for (int i = 0; i < engine.maxPlayers; i++)
            {
                engine.enemy[i].readInfo(i);

                var item = new ListViewItem(i.ToString());
                item.Name = engine.enemy[i].name;
                item.SubItems.Add(engine.enemy[i].name);
                item.SubItems.Add(engine.enemy[i].getRank);
                item.SubItems.Add(engine.enemy[i].Wins);
                item.SubItems.Add(engine.enemy[i].health);
                item.SubItems.Add(engine.enemy[i].armor);

                listOfItems.Add(item);
            }

            listView1.BeginUpdate();
            listView1.Items.Clear();
            listView1.Items.AddRange(listOfItems.ToArray());
            listView1.EndUpdate();
        }

        private void RefreshData()
        {
            listView1.BeginUpdate();
            var listOfItems = new List<ListViewItem>();
            var playersNames = new List<string>();
            var itemsNames = new List<string>();


            for (int i = 0; i < engine.maxPlayers; i++)
            {
                engine.enemy[i].readInfo(i);

                playersNames.Add(engine.enemy[i].name);

                var items = listView1.Items.Find(engine.enemy[i].name, false);

                switch (items.Length)
                {
                    case 1: // update
                        items[0].SubItems[0].Text = engine.enemy[i].name;
                        items[0].SubItems[1].Text = engine.enemy[i].getRank;
                        items[0].SubItems[2].Text = engine.enemy[i].Wins;
                        items[0].SubItems[3].Text = engine.enemy[i].health;
                        items[0].SubItems[4].Text = engine.enemy[i].armor;
                        break;

                    case 0: // add
                        var item = new ListViewItem(i.ToString());
                        item.Name = engine.enemy[i].name;
                        item.SubItems.Add(engine.enemy[i].name);
                        item.SubItems.Add(engine.enemy[i].getRank);
                        item.SubItems.Add(engine.enemy[i].Wins);
                        item.SubItems.Add(engine.enemy[i].health);
                        item.SubItems.Add(engine.enemy[i].armor);
                        listOfItems.Add(item);
                        break;
                }
            }


            if (listOfItems.Count > 0)
                listView1.Items.AddRange(listOfItems.ToArray());

            foreach (ListViewItem item in listView1.Items)
                itemsNames.Add(item.Name);


            // check if there are more listview items than data.
            if (itemsNames.Count > playersNames.Count)
                foreach (var name in itemsNames.Except(playersNames))
                    listView1.Items.RemoveByKey(name);

            listView1.EndUpdate();
        }

I assumed that engine.enemy[i].name is unique for each palyer.

Maybe there are some errors in the code since I have not written anything for winforms for a long time, but I think you should get the meaning.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM