Search button losing datada - winforms

Afternoon,
I have a search button which searches the list of the users is either by IDnumber,Username or department.
I have 2 list view which the users clicks and it load the selected user to the second list view. now the problem is after u clicked users from listview1 and u went to type ID,Username or select department and click search...It clears all the users you selected and bring new users u where searching.
I want to be able to select the users and be able to search without losing my current selected users from the listview
my code ;
//search button
private void Searchbutton_Click(object sender, EventArgs e)
{
try
{
listView1.Items.Clear();
string sID = string.IsNullOrEmpty(txtUserID.Text) ? null : txtUserID.Text;
string sDepartment;
if (cboDepartment.SelectedItem != null)
{
sDepartment = cboDepartment.SelectedItem.ToString();
}
else
{
sDepartment = "";
}
oConnection = new SqlConnection(_connectionString);
oCommand = new SqlCommand(#"Select us.sFieldValue5, u.sUserName, d.sName, TB_USER_CUSTOMINFO.sFieldValue2
From TB_USER u(nolock)
left join [TB_USER_CUSTOMINFO] us(nolock) on us.nUserIdn = u.nUserIdn
left join TB_USER_CUSTOMINFO on u.nUserIdn = TB_USER_CUSTOMINFO.nUserIdn
left join TB_USER_DEPT d(nolock) on d.nDepartmentIdn = u.nDepartmentIdn
where u.sUserName like '%'+ISNULL(#UserName,u.sUserName)+'%'
and us.sFieldValue5 = isnull(#IDNumber,us.sFieldValue5)
and d.sDepartment like '%'+isnull(#Department,d.sDepartment)+'%'", oConnection);
oCommand.Parameters.AddWithValue("UserName", string.IsNullOrEmpty(txtUsername.Text) ? DBNull.Value : (object)txtUsername.Text);
oCommand.Parameters.AddWithValue("IDNumber", string.IsNullOrEmpty(txtUserID.Text) ? DBNull.Value : (object)txtUserID.Text);
oCommand.Parameters.AddWithValue("Department", string.IsNullOrEmpty(sDepartment) ? DBNull.Value : (object)sDepartment);
oConnection.Open();
oDataset = new System.Data.DataSet();
SqlDataReader oReader = oCommand.ExecuteReader();
while (oReader.Read())
{
ListViewItem item1 = new ListViewItem(oReader[1].ToString());
item1.SubItems.Add(oReader[2].ToString());
item1.SubItems.Add(oReader[3].ToString());
item1.SubItems.Add(oReader[0].ToString());
listView1.Items.AddRange(new ListViewItem[] {item1});
}
oReader.Close();
oConnection.Close();
}
//selected users
private void listView1_ItemChecked(object sender, ItemCheckedEventArgs e)
{
if (e.Item.Checked == true)
{
ListViewItem l = listView1.Items[e.Item.Index];
int i = l.SubItems.Count;
string sValue1 = l.SubItems[1].Text;
string sValue2 = l.SubItems[2].Text;
string sValue3 = l.SubItems[3].Text;
ListViewItem item1 = new ListViewItem(l.SubItems[0].Text.ToString());
item1.SubItems.Add(sValue3);
item1.SubItems.Add(sValue2);
item1.SubItems.Add(sValue1);
listView2.Items.AddRange(new ListViewItem[] { (ListViewItem)l.Clone() });
}
else if (e.Item.Checked == false)
{
ListViewItem l = listView1.Items[e.Item.Index];
foreach (ListViewItem i in listView2.Items)
{
if (i.SubItems[0].Text == l.SubItems[0].Text.ToString())
{
listView2.Items.Remove(i);
}
}
}
}

ListView remember which ListViewItems were checked. As soon as you say Items.Clear(), all that knowledge is lost.
To solve your problem, you will need to remember which users were checked, do your refresh, then go through and mark each user as checked again.
To do those steps, you really need to separate your model from your view. Create a User model object to hold the actual data, then populate your ListView with data from your model. When the user does another search, update or refresh your model objects, then rebuild your view.
Doing this model/view separation is a little more work up front, but future generations of maintenance programmers will bless your name.
BTW, ObjectListView -- an open source wrapper around a .NET ListView -- makes creating a fully functional ListView from a list of model objects trivially easy.

Related

DataGridViewComboBoxCell Value Not Updating When Set Programmatically C# Winforms

I've read all the similiar posts about this darn control (DataGridViewComboBoxCell) and setting it's value programmatically but implementing all the suggestions hasn't worked. I could probably change the UI to get around this problem but I don't like to be beat!
private void PopulateAreaForRoleAssociation()
{
// If businessRoleList is null then no data has been bound to the dgv so return
if (businessRoleList == null)
return;
// Ensure businessArea repository is instantiated
if (businessAreaRepository == null)
businessAreaRepository = new BusinessAreaRespository();
// Get a reference to the combobox column of the dgv
DataGridViewComboBoxColumn comboBoxBusinessAreaColumn = (DataGridViewComboBoxColumn)dgvBusinessRole.Columns["BusinessArea"];
// Set Datasource properties to fill combobox
comboBoxBusinessAreaColumn.DisplayMember = "Name";
comboBoxBusinessAreaColumn.ValueMember = "Id";
comboBoxBusinessAreaColumn.ValueType = typeof(Guid);
// Fill combobox with businessarea objects from list out of repository
comboBoxBusinessAreaColumn.DataSource = businessAreaRepository.GetAll();
// loop through the businessRoles list which the dgv is bound to and get out each dgv row based upon the current id in the loop
businessRoleList.Cast<BusinessRole>().ToList().ForEach(delegate(BusinessRole currentRole)
{
DataGridViewRow currentRowForRole = dgvBusinessRole.Rows.Cast<DataGridViewRow>().ToList().Find(row => ((BusinessRole)row.DataBoundItem).Id == currentRole.Id);
// Get a reference to the comboBox cell in the current row
DataGridViewComboBoxCell comboBoxCell = (DataGridViewComboBoxCell)currentRowForRole.Cells[2];
// Not sure if this is necessary since these properties should be inherited from the combobox column properties
comboBoxCell.DisplayMember = "Name";
comboBoxCell.ValueMember = "Id";
comboBoxCell.ValueType = typeof(Guid);
// Get the business area for the current business role
BusinessArea currentAreaForRole = businessAreaRepository.FetchByRoleId(currentRole.Id);
// if the role has an associated area then set the value of the cell to be the appropriate item in the combobox
// and update the cell value
if (currentAreaForRole != null)
{
foreach (BusinessArea area in comboBoxCell.Items)
{
if (currentAreaForRole.Id == area.Id)
{
comboBoxCell.Value = area.Id;
dgvBusinessRole.UpdateCellValue(2, comboBoxCell.RowIndex);
}
}
}
});
}
The dgv is first bound to a binding list holding BusinessRole objects, then the combobox column is bound to a basic list of BusinessArea objects that come out of a repository class. I then loop through the bindinglist and pull out the row of the dgv that is bound to the current item in the bindinglist loop.
With that row I make a database call to see if the BusinessRole entity is associated with a BusinessArea entity. If it is then I want to select the item in the combobox column that holds the BusinessAreas.
The problem is that when the grid is loaded, all the data is there and the comboboxes are populated with a list of available areas, however any values that are set are not displayed. The code that sets the value is definately getting hit and the value I am setting definately exists in the list.
There are no data errors, nothing. It's just refusing to update the UI with the value I programmatically set.
Any help would be greatly appreciated as always.
Thanks
This code works for my first combo box I have in my row with the following code, but I try it with the next one in the row and it doesn't work. I could use the help with the rest I have 3 more to do. I do set the combo boxes on a second form from this form with the same code as is on the last line of the try block but using the cell information instead of the dataset
try
{
string strQuery = "select fundCode from vwDefaultItems where IncomeType = '" + stIncome + "'";
SqlDataAdapter daDefault = new SqlDataAdapter(strQuery, conn);
DataSet dsDefault = new DataSet();
daDefault.Fill(dsDefault);
strDefFund = dsDefault.Tables[0].Rows[0].ItemArray[0].ToString();
dgvCheckEntry.Rows[curRow].Cells[7].Value = dsDefault.Tables[0].Rows[0].ItemArray[0].ToString();
}
catch (Exception eq)
{
}

How to find matching words in Text Box and Datagrid when VirtualizingStackPanel.IsVirtualizing="true"?

I have Datagrid and Text Box in my Form. Datagrid is showing me existing items in my stock. I use Text Box to search and set focus to that row which is matching with my Text Box. Now it is working fine when VirtualizingStackPanel.IsVirtualizing="false" but it is very slow and getting a lot RAM resource.
Here is my code for this.
public IEnumerable<Microsoft.Windows.Controls.DataGridRow> GetDataGridRows(Microsoft.Windows.Controls.DataGrid grid)
{
var itemsSource = grid.ItemsSource as IEnumerable;
if (null == itemsSource) yield return null;
foreach (var item in itemsSource)
{
var row = grid.ItemContainerGenerator.ContainerFromItem(item) as Microsoft.Windows.Controls.DataGridRow;
if (null != row) yield return row;
}
}
private void SearchBoxDataGrid_TextChanged(object sender, TextChangedEventArgs e)
{
var row = GetDataGridRows(AssortDataGrid);
/// go through each row in the datagrid
foreach (Microsoft.Windows.Controls.DataGridRow r in row)
{
DataRowView rv = (DataRowView)r.Item;
// Get the state of what's in column 1 of the current row (in my case a string)
string t = rv.Row["Ассортимент"].ToString().ToLower();
if (t.StartsWith(SearchBoxDataGrid.Text.ToLower()))
{
AssortDataGrid.SelectedIndex = r.GetIndex();
AssortDataGrid.ScrollIntoView(AssortDataGrid.SelectedItem);
break;
}
}
}
What I want is to make it VirtualizingStackPanel.IsVirtualizing="true" but in this case my method is not working. I know why it is not working, my code will work only for showing part of Datagrid.
What do you recommend? How to fix this issue? Any idea will be appreciated. If you give any working code it will be fantastic. I hope I could explain my problem.
Virtualization means that WPF will reuse the UI components, and simply replace the DataContext behind the components.
For example, if your Grid has 1000 items and only 10 are visible, it will only render around 14 UI items (extra items for scroll buffer), and scrolling simply replaces the DataContext behind these UI items instead of creating new UI elements for every item. If you didn't use Virtualization, it would create all 1000 UI items.
For your Search to work with Virutalization, you need to loop through the DataContext (DataGrid.Items) instead of through the UI components. This can either be done in the code-behind, or if you're using MVVM you can handle the SeachCommand in your ViewModel.
I did a little coding and make it work. If anyone needs it in future please, use it.
Firstly I am creating List of Products
List<string> ProductList;
Then on Load Method I list all my products to my Product List.
SqlCommand commProc2 = new SqlCommand("SELECT dbo.fGetProductNameFromId(ProductID) as ProductName from Assortment order by ProductName desc", MainWindow.conn);
string str2;
SqlDataReader dr2 = commProc2.ExecuteReader();
ProductList = new List<string>();
try
{
if (dr2.HasRows)
{
while (dr2.Read())
{
str2 = (string)dr2["ProductName"];
ProductList.Insert(0, str2.ToLower ());
}
}
}
catch (Exception ex)
{
MessageBox.Show("An error occured while trying to fetch data\n" + ex.Message);
}
dr2.Close();
dr2.Dispose();
After that I did some changes in SearchBoxDataGrid_TextChanged
private void SearchBoxDataGrid_TextChanged(object sender, TextChangedEventArgs e)
{
int pos = 0;
string typedString = SearchBoxDataGrid.Text.ToLower();
foreach (string item in ProductList)
{
if (!string.IsNullOrEmpty(SearchBoxDataGrid.Text))
{
if (item.StartsWith(typedString))
{
pos = ProductList.IndexOf(item);
AssortDataGrid.SelectedIndex = pos;
AssortDataGrid.ScrollIntoView(AssortDataGrid.SelectedItem);
break;
}
}
}
}
Now it works when VirtualizingStackPanel.IsVirtualizing="true".
That is all.

Under SelectionChanged read out the underlying data from a List

Im busy with my app and i walked in some problems when i click on a photo in my listbox PhotoFeed.
I got 1 List<> with in it the strings UrlTumb and UrlFull.
I got 1 ListBox with in it a WrapPanel filled with images wich i set the Image.Source from my UrlTumb.
What my problem is when i click on a photo in my listbox i want to navigate to a new page and display there the original image (UrlFull) now i can only get my UrlTumb from my Image.Source but i want my UrlFull which is stored in the List. Now is my question how do i obtain the UrlFull. So how can i back trace which item i clicked and get the UrlFull from that item so i can send it with my NavigationService.Navigate
I can do it on an dirty way and create an invisible textblock besides the image in my ListBox and put the UrlFull in there but i would like to do it in a proper way
So what do i place in the ????? spot in this line
NavigationService.Navigate(new Uri("/PhotoInfo.xaml?urlfull={0}", ????? , UriKind.Relative));
Greetings Cn
There are multiple options:
Use selected item's index listBox.SelectedIndex to get the index
of the selected property which will correspond to the index in your
source (it might not if you filter the collection using collection
source, but I think that is not the case)
Use selected item listBox.SelectedItem this will return the
SelectedItem which will contain your object. (Note, that if your
selection mode set to multiple, this will return only the firstly
selected item)
Use SelectemItems. It will allow you to get an array of selected
items (Note: this should be normally used only when your list's
selection mode is set to multiple)
Use SelectedValue, which will contain the value of the SelectedItem
(this will save you and extra step.
Use arguments of the Selection changed event AddedItems.
Bellow is the code snippet of 3 options above. x, y, z will all be your selected names (e.g. "Mike")
XAML:
<ListBox x:Name="lb"
ItemsSource="{Binding Names}"
SelectionChanged="NameChanged" />
Code behind:
public class Person
{
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
private List<Person> people = new List<Person>
{
new Person{Name = "Lewis"},
new Person{Name = "Peter"},
new Person{Name = "Brian"}
};
public List<Person> People
{
get
{
return this.people;
}
set
{
this.people = value;
}
}
private void NameChanged(object sender, SelectionChangedEventArgs e)
{
var x = this.people[lb.SelectedIndex];
var y = lb.SelectedItem;
var z = lb.SelectedItems[0];
var h = lb.SelectedValue;
var u = e.AddedItems[0];
var person = e.AddedItems[0] as Person;
if (person != null)
{
var result = person.Name;
}
}
For the differences between SelectedValue and SelectedItem refer here SelectedItem vs SelectedValue

Databinding of DataGridViewComboBoxColumn

In the code below, the combo box named "ConnectionType" shows the selected item, but one cannot change the selected item (it seems like there is only one item in the combo box). If I comment out the line
typeCol.DataPropertyName = "ConnectionTypeName";
then the combo box is selectable, but the correct item is not selected, of course. What am I doing wrong??
Thanks.
private void LoadConnectionsGrid()
{
_dc = new EnterpriseEntities();
dataGridViewConnections.AutoGenerateColumns = false;
dataGridViewConnections.DataSource = _dc.Connection.Include("ConnectionType");
DataGridViewComboBoxColumn typeCol =
(DataGridViewComboBoxColumn)dataGridViewConnections.Columns["ConnectionType"];
typeCol.DataPropertyName = "ConnectionTypeName";
var qry = from c in _dc.ConnectionType
select c.Type;
typeCol.DataSource = qry;
DataGridViewTextBoxColumn nameCol =
(DataGridViewTextBoxColumn)dataGridViewConnections.Columns["ConnectionName"];
nameCol.DataPropertyName = "Name";
DataGridViewTextBoxColumn connStrCol =
(DataGridViewTextBoxColumn)dataGridViewConnections.Columns["ConnectionString"];
connStrCol.DataPropertyName = "ConnectionString";
}
Ultimately, I could not get data binding, the entity framework, and comboboxes to play nice and I just created the data grid brute force (code below). This means that I handle inserts, updates and deletes by hand.
private void LoadConnectionsGrid()
{
DataGridViewComboBoxColumn typeCol =
(DataGridViewComboBoxColumn)dataGridViewConnections.Columns["ConnectionType"];
var qry = from c in _dc.ConnectionType
select c.Type;
typeCol.DataSource = qry;
dataGridViewConnections.Rows.Clear();
foreach (Connection conn in _dc.Connection.Include("ConnectionType"))
{
dataGridViewConnections.Rows.Add(conn.Name,
conn.ConnectionType.Type, conn.ConnectionString);
}
}

How to maintain ComboBox.SelectedItem reference when DataSource is resorted?

This really seems like a bug to me, but perhaps some databinding gurus can enlighten me? (My WinForms databinding knowledge is quite limited.)
I have a ComboBox bound to a sorted DataView. When the properties of the items in the DataView change such that items are resorted, the SelectedItem in my ComboBox does not keep in-sync. It seems to point to someplace completely random. Is this a bug, or am I missing something in my databinding?
Here is a sample application that reproduces the problem. All you need is a Button and a ComboBox:
public partial class Form1 : Form
{
private DataTable myData;
public Form1()
{
this.InitializeComponent();
this.myData = new DataTable();
this.myData.Columns.Add("ID", typeof(int));
this.myData.Columns.Add("Name", typeof(string));
this.myData.Columns.Add("LastModified", typeof(DateTime));
this.myData.Rows.Add(1, "first", DateTime.Now.AddMinutes(-2));
this.myData.Rows.Add(2, "second", DateTime.Now.AddMinutes(-1));
this.myData.Rows.Add(3, "third", DateTime.Now);
this.myData.DefaultView.Sort = "LastModified DESC";
this.comboBox1.DataSource = this.myData.DefaultView;
this.comboBox1.ValueMember = "ID";
this.comboBox1.DisplayMember = "Name";
}
private void saveStuffButton_Click(object sender, EventArgs e)
{
DataRowView preUpdateSelectedItem = (DataRowView)this.comboBox1.SelectedItem;
// OUTPUT: SelectedIndex = 0; SelectedItem.Name = third
Debug.WriteLine(string.Format("SelectedIndex = {0:N0}; SelectedItem.Name = {1}", this.comboBox1.SelectedIndex, preUpdateSelectedItem["Name"]));
this.myData.Rows[0]["LastModified"] = DateTime.Now;
DataRowView postUpdateSelectedItem = (DataRowView)this.comboBox1.SelectedItem;
// OUTPUT: SelectedIndex = 2; SelectedItem.Name = second
Debug.WriteLine(string.Format("SelectedIndex = {0:N0}; SelectedItem.Name = {1}", this.comboBox1.SelectedIndex, postUpdateSelectedItem["Name"]));
// FAIL!
Debug.Assert(object.ReferenceEquals(preUpdateSelectedItem, postUpdateSelectedItem));
}
}
To clarify:
I understand how I would fix the simple application above--I only included that to demonstrate the problem. My concern is how to fix it when the updates to the underlying data rows could be happening anywhere (on another form, perhaps.)
I would really like to still receive updates, inserts, deletes, etc. to my data source. I have tried just binding to an array of DataRows severed from the DataTable, but this causes additional headaches.
Just add a BindingContext to the ComboBox :
this.comboBox1.DataSource = this.myData.DefaultView;
this.comboBox1.BindingContext = new BindingContext();
this.comboBox1.ValueMember = "ID";
this.comboBox1.DisplayMember = "Name";
By the way, try not keeping auto-generated names for your widgets (comboBox1, ...), it is dirty. :-P
The only promising solution I see at this time is to bind the combo box to a detached data source and then update it every time the "real" DataView changes. Here is what I have so far. Seems to be working, but (1) it's a total hack, and (2) it will not scale well at all.
In form declaration:
private DataView shadowView;
In form initialization:
this.comboBox1.DisplayMember = "Value";
this.comboBox1.ValueMember = "Key";
this.shadowView = new DataView(GlobalData.TheGlobalTable, null, "LastModified DESC", DataViewRowState.CurrentRows);
this.shadowView.ListChanged += new ListChangedEventHandler(shadowView_ListChanged);
this.ResetComboBoxDataSource(null);
And then the hack:
private void shadowView_ListChanged(object sender, ListChangedEventArgs e)
{
this.ResetComboBoxDataSource((int)this.comboBox1.SelectedValue);
}
private void ResetComboBoxDataSource(int? selectedId)
{
int selectedIndex = 0;
var detached = new KeyValuePair<int, string>[this.shadowView.Count];
for (int i = 0; i < this.shadowView.Count; i++)
{
int id = (int)this.shadowView[i]["ID"];
detached[i] = new KeyValuePair<int, string>(id, (string)this.shadowView[i]["Name"]);
if (id == selectedId)
{
selectedIndex = i;
}
}
this.comboBox1.DataSource = detached;
this.comboBox1.SelectedIndex = selectedIndex;
}
Must detach event handler in Dispose:
this.shadowView.ListChanged -= new ListChangedEventHandler(shadowView_ListChanged);
Your example sorts the data on the column it updates. When the update occurs, the order of the rows changes. The combobox is using the index to keep track of it's selected items, so when the items are sorted, the index is pointing to a different row. You'll need to capture the value of comboxBox1.SelectedItem before updating the row, and set it back once the update is complete:
DataRowView selected = (DataRowView)this.comboBox1.SelectedItem;
this.myData.Rows[0]["LastModified"] = DateTime.Now;
this.comboBox1.SelectedItem = selected;
From an architecture perspective, the SelectedItem must be cleared when rebinding the DataSource because the DataBinder don't know if your SelectedItem will persist or not.
From a functional perspective, the DataBinder may not be able to ensure that your SelectedItem from you old DataSource is the same in your new DataSource (it can be a different DataSource with the same SelectedItem ID).
Its more an application feature or a custom control feature than a generic databinding process.
IMHO, you have theses choices if you want to keep the SelectedItem on rebind :
Create a reusable custom control / custom DataBinder with a persistance option which try to set the SelectedItem with all your data validation (using a DataSource / item identification to ensure the item validity)
Persist it specifically on your Form using the Form/Application context (like ViewState for ASP.NET).
Some controls on the .NET market are helping you by rebinding (including selections) the control from their own persisted DataSource if the DataSource is not changed and DataBind not recalled. That's the best pratice.

Resources