Get data from DataGrid WPF - c#

How can I get data from DataGrid when ComboBox' text is equal with the record of the DataGrid:
combobox1.ItemsSource = database.Mahs.ToList();
combobox1.DisplayMemberPath = "MahName";
and
private void datagrid_customer_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var data = datagrid_customer.SelectedItem;
string id = (datagrid_customer.SelectedCells[0].Column.GetCellContent(data) as TextBlock).Text;
txt_f1.Text = id;
}
It shows me the id, but when i selected item but i want show me id when combobox.Text = name of the row in the DataGrid, then show the id of that row.

I would suggest changing your approach slightly, and retrieving the entire object from both controls for the comparison. This way you have full access to the properties of each selected object.
If the objects you're retrieving from your database override .Equals and .GetHashCode you could do away with some of the if statements below. But to get you started here's a quick example of your change listener
private void datagrid_customer_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Cast the objects from your DataGrid and your ComboBox.
YourDataObject dataItem = (YourDataObject)dataGrid1.SelectedItem;
YourDataObject comboItem = (YourDataObject)combo.SelectedItem;
// Compare your objects and decide if they're the same. Ideally you would
// have Equals and HashCode overridden so you could improve this
if (dataItem == null || comboItem == null)
text.Text = "Not Matching";
else
{
if (dataItem.MahName == comboItem.MahName)
// You've got full access to the object and all it's properties
text.Text = dataItem.Id.ToString();
else
text.Text = "Not Matching";
}
}

Related

Sorting WPF datagrid with ComboBox

I have filled a WPF DataGrid by setting the ItemSource with the desired table (DataTable). Now I do want to sort the table by a value in one of the DataTable column. I do not want to sort the table using the default sorting which can be used by clicking on the table headers. I do want want sort the table using a ComboBox. You can select one of the items in the combobox and the table will sort. I have implemented the code below. However when I change the selected item in the combobox, the DataGrid will be empty. All records are not shown. The headers are still visible.
private void DbFilterSortByBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (DbFilterSortByBox.SelectedItem != null)
{
DbMainTable.ItemsSource = SortedTable(DbFilterSortByBox.SelectedItem.ToString()).DefaultView;
for (int i = 0; i < propertiesHandler.TablePropertiesIndex.Length; i++)
{
if (propertiesHandler.TablePropertiesValue[i] == false)
{
DbMainTable.Columns[propertiesHandler.TablePropertiesIndex[i]].Visibility = Visibility.Hidden;
}
}
}
}
private DataTable SortedTable(string sortingBy)
{
DataView sortedTable = dataHandler.KicksTable.Clone().DefaultView;
if (sortingBy == "Type")
{
sortedTable.Sort = "Type";
} else if (sortingBy == "Size, ascending")
{
sortedTable.Sort = "Size asc";
}
return sortedTable.ToTable();
}
The DbFilterSortByBox_SelectionChanged event occurs when the selected item in the ComboBox is changed. The SortedTable function is then called to sort the table by the desired value.
Does someone see where it goes wrong and how to solve this problem?
If the itemssource is set to
dataHandler.KicksTable.DefaultView = dataHandler.KicksTable.DefaultView;
You can just do:
dataHandler.KicksTable.DefaultView.Sort = "Size asc";
dataHandler.KicksTable.DefaultView.Refresh();
No cloning.
Obviously, you still need the logic chooses which sort string to set.
DataTable.Clone creates a new DataTable with the same structure as the original DataTable, but does not copy any data (the new DataTable will not contain any DataRows - it will always be empty).
To copy both the structure and data into a new DataTable, use must DataTable.Copy.
However, you shouldn't clone nor copy the DataTable at all. It just creates unnecessary overhead.
Simply bind the DataTable to the DataGrid.ItemsSource. Because the ItemsControl in general works on collection views, all you need to do is to sort/filter/group the view. You don't have to refresh anything or reset the ItemsSource property.
Because collection view and collection are different layers, modifying the collection view does not modify the underlying collection (or DataTable). You therefore shouldn't clone/copy your DataTable before or after sorting/filtering/grouping it.
public Constructor()
{
InitializeComponent();
// Prefer data binding instead by simply binding to the DataTable.
// Binding will automatically get the default view of the DataTable.
DbMainTable.ItemsSource = dataHandler.KicksTable.DefaultView;
}
private void DbFilterSortByBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var comboBox = sender as ComboBox;
if (comboBox.SelectedItem is null)
{
return;
}
DataView kicksTableView = dataHandler.KicksTable.DefaultView;
var sortConstraint = comboBox.SelectedItem as string;
SortTable(kicksTableView, sortConstraint);
}
private void SortTable(DataView tableView, string sortConstraint)
{
if (sortConstraint.Equals("Type", StringComparison.OrdinalIgnoreCase))
{
// Setting the Sort property will automatically sort the view and the DataGrid that binds to this view.
tableView.Sort = "Type";
}
else if (sortConstraint.Equals("Size, ascending", StringComparison.OrdinalIgnoreCase))
{
// Setting the Sort property will automatically sort the view and the DataGrid that binds to this view.
tableView.Sort = "Size asc";
}
}

ListBox deletion

I have a listbox, with 2 buttons, new and delete. new adds an item into the list box, and the delete button should delete the item out of the list box. The list box items are tied to a class that stores user entered data from text boxes below.
private void AddListBox()
{
lstCondition.BeginUpdate();
Condition cond = new Condition("");
cond.Name = string.Format("Condition {0}", _selection.NetConditions.Count + 1);
_selection.NetConditions.Add(cond);
lstCondition.EndUpdate();
lstCondition.SelectedItem = cond;
cboNetCondition.Properties.Items.Clear();
cboNetCondition.Properties.Items.AddRange(NetCondition);
cboControlType.Properties.Items.Clear();
cboControlType.Properties.Items.AddRange(ControlType);
cboFlowRate.Properties.Items.Clear();
cboFlowRate.Properties.Items.AddRange(FlowRate);
}
private void btnNew_Click(object sender, EventArgs e)
{
AddListBox();
}
the cbo items are comboboxes, whose data gets tied in the condition class to each instance of the list box.
public frmNetConditions(Condition condo, Selection selection)
{
InitializeComponent();
_selection = selection;
lstCondition.DataSource = _selection.NetConditions;
condition = _selection.NetConditions.Count;
}
private void btnDelete_Click(object sender, EventArgs e)
{
selectedCondition = (Condition)lstCondition.SelectedItem;
cboControlType.SelectedIndex = -1;
cboNetCondition.SelectedIndex = -1;
cboFlowRate.SelectedIndex = -1;
txtFlowRate.Text = string.Empty;
txtStatPressure.Text = string.Empty;
txtDampOpening.Text = string.Empty;
txtDensity.Text = string.Empty;
cboDensity.SelectedIndex = -1;
lstCondition.Items.Remove(lstCondition.SelectedItem);
lstCondition.Refresh();
}
After pressing this delete button, the listbox, still contains the item i wish to delete, im unsure why thats the case?
Update with datasource
public List<Condition> NetConditions { get { return _netconditions; } }
As already suggested, you should bind to a BindingList<Condition> instead of a List<Condition>. This allows you to change the datasource and the control (ListBox) to get notified by your changes. The code should look like this:
lstCondition.ValueMember = "ConditionId";
lstCondition.DisplayMember = "Name";
lstCondition.DataSource = NetConditions;
After defining the binding, the correct way of operating on the ListBox items is to remove from the datasource, not the ListBox itself:
// SelectedItem should be checked for null (no selection is an option)
NetCondition.Remove((Condition)lstCondition.SelectedItem);
However, if you plan to change properties from an element (so, not the list itself), the control is notified only if your element (Condition) implements INotifyPropertyChanged interface.

DevExpress lookupedit repository item add new row in Xtra Grid View in first row

I have a devexpress gridcontrol with 5 columns. The first column is a lookupedit repository with some data, let say with CarTypes.
To load data in grid I am using a BindingSource. In this BindingSource.DataSource I have load a IList<Cars>
and then
added this binding source in dataSource of my gridcontrol
like bellow
BindingSource _carsBindingSource = new BindingSource();
private void BindData(IList<Cars> data)
{
_carsBindingSource.DataSource = data;
carsGridControl.BeginUpdate();
carsGridControl.DataSource = _carsBindingSource;
carsGridControl.RefreshDataSource();
carsGridControl.EndUpdate();
}
I have a button to add new line in my grid "Add new car" and add a new line in _carBindingSource
private void AddNewRow()
{
_newRow = true;
_carsBindingSource.AllowNew = true;
Cars newCar = new Cars();
newCar.CarType = new CarType();
_carsBindingSource.Add(newCar );
//_carsBindingSource.Insert(0,newCar);
}
Now I want to add the new line in the first row of grid.
I use Insert
_carsBindingSource.Insert(0,newCar);
But it didn't work. The lookupedit repository can't load data.
With _carsBindingSource.Add(newCar); it works fine
Can anyone help me? Thank you!
If you haven't already, consider using an intermediate list for your car types:
private List<CarTypes> _CarTypes;
// Elsewhere in the code...
_CarTypes = GetCarTypes();
And then in the form load event, be sure this is bound to the data source:
repositoryLookupItemCarTypes.DataSource = _CarTypes;
With this, the grid should now automatically manage the instantiation and selection of the CarType object for each Cars object. You can omit this line when you add a car to the grid:
newCar.CarType = new CarType();
In the designer, I think it helps to alter the DisplayMember Property of the repository Item.
With this setup, any Car added to your grid should automatically have the CarType as a populated Lookup Edit.
If any of this is unclear, let me know. I did a quick and dirty solution to test this, and I obviously can't post it all, but I can tell you it did work with both Add and Insert.
Actualy I found a solutions.
The problem was in GridView_CustomRowCellEdit(object sender, CustomRowCellEditEventArgs e) event
where I change the AllowEdit value (e.Column.OptionsColumn.AllowEdit = true;).
private void gridView_CustomRowCellEdit(object sender, CustomRowCellEditEventArgs e)
{
string cName = e.Column.FieldName;
GridView gv = sender as GridView;
if (cName == "CarType.IdApi")
{
if (isNewRow)
{
Cars cars= (Cars)gv.GetRow(e.RowHandle);
int a = e.RowHandle;
if (cars.ID== 0 && e.RowHandle == 0)
{
e.Column.OptionsColumn.AllowEdit = true;
}
else
{
e.Column.OptionsColumn.AllowEdit = false;
}
}
}
}
When I use Insert(0, new Car) then because of second row whitch has value
the AllowEdit was false;
So I remove else code and it works
private void gridView_CustomRowCellEdit(object sender, CustomRowCellEditEventArgs e)
{
string cName = e.Column.FieldName;
GridView gv = sender as GridView;
if (cName == "CarType.IdApi")
{
if (isNewRow)
{
Cars cars= (Cars)gv.GetRow(e.RowHandle);
int a = e.RowHandle;
if (cars.ID== 0 && e.RowHandle == 0)
{
e.Column.OptionsColumn.AllowEdit = true;
}
}
}
}
So finlay I found that bindingSource.Add(object) and bindingSource.Insert(0,object) is same!
I apologize for my english!!

ComboBox item doesn't update even if case is changed

I have a basic form with a combobox, a textbox, and a button on it. The combobox has an unalterable number of items within it, but the items themselves can be changed by inputting a new value for the selected item.
From the example in the picture, if I input a string such as "identifier", the selected item in the combobox changes from "ID" to "identifier", as expected. However, if I input "id", the logic (below) executes normally, the item updates, but visually, the item does not change from "ID" to "id".
Here is the code for the event handler of the button
private void btnApply_Click(object sender, EventArgs e) {
string newValue = txtNewName.Text;
if(string.IsNullOrWhiteSpace(newValue)) {
MessageBox.Show("Please input a new column name");
return;
}
if(cmbHeaderNames.Items.Contains(newValue)) {
MessageBox.Show("A column with that name already exists");
return;
}
cmbHeaderNames.Items[cmbHeaderNames.SelectedIndex] = newValue;
txtNewName.Text = "";
}
I believe that the ComboBox is doing some string comparison, because the following code sample works.
if (comboBox1.SelectedItem.ToString().ToUpper() == textBox1.Text.ToUpper())
{
comboBox1.Items[comboBox1.SelectedIndex] = string.Empty;
comboBox1.Items[comboBox1.SelectedIndex] = textBox1.Text;
}
Apparently, the update successfully applies if the two string values are not identical when applying ToUpper() or ToLower().

DataGridView Check Box selection

I added datagridview to my win forms app and I also added one CheckBox for marking rows.
The CheckBox works as I expected until the user sorts the DataGridView. After the sort the previous selection of checkbox column is lost.
Is there a way I can make my datagridview remember which row is selected after sorting?
You have two options to solve this issue.
The first and possibly the most simple is to bind your checkbox column to your datasource. For example, if you are using a DataTable as your datasource, adding a boolean column will create a checkbox on your DataGridView that will sort and not lose the checked state.
If this is not an option then the other way of addressing the problem is to set your DataGridView to Virtual mode and maintain a cache of your checkbox values.
Check out the excellent DataGridView FAQ for an example of how to do this. I've also provided the code below but do check out the FAQ:
private System.Collections.Generic.Dictionary<int, bool> checkState;
private void Form1_Load(object sender, EventArgs e)
{
dataGridView1.AutoGenerateColumns = false;
dataGridView1.DataSource = customerOrdersBindingSource;
// The check box column will be virtual.
dataGridView1.VirtualMode = true;
dataGridView1.Columns.Insert(0, new DataGridViewCheckBoxColumn());
// Initialize the dictionary that contains the boolean check state.
checkState = new Dictionary<int, bool>();
}
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
// Update the status bar when the cell value changes.
if (e.ColumnIndex == 0 && e.RowIndex != -1)
{
// Get the orderID from the OrderID column.
int orderID = (int)dataGridView1.Rows[e.RowIndex].Cells["OrderID"].Value;
checkState[orderID] = (bool)dataGridView1.Rows[e.RowIndex].Cells[0].Value;
}
}
private void dataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
// Handle the notification that the value for a cell in the virtual column
// is needed. Get the value from the dictionary if the key exists.
if (e.ColumnIndex == 0)
{
int orderID = (int)dataGridView1.Rows[e.RowIndex].Cells["OrderID"].Value;
if (checkState.ContainsKey(orderID))
e.Value = checkState[orderID];
else
e.Value = false;
}
}
private void dataGridView1_CellValuePushed(object sender, DataGridViewCellValueEventArgs e)
{
// Handle the notification that the value for a cell in the virtual column
// needs to be pushed back to the dictionary.
if (e.ColumnIndex == 0)
{
// Get the orderID from the OrderID column.
int orderID = (int)dataGridView1.Rows[e.RowIndex].Cells["OrderID"].Value;
// Add or update the checked value to the dictionary depending on if the
// key (orderID) already exists.
if (!checkState.ContainsKey(orderID))
{
checkState.Add(orderID, (bool)e.Value);
}
else
checkState[orderID] = (bool)e.Value;
}
}
I'm surprised that that happens, but if there's no other way around it in a worst case you could set the sorting to programmatic and then handle when the user clicks on the column header, save a list of which items are checked, do the sorting programmatically and then check any items that should be checked.

Categories