ListBox forcing update of items - c#

I created a ListBoxItem where I have a property Name and override ToString() to give back name. That works nicely when I add new items.
But now I need to force the ListBox to update the labels when I change the name of my ship. I thought Refresh or Update would do that but that doesn't work.
I might be missing something very easy here.
public class ShipListBoxItem
{
public ListBox Parent { get; set; }
public ShipType Ship { get; set; }
public ShipListBoxItem()
{
Ship = new ShipType();
}
public ShipListBoxItem(ShipType st)
{
Ship = st;
}
public override string ToString()
{
return Ship.Name;
}
public void UpdateListBox()
{
Parent.Refresh(); //My problem is here. Update doesn't work either.
}
public static ShipListBoxItem AddToListBox(ListBox lb, ShipType ship)
{
ShipListBoxItem li = new ShipListBoxItem(ship);
li.Parent = lb;
lb.Items.Add(li);
return li;
}
}

If you use a List<T> as the DataSource for the listbox it is pretty easy to have changes to items show up. It also means there is no real reason to have a special class for adding a ShipListBoxItem to a ListBox, your basic Ship class may work:
class ShipItem
{
public enum ShipTypes { BattleShip, Carrier, Destroyer, Submarine, Frigate };
public ShipTypes Ship { get; set; }
public string Name { get; set; }
public ShipItem(string n, ShipTypes st)
{
Name = n;
Ship = st;
}
public override string ToString()
{
return String.Format("{0}: {1}", Ship.ToString(), Name);
}
}
The form related stuff:
private void Form1_Load(object sender, EventArgs e)
{
// add some ships
Ships = new List<ShipItem>();
Ships.Add(new ShipItem("USS Missouri", ShipTypes.BattleShip));
Ships.Add(new ShipItem("USS Ronald Reagan", ShipTypes.Carrier));
lb.DataSource = Ships;
}
private void button1_Click(object sender, EventArgs e)
{
// change a ship name
lb.DataSource = null; // suspend binding
this.Ships[0].Name = "USS Iowa";
lb.DataSource = Ships; // rebind
lb.Refresh();
}
As an alternative, you can also tell the Listbox to use a specific property for the display using DisplayMember:
lb.DataSource = Ships;
lb.DisplayMember = "Name";
This would use the Name property in the listbox instead of the ToString method. If your list is changing a lot, use a BindingList instead. It will allow changes to the list show up in the ListBox as you add them without toggling the DataSource.

Try this
ListBox.RefreshItems()
msdn
EDIT: You can use an extended class like this:
public class FooLisBox : System.Windows.Forms.ListBox
{
public void RefreshAllItems()
{
RefreshItems();
}
}
private void button1_Click(object sender, EventArgs e)
{
(listBox1.Items[0] as ShipListBoxItem).Ship.Name = "AAAA";
listBox1.RefreshAllItems();
}

I managed to solve my problem.
Mostly, thanks Jose M.
I ran into a problem however. RefreshItems() triggers OnSelectedIndexChanged()
so my overridden class looks like this
public class MyListBox : ListBox
{
public bool DoEvents{ get; set; } // Made it public so in the future I can block event triggering externally
public MyListBox()
{
DoEvents = true;
}
public void RefreshAllItems()
{
SuspendLayout();
DoEvents = false;
base.RefreshItems(); // this triggers OnSelectedIndexChanged as it selects the selected item again
DoEvents = true;
ResumeLayout();
}
// I only use this event but you can add all events you need to block
protected override void OnSelectedIndexChanged(EventArgs e)
{
if (DoEvents)
base.OnSelectedIndexChanged(e);
}
}

Related

Cannot add children to a Grid in WPF

There are two ways that I tried to add children to a Grid. One is working but it is not prefered to me because I have ViewModel class. And I want to apply it in that class. So the another way is not working correctly, actually, it is not added as children to Grid. The last way is applied in ViewModel class. Here is two ways that I applided:
1- Working way.
public LaserLib fiber1 = new LaserLib();
private void MarkInit()
{
fiber1.Init(); // that is my specialized laser library.
myGrid.Children.Add(fiber1); // when I do this, I can see the fiber1 condition in grid.
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
MarkInit();
}
But, I have many of this and I want to appy to my viewModel.
What I tried:
2- Not worked. Way 1:
public class ViewModels
{
public ObservableCollection<MarkingManualStatuses> markingManualStatuses { get; set;}
= new ObservableCollection<MarkingManualStatuses(Enumerable.Range(0,4).Select(i => new MarkingManualStatuses()));
}
public class MarkingManualStatuses : INotifyPropertyChanged
{
public LaserLib Tmc { get; set; }
public void TmcInitialize()
{
Tmc = new LaserLib ();
Tmc.Init();
}
}
I run TmcInitialize() function at MainWindow_Onloaded
Everything is fine for now. My LaserLib gets initialize. it works. But, I cannot add it to grid.
Here is where I tried to add at ManualPage:
private void listViewMarkingInfos_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
TmcViewerGrid.Children.Clear();
int index = listViewMarkingInfos.SelectedIndex;
ContainerMarkingDockPanel.DataContext = viewModels.markingManualStatuses[index];
TmcViewerGrid.Children.Add(viewModels.markingManualStatuses[index].Tmc); // this is not working
ContainerMarkingDockPanel.Visibility = Visibility.Visible;
}
3. Not worked. Way 2:
I have added
Grid property
in
MarkingManualStatuses
It sounds bad but anyway, I just wanted to see if it works or not.
In viewModel:
public class MarkingManualStatuses : INotifyPropertyChanged
{
public LaserLib Tmc { get; set; }
public Grid myGrid {get;set;}
public void TmcInitialize()
{
Tmc = new LaserLib ();
Tmc.Init();
myGrid = new Grid();
myGrid.Children.Add(Tmc);
}
}
In Manual_Page:
private void listViewMarkingInfos_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
TmcViewerGrid.Children.Clear();
int index = listViewMarkingInfos.SelectedIndex;
ContainerMarkingDockPanel.DataContext = viewModels.markingManualStatuses[index];
TmcViewerGrid = viewModels.markingManualStatuses[index].myGrid; // this is also not working.
ContainerMarkingDockPanel.Visibility = Visibility.Visible;
}
I have tried two ways but it didn't work. Any suggestions to solve this?
And maybe I missed a small point that I forget, I am not sure, but Thank you all.

Winform how to remove Item from combobox using SelectedValue?

public void Fill()
{
cmb1.DataSource = dt;
cmb1.DisplayMember = "Name";
cmb1.ValueMember = "ID";
}
public void Remove()//by Text
{
string selectedItem ="Jack";
cmb1.Items.Remove(selectedItem );
}
public void Remove()//by Value
{
string selectedvalue ="10";
cmb1.Items.RemoveAt(selectedvalue);
}
This Code used but not worked.
Not Remove Using Value or Text.Or Any Other Methods to fill combobox without DataSource.
You can do the following because you are using a data source:
DataTable dt = (DataTable)cmb1.DataSource;
dt.Rows.RemoveAt(cmb1.SelectedIndex);
Try something like this:
class Student {
public int ID{get;set;}
public string Name { get; set; }
// You override this method depending in what you want to show on the comboBox as text
public override string ToString()
{
return Name;
}
}
private void fillComboBox(List<Student> students)
{
// We manually fill the comboBox Items; The displayed text is going to be define by the ToString() method this object has define
students.ForEach(x => cmb1.Items.Add(students));
}
private void button1_Click(object sender, EventArgs e)
{
// We can remove the items by just doing this
cmb1.Items.Remove(cmb1.SelectedItem);
}

How we can refresh items text in ListBox without reinserting it?

I have the class TestClass that has ToString overriden (it returns Name field).
I have instances of TestClass added into ListBox and at certain point I need to change Name of one of this instances, how then I can refresh it's text in ListBox?
using System;
using System.Windows.Forms;
namespace TestListBox
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
listBox1.Items.Add(new TestClass("asd"));
listBox1.Items.Add(new TestClass("dsa"));
listBox1.Items.Add(new TestClass("wqe"));
listBox1.Items.Add(new TestClass("ewq"));
}
private void button1_Click(object sender, EventArgs e)
{
((TestClass)listBox1.Items[0]).Name = "123";
listBox1.Refresh(); // doesn't help
listBox1.Update(); // same of course
}
}
public class TestClass
{
public string Name;
public TestClass(string name)
{
this.Name = name;
}
public override string ToString()
{
return this.Name;
}
}
}
try
listBox1.Items[0] = listBox1.Items[0];
I have encountered this same issue and tried all sorts of different ways to tr y to get the displayed text of an item to actually reflect the underlying item value.
After going through all the available properties I found this to be the simplest.
lbGroupList.DrawMode = DrawMode.OwnerDrawFixed;
lbGroupList.DrawMode = DrawMode.Normal;
It triggers the appropriate events within the control to update the displayed text.
Your Testclass needs to implement INotifyPropertyChanged
public class TestClass : INotifyPropertyChanged
{
string _name;
public string Name
{
get { return _name;}
set
{
_name = value;
_notifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void _notifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public TestClass(string name)
{
this.Name = name;
}
public override string ToString()
{
return this.Name;
}
}
However this only works if you use Columns that do not rely on the ToString() but bind the property Name
This can be done by altering your code:
somewhere in class declare
BindingList<TestClass> _dataSource = new BindingList<TestClass>();
In initializeComponent write
listBox1.DataSource = _dataSource;
Then do all operations on _dataSource instead of Listbox.
You could use a BindingList:
items = new BindingList<TestClass>( );
listBox1.DataSource = items;
listBox1.DisplayMember = "_Name";
Then to refresh the list call:
items.ResetBindings( );
edit: Also don't forget to create a get Property for Name
public string _Name
{
get { return Name; }
set { Name= value; }
}
I use the following code:
public static void RefreshItemAt (ListBox listBox, int itemIndex)
{
if (itemIndex >= 0)
{
Rectangle itemRect = listBox.GetItemRectangle(itemIndex);
listBox.Invalidate(itemRect);
listBox.Update();
}
}

c# - StackOverFlowException when mentioning ListBoxt?

I'm trying to get a new form (FormAlbum) to open when I click buttonOpenAlbum and have an item selected in the AlbumListBox.
If I just have this in buttonOpenAlbum_Click:
private void buttonOpenAlbum_Click(object sender, EventArgs e)
{
FormAlbum MusicForm = new FormAlbum(this);
MusicForm.ShowDialog();
}
The new from opens without error. However, as soon as I mention "AlbumListBox.SelectedItem" (as in the code belowin Form FormMain), I get a "StackOverflowException was unhandled" at:
public ListBox AlbumListBox
{
get
{ // <-This bracket here is where the error highlights
I don't understand why I'm getting this error, only that it must have something to do with AlbumListBox. What am I doing wrong? Any help is appreciated, thank you.
Form FormMain:
public FormMain()
{
InitializeComponent();
}
private void buttonAddAlbum_Click(object sender, EventArgs e)
{
FormAlbumAC addAlbumForm = new FormAlbumAC(this);
addAlbumForm.ShowDialog();
}
private void buttonOpenAlbum_Click(object sender, EventArgs e)
{
if (AlbumListBox.SelectedItem != null)
{
MessageBox.Show(AlbumListBox.SelectedItem.ToString());
FormAlbum MusicForm = new FormAlbum(this);
MusicForm.ShowDialog();
}
else
{
MessageBox.Show("You need to select an album from the list to open.");
}
}
public static class PublicVars
{
public static List<Album> AlbumList { get; set; }
static PublicVars()
{
AlbumList = new List<Album>(MAX_ALBUMS);
}
}
public ListBox AlbumListBox
{
get
{
return AlbumListBox;
}
}
Look at your property implementation:
public ListBox AlbumListBox
{
get
{
return AlbumListBox;
}
}
It's just calling itself, recursively. It may be easier to see that if we convert it to a method:
public ListBox GetAlbumListBox()
{
return GetAlbumListBox();
}
That's why you've got an overflow. It's not clear what you meant it to do... where did you expect the value to come from? You probably need a variable to back the property. What did you expect to set the value returned?
I'd also strongly discourage the design of the PublicVars class. Aside from the naming, you're basically using global variables - not a good idea. Work out which classes need access to the data, and how to get that data to them appropriately.

Want to avoid default zero value

I have the a class in my application. It has been bound to winform textbox controls. But the textbox which is bound to BookingNo property, always shows zero (0). But i want the textbox keep empty. Is there any way to do it? Here is my code snippet.
public class Booking
{
private int pBookingNo;
private string pCustomerName;
private string pAddress;
public int BookingNo
{
get { return pBookingNo; }
set
{
if (!value.Equals(pBookingNo))
{
pBookingNo = value;
}
}
}
public string CustomerName
{
get { return pCustomerName; }
set
{
if (!value.Equals(pCustomerName))
{
pCustomerName = value;
}
}
}
public Booking() { }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
AddDataBindings();
}
private void AddDataBindings()
{
bsBooking.DataSource = typeof(Booking);
txtBookingNo.DataBindings.Add("Text", bsBooking, "BookingNo", true, DataSourceUpdateMode.OnPropertyChanged, null, "G", GlobalVariables.CurrentCultureInfo);
txtCustomerName.DataBindings.Add("Text", bsBooking, "CustomerName");
}
}
The default value of an Integer is 0, so you have to wrap it into some other object, which supports values other than 0, like
public int? BookingNo { get; set; }
You can use Nullable Type
public int? pBookingNo
{
get;
set;
}
Link : http://msdn.microsoft.com/fr-fr/library/1t3y8s4s(v=vs.80).aspx
You could use custom formatting for the binding by adding a handler to the Format event (http://msdn.microsoft.com/en-us/library/system.windows.forms.binding.format.aspx) and return an empty string when the value is zero. But you wouldn't be able to tell whether the value is actually zero or it just hasn't been set already, in which case using the int? approach suggested by #Grumbler85 is better.
what´s about:
textBox1.BindingContextChanged += new EventHandler(BindingContext_Changed);
private void BindingContext_Changed(object sender, EventArgs e)
{
TextBox txtBox = (TextBox)sender;
if (txtBox.Text == "0"){
txtBox.Text = "";
}
}
don´t know if it works, just an idea.

Categories

Resources