Removing databound item from listbox causes refresh problems - c#

I have databound a listbox to a simple custom object collection. Next, I added a button to remove the selected item from the object collection. The problem is that when certain items are removed and the listbox is showing the vertical scroll bar, the scrollbar appears to reset to a new position, although what I really think is happening is that the control is repainting.
The folowing code sample demonstrates the problem. Add this code to a form, making sure that the vertical scrollbar appears. Select an item in the middle of the collection so that the scrollbar is centered and press the remove button. When the control repaints, the items and scrollbar are in a different position. I would like for the listbox to behave as it would with non-databound items. Am I better off not using databinding, or is there a solution that allows me to keep the contol bound?
Thanks.
public partial class Form1 : Form
{
private BindingList<ItemData> m_bList = new BindingList<ItemData>();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i < 50; i++)
{
m_bList.Add(new ItemData("Name " + i.ToString(), i));
}
this.listBox1.DisplayMember = "Name";
this.listBox1.DataSource = m_bList;
}
private void btnRemove_Click(object sender, EventArgs e)
{
m_bList.Remove(listBox1.SelectedItem as ItemData);
}
}
public class ItemData
{
public string Name { get; set; }
public int Position { get; set; }
public ItemData(string name, int position)
{
Name = name;
Position = position;
}
}

You need to preserve the TopIndex property of the listbox when removing the item. Preserving SelectedIndex does not stop the scrollbar from jumping. The code below does what I think you want.
private void btnRemove_Click(object sender,EventArgs e)
{
int topIndex = listBox1.TopIndex;
m_bList.Remove(listBox1.SelectedItem as ItemData);
if(listBox1.Items.Count>topIndex)
listBox1.TopIndex = topIndex;
}

I can think of one way to dampen the error (note this might not be the most accurate solution) . I just added a few things to the button click event. I am not sure if they solve your requirements completely since you would be the best judge of that, but nonetheless here you go.
private void btnRemove_Click(object sender, EventArgs e)
{
int s = listBox1.SelectedIndex;
m_bList.Remove(listBox1.SelectedItem as ItemData);
listBox1.Refresh();
listBox1.SelectedIndex = s;
}

Related

How to bind an object to the label in c# windows forms

I have labels with footballer's names inside. I want to get footballer's age after clicking on these labels. I do it by this way:
public partial class Form1 : Form
{
Footballer[] team = { /*team initialization*/};
public Form1()
{
InitializeComponent();
}
private void OnLabel_Click(object sender, EventArgs e)
{
for (int i = 0; i < team.Length; i++)
{
if (team[i].Name == this.Text)
{
MessageBox.Show(team[i].Age.ToString());
break;
}
}
}
}
But there is problem. There might be more than one player with the same name. So I want to bind each label with footballer. How can I do this?
For sake of simplicity suppose that you initialize your labels in this mode....
public Form1()
{
InitializeComponent();
int index = 0;
foreach(Label lbl in this.Controls.OfType<Label>())
{
lbl.Text = team[index].Name;
// Make the Tag property reference the Footballer instance
// used to set the label text with the footballer name
lbl.Tag = team[index];
index++;
}
At this point, when you receive the click event, you just need to retrieve the reference from the Tag property and use it directly
private void OnLabel_Click(object sender, EventArgs e)
{
// No loop needed here
Label current = sender as Label;
Footballer player = current.Tag as Footballer;
if(player != null)
MessageBox.Show(player.Age.ToString());
}
}
You should separate out your business logic from your presentation logic. The name is what you presented, but to keep each player/team unique then assign a unique ID.
This is then assigned to the label but hidden from view, so that when the label is clicked the ID is retrieved and you can then do a lookup based on this.
So with Player class like the following:
public class Player
{
public int ID { get;set; }
public string Name { get;set; }
//etc.
}
Then when assigning a Player to label use Label.Tag which is a general purpose field which you can use for anything your want. (Available on all Controls).
label1.Text = MyPlayer.Name;
label1.Tag = MyPlayer.ID;
If I was you, I would also change your Teams to be a List not an array
List<Footballer> team = new List<Footballer>() { /*team initialization*/};
Then you can look up as follows
private void OnLabel_Click(object sender, EventArgs e)
{
Label clickedLabel = (sender as Label);
int id = Convert.ToInt32(clickedLabel.Tag);
Footballer found = team.Find(x => x.Id == id);
MessageBox.Show(found.Age.ToString());
}

C# Share listview items across pages

Beginner here.
I have a list on my MainPage that I am adding items to by typing stuff into a textbox submitting it. After creating a listview item, I would like to be able to click on an item and see its value in another page.
I created a DetailsPage for this, but I don't know what the best way is to share that data across these pages.
This is what I have in my MainPage so far:
public sealed partial class MainPage : Page
{
public int itemCounter;
public MainPage()
{
this.InitializeComponent();
//This is the button that submits the entered text
public void btnSubmitInPopup_Click_1(object sender, RoutedEventArgs e)
{
//Create new listview item and textblock
ListViewItem item = new ListViewItem();
TextBlock txtBloodSugarValue = new TextBlock();
//Save text input value to label and add item to list
txtBloodSugarValue.Text = txtInput.Text;
item.Content = txtBloodSugarValue;
mainListView.Items.Add(item);
//Reset text input field
txtInput.Text = "";
//Update counter of items in list
itemCounter = mainListView.Items.Count();
lblItemCounter.Text = itemCounter.ToString();
}
}
My DetailPage literally has just one label that I am trying to populate with whatever value is in the listview item that I clicked on in my MainPage.
What's the best way to do this?
Hope this makes sense.
Thanks so much.
You have 3 way to do that:
declaring static variable and set that in first form and use in second form(or anywhere!)
Create public field/propeey inside second form class and set it from first form
Pass value as parameter to constructor/function of second class
I add second form class here:
public static int StaticVariable { get; set; }//First Method
public int PublicProperty { get; set; }
public Form2(int Value)
{
InitializeComponent();
//Do your code here with constructor way here
}
public Form2()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
//Do your code for 1,2 here
}
public void SetValueWithFunction(int value)
{
//Do your code for setting value with second type in Number 3
}
I hope this helps :)

Add buttons Dynamically at Runtime

I want to add buttons dynamically on c sharp windows form. the no of buttons should be equal the no of records available in data table & i want to display the record whose button is clicked. could anybody help me?
In your case you need to create user control which will represent your item record on UI, create constructor which asspt your item and public event in this user control and add to your container like this.
myPanel.Controls.Add(new ItemRecordUserControl(item));
Probably you will need to use some specific containers instead of regular panel, something like System.Windows.Forms.FlowLayoutPanel.
User control will looks like:
public partial class ItemRecorUserControl : UserControl
{
public event EventHandler<EventArgs> ActionButtonClicked;
public void OnActionButtonClicked(object sender, EventArgs e)
{
if (this.ActionButtonClicked != null)
this.ActionButtonClicked(sender, e);
}
public ItemRecorUserControl()
{
InitializeComponent();
}
public ItemRecorUserControl(ItemRecord item) : this()
{
// fill item data here to controls
}
private void btnAction_Click(object sender, EventArgs e)
{
this.OnActionButtonClicked(sender, e);
}
}
you can add buttons like this :
for (int i = 0; i < YourDataTableItemsCount; i++)
{
Button b = new Button();
b.Left = //Calculate Left
b.Top = //Calculate Top
b.Parent = this;
//Or
this.Controls.Add(b);
}

How can I take items from a listbox and display them in a listview in another form?

I have a listbox full of items for my order.
I want to take all of the items inside my listbox and transfer them into my listview.
Then I want to take my listview and display it in another form (my messagebox).
My new listview:
private void CustomerInfo_Click(object sender, EventArgs e)
{
ListViewItem customers = new ListViewItem(fullName.Text);
customers.SubItems.Add(totalcount.ToString());
customers.SubItems.Add(total.ToString());
customers.SubItems.Add(Address.Text);
customers.SubItems.Add(telephone.Text);
for (int i = 0; i < OrderlistBox.Items.Count; i++)
{
customers.SubItems.Add(OrderlistBox.Items[i].ToString());
}
Customers.Items.Add(customers);
//CLEAR ALL FIELDS
OrderlistBox.Items.Clear();
fullName.Text = "";
Address.Text = "";
telephone.Text = "";
totalDue.Text = "";
totalItems.Text = "";
}
My contextMenuStrip, so when I click on the customer I can get its info (name, address, order, etc.):
private void customerInformationToolStripMenuItem_Click(object sender, EventArgs e)
{
if (Customers.SelectedItems.Count != 0)
{
var myformmessagedialog = new MessageBoxForm
{
name = Customers.SelectedItems[0].SubItems[0].Text,
address = Customers.SelectedItems[0].SubItems[3].Text,
telephone = Customers.SelectedItems[0].SubItems[4].Text,
};
myformmessagedialog.ShowDialog();
}
}
My new form, the messagebox where I will display all the info for the client:
public partial class MessageBoxForm : Form
{
public MessageBoxForm()
{
InitializeComponent();
}
public string name;
public string address;
public string telephone;
public ListViewItem order = new ListViewItem();
private void MessageBoxForm_Load(object sender, EventArgs e)
{
lblName.Text = name;
lbladdress.Text = address;
lbltelephone.Text = telephone;
orderListView.Items.Add(order);
}
}
I'm sorry if this seems confusing but I'm just looking for help to go in the right direction. Any help is appreciated.
One way to do this is to put the data that you want to display in some sort of ViewModel, basically a class or set of classes that has the data that you want to display. Then the main form can display it, and you can pass a reference to that ViewModel to the message box and it can display it as well.
In general you want to avoid any kind of code that directly ties controls from different forms together.
The easiest way based on your current setup is to simply pass your list view data across to your MessageBoxForm e.g.
public partial class MessageBoxForm : Form
{
...
public void LoadListView(ListViewItemCollection items)
{
orderListView.Clear();
orderListView.AddRange(items);
}
}
....
private void customerInformationToolStripMenuItem_Click(object sender, EventArgs e)
{
if (Customers.SelectedItems.Count != 0)
{
var myformmessagedialog = new MessageBoxForm
{
name = Customers.SelectedItems[0].SubItems[0].Text,
address = Customers.SelectedItems[0].SubItems[3].Text,
telephone = Customers.SelectedItems[0].SubItems[4].Text,
};
myformmessagedialog.LoadListView(Customers.Items);
myformmessagedialog.ShowDialog();
}
}
Basic answer is you don't.
You maintain a collection of items (whatever they are).
You display them in a list box.
You display them in a list view.
If you want say select some from the list box and only move them to the list view.
Then you use the listbox selection to find them in your collections of items, create a list of selected ones then passs that to the form with the listview to display.
Don't use UI controls to store your data and try really hard to never make one form's UI directly dependant on another.
I'm guessing what you'd need (and I could have misunderstood what you are looking for) is a new method in you MessageBoxForm to pass in your Customers object:
private void customerInformationToolStripMenuItem_Click(object sender, EventArgs e)
{
if (Customers.SelectedItems.Count != 0)
{
var myformmessagedialog = new MessageBoxForm;
myformmessagedialog.Customers = Customers;
if (myformmessagedialog.ShowDialog() == DialogResult.OK)
{
Customers = myformmessagedialog.Customers;
}
}
}
If so, simply modify your class to be something like this:
public partial class MessageBoxForm : Form
{
public MessageBoxForm()
{
InitializeComponent();
}
private void MessageBoxForm_Load(object sender, EventArgs e)
{
if (Customers != null)
{
// add your code here to add your Customers as needed
}
}
public Customers Customers { get; set; }
}
To access anything from the parent form you need to pass it to the child form so
myformmessagedialog.ShowDialog();
becomes
myformmessagedialog dialog = new myformmessagedialg(this);
dialog.ShowDialog();
and your class constructor becomes this:
public MessageBoxForm(myformmessagedialog parent){
name=parent.fullName.Text;
address=parent.address.Text;
...etc...
InitializeComponent();
}
Though it might be better to just pass in the name, address, etc rather than the whole form, this way is nice for while you are changing things because you have one less place to change to add another variable to pass.

Handling double click events on ListBox items in C#

I am trying to do something when double clicking an item in a ListBox. I have found this code for doing that
void listBox1_MouseDoubleClick(object sender, MouseEventArgs e)
{
int index = this.listBox1.IndexFromPoint(e.Location);
if (index != System.Windows.Forms.ListBox.NoMatches)
{
MessageBox.Show(index.ToString());
//do your stuff here
}
}
However, when i click on an item, the event isn't fired. The event is fired if i click in the ListBox below all the items.
I set the DataSource property of the ListBox to IList<MyObject>.
Any ideas?
Tried creating a form with a ListBox with MouseDown and DoubleClick events. As far as I can see, the only situation, when DoubleClick won't fire, is if inside the MouseDown you call the MessageBox.Show(...). In other cases it works fine.
And one more thing, I don't know for sure, if it is important, but anyway. Of course, you can get the index of the item like this:
int index = this.listBox1.IndexFromPoint(e.Location);
But this way is fine as well:
if (listBox1.SelectedItem != null)
...
Works for me, so I assume there might be something about the items in the list (custom? intercepting the event?) or the event is not properly wired up.
For example try this (complete Form1.cs):
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
namespace WindowsFormsApplication1 {
public class MyObject {
public MyObject(string someValue) {
SomeValue = someValue;
}
protected string SomeValue { get; set; }
public override string ToString() {
return SomeValue;
}
}
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
var list = new List<MyObject> {
new MyObject("Item one"), new MyObject("Item two")
};
listBox1.DataSource = list;
}
private void listBox1_DoubleClick(object sender, EventArgs e) {
Debug.WriteLine("DoubleClick event fired on ListBox");
}
}
}
With the designer source file (Form1.Designer.cs) containing this:
private void InitializeComponent() {
this.listBox1 = new System.Windows.Forms.ListBox();
... // left out for brevity
this.listBox1.DoubleClick += new System.EventHandler(this.listBox1_DoubleClick);
As a test, create a new Forms base application through the templates, then add just the ListBox and define a class MyObject. See whether you observe the same or a different behavior.
Thank you for all replies. It now works. I solved it, like 26071986 said, with handling double click in the MouseDown handler by checking if e.Clicks is 1. If so, I call DoDragDrop, if not, I call the method that handles double click.
private void MouseDown_handler(object sender, MouseEventArgs e)
{
var listBox = (ListBox) sender;
if (e.Clicks != 1)
{
DoubleClick_handler(listBox1.SelectedItem);
return;
}
var pt = new Point(e.X, e.Y);
int index = listBox.IndexFromPoint(pt);
// Starts a drag-and-drop operation with that item.
if (index >= 0)
{
var item = (listBox.Items[index] as MyObject).CommaDelimitedString();
listBox.DoDragDrop(item, DragDropEffects.Copy | DragDropEffects.Move);
}
}
Here's what I used in the MouseDoubleClick event.
private void YourMethodForDoubleClick(object sender, MouseButtonEventArgs e)
{
Type sourceType = e.OriginalSource.GetType();
if (sourceType != typeof(System.Windows.Controls.TextBlock)
&& sourceType != typeof(System.Windows.Controls.Border))
return;
//if you get here, it's one of the list items.
DoStuff();
...
}
John: then it works. But i figured out that the event isn't fired because I am also handling the MouseDown event. I tried to remove the MouseDown handling, and then it works. Is there a smooth way to handle both those events? If not, I just have to find some other way to catch a double click through the MouseDown event.

Categories