I have a ComboBox, which holds a 3rd level selection (Parent-category > Sub-category > Brand). When any of the two higher level categories are changed, the DataSource for the Brand selection should be refreshed, and the selection reset.
Now this works quite well for the Sub-category selection, but not for the Parent-category selection Despite using almost identical syntax.
I could of course just make a quick fix, using cboBrand.Text = string.Empty, but the thing is, that the brand combo box still holds the same number of slots as before, just empty. I.e. if there was 3 brand options before changing the parent category, there will be 3 empty slots in the drop down list. I have checked, and the Count() of viewModel.BrandOptions is zero, so this is not the issue.
Does anyone have any idea, what I am doing wrong?
Parent-category Changed (does not work)
private void cboCategory_SelectionChangeCommitted(object sender, EventArgs e)
{
viewModel.Category = (CategoryModel)cboCategory.SelectedItem;
// Update sub-category data source
cboSubCategory.DataSource = viewModel.SubCategoryOptions;
// Update Brand data source and reset selection
cboBrand.DataSource = viewModel.BrandOptions;
cboBrand.SelectedIndex = -1;
}
Sub-category Changed (this one works fine)
private void cboSubCategory_SelectionChangeCommitted(object sender, EventArgs e)
{
if (cboCategory.SelectedItem != null)
viewModel.SubCategory = (CategoryModel)cboSubCategory.SelectedItem;
// Update Brand data source and reset selection
cboBrand.DataSource = viewModel.BrandOptions;
cboBrand.SelectedIndex = -1;
}
Update
Setting cboBrand.DataSource = null will make it clear the combo box. But it does not fix the multiple blank rows, in the drop down list. I have also checked (before changing the data source to null) if there was any objects in the data source. This was not the case, as you can see in the image.
Have you tried putting:
private void cboCategory_SelectionChangeCommitted(object sender, EventArgs e)
{
viewModel.Category = (CategoryModel)cboCategory.SelectedItem;
// Update sub-category data source
cboSubCategory.DataSource = viewModel.SubCategoryOptions;
cboSubCategory.SelectedIndex = -1; // << new line to blank out SubCat
// Update Brand data source and reset selection
cboBrand.DataSource = viewModel.BrandOptions;
cboBrand.SelectedIndex = -1;
}
... I have a feeling that what's going on is that you're refreshing two data sources in two sequential lines, and that it's a timing issue - cboBrand is trying to refresh its data before cboSubCategory has the correct value in place.
Related
I am working with a datagrid in WPF which is bound to a collectionviewsource. The viewsource is bound to an observable collection named Rows.
The datagrid has add and delete functions which function properly except on small problem.
Here are images:
The datagrid has more data than this. Each test starts off with two sequences (the two rows you see belong to a single test) and they are grouped and sorted by a unique ID.
I have clicked the red "X" to delete the row. I will now click the "Add" button located at the top-left of the image.
The data is still there.
These are my add and delete functions:
private void Add(object sender, ExecutedRoutedEventArgs e)
{
var testRun = e.Parameter as TestRun;
if (testRun != null)
{
var numberOfRows = testRun.Property.GetValue("numberOfRows").ToNullable<int>().GetValueOrDefault(2);
numberOfRows++;
testRun.Property.SetValue("numberOfRows", numberOfRows.ToString());
this.Rows.Add(new ESCHandle(testRun, numberOfRows));
}
}
private void Delete(object sender, ExecutedRoutedEventArgs e)
{
var esc = e.Parameter as ESCHandle;
if (esc != null)
{
this.Rows.Remove(esc);
var numberOfRows = esc.TestRun.Property.GetValue("numberOfRows").ToNullable<int>().GetValueOrDefault(2);
numberOfRows--;
esc.TestRun.Property.SetValue("numberOfRows", numberOfRows.ToString());
}
}
The ESC object is properly removed from the observablecollection on Delete. But on when I add another ESC object/row to the colleciton and datagrid, the data is somehow copied to the new object.
You may forget to call a refresh method on datagrid to update its visual elements, such as the rows.
Datagrid.Items.Refresh(), as described here:
http://programmer.wrighton.org/2009/01/wpf-datagrid-items-refresh.html
This problem may be caused because PropertyChange is not raised properly.
How can I have an empty item in the bound ComboBox which uses NULL as the value for an Insert or Update?
With the code below, I can manually add the additional row. The column inspector_id is the primary key of an FK relationship. I have to set inspector_id = -1, since C# does not allow an int to be null. However, the insert (or update) fails since there is no inspector_id: -1 in the database.
private void ItemInfo_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'someDBDataSet.inspector' table. You can move, or remove it, as needed.
this.inspectorTableAdapter.ClearBeforeFill = false;
someDBDataSet.inspectorRow newRow = this.someDBDataSet.inspector.NewinspectorRow();
newRow.inspector_id = -1; // Since an int in C# cannot be null
newRow.fullName = "(none)";
newRow.employeeCode = "";
this.someDBDataSet.inspector.AddinspectorRow(newRow);
this.inspectorTableAdapter.Fill(this.someDBDataSet.inspector);
//this.inspectorTableAdapter.ClearBeforeFill = false;
// TODO: This line of code loads data into the 'someDBDataSet.item' table. You can move, or remove it, as needed.
this.itemTableAdapter.Fill(this.someDBDataSet.item);
}
Eureka! Bind to a view, not table.
Bind inspector_idComboBox to a new SQL Server view of the inspector table.
SELECT NULL as inspector_id, '(none)' as fullName, '' as employeeCode
UNION
SELECT inspector_id, fullName, employeeCode
FROM dbo.inspector
Pros:
The (none) item is in the ComboBox
The SelectedItem and text persists when selecting the item.
The SQL view allows a NULL value for inspector_id
No workarounds are needed in the application code. Just fill the DataSet from the view.
Allows more flexibility as the relationship is not bound.
... brilliant!
Another approach is to clear the ComboBox when selecting (none):
private void inspector_idComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (inspector_idComboBox.SelectedValue != null)
if ((int)inspector_idComboBox.SelectedValue == -1)
inspector_idComboBox.SelectedItem = null;
}
Pros:
The correct NULL value is saved to the DataSet and sent on to the database.
No external clear button is needed.
Cons:
Selecting (none) also clears the text. I'd prefer for (none) to stay selected.
After trying various approaches, I initially decided on the following:
private void ItemInfo_Load(object sender, EventArgs e)
{
this.inspectorTableAdapter.Fill(this.someDBDataSet.inspector);
this.itemTableAdapter.Fill(this.someDBDataSet.item);
}
private void noInspector_btn_Click(object sender, EventArgs e)
{
inspector_idComboBox.SelectedItem = null;
}
Rather than adding a dummy item into the ComboBox, I added a (link) button to clear the ComboBox.
Pros:
The ComboBox clears.
The tableAdapter sets item.inspector_id = NULL
Cons:
Other form controls bound to inspector fields remain unchanged (as there is no "empty" inspector row to use).
No text is displayed in inspector_idComboBox when SelectedItem is null. I'd prefer having something like (none) shown in the box.
I think this issue has a simple solution but I have been banging my head on it for a few days now. I have a web application in which Dynamically gets a list of students from a stored procedure. I want to look at detailed information for each student and subsequent class information. On the Student's Details page, there is a dropdown list that contains all the classes that the student is in and when one is selected, the Community Partner field should be updated.
I am using SelectedIndexChanged method but in order to make it work, I need to set AutoPostBack to True and that causes the page to reload and thus the dropdown list and selected value to reload as well. I have tried several different configurations of this code with no results.
Here is my ascx file
<asp:DropDownList ID="StudentCourses" runat="server"></asp:DropDownList>
And here is my ascx.cs file
protected void Page_PreRender(object sender, EventArgs e)
{
if (Session["StudentID"] != null)
{
int studentId = Convert.ToInt32(Session["StudentID"]);
Student student = studentRepository.GetStudent(studentId);
StudentCourses_SelectedIndexChanged(sender, e);
StudentCommunityPartner.Text = StudentCourses.SelectedItem.Value;
...
And here is my SelectedIndexChanged method
protected void StudentCourses_SelectedIndexChanged(object sender, EventArgs e)
{
IList<KeyValuePair<Course, CommunityPartner>> courseList = studentRepository.GetStudentCourses(Convert.ToInt32(Session["StudentID"]));
StudentCourses.DataSource = courseList;
StudentCourses.DataBind();
int ctr = 0;
foreach (KeyValuePair<Course, CommunityPartner> kvp in courseList)
{
if (ctr < StudentCourses.Items.Count)
{
StudentCourses.Items[ctr].Text = kvp.Key.CourseCode;
StudentCourses.Items[ctr].Value = kvp.Value.PartnerName;
ctr++;
}
else ctr = 0;
}
StudentCommunityPartner.Text = StudentCourses.SelectedItem.Value;
}
I have tried several combinations and I am at a loss as to how to properly change the content on the page without the dropdownlist refreshing every time I do. Thanks for your help, it is much appreciated.
To set a textbox off of a drop down change look here:
set dropdownlist value to textbox in jQuery
If you have more that you want to do, the selected value from the drop down should be kept in the view state on post back. You might try saving that value
var Selected = StudentCourses.SelectedValue;
populate the drop down
and then set the selected value with the saved value
StudentCourses.SelectedValue = Selected;
I have a dataset, and 2 datatables.
Datatable1 = Combobox source (This will display a list of options)
Datatable2 = DataGrid (This will display data relevant to the options in combo box)
Submit Button (populate datagrid based on combo box selected value)
When i select an item in combo box and click submit, it load up the relevant records in datagrid. If i then change a value in the datagrid and click the submit button, the value i have just changed, dissapears?
How can i make it so that any altered datagrid values amend the datable, so that even if i view different options, i can always return any, an retain any of the changed values?
Here is my code:
//Load the data grid according to the ComboCAtegory selection
public void Grid_Load()
{
DataSet();
var Result = from c in DataSet_Main.Tables[2].AsEnumerable()
where c.Field<string>("Test_Code").Equals(comboBox_CategorySelect.SelectedValue)
select c;
dataGridView_Main.DataSource = Result.AsDataView();
dataGridView_Main.Columns["Test_Code"].Visible = false;
dataGridView_Main.Columns["ID"].Visible = false;
dataGridView_Main.Columns["Description"].Visible = false;
dataGridView_Main.Columns["Expected_Result"].Visible = false;
}
private void buttonSubmit_Click(object sender, EventArgs e)
{
Grid_Load();
}
public void Fail()
{
DataTable dt = DataSet_Main.Tables[2];
//dataGridView_Main.SelectedRows[0].Cells["Check"].Value = "Fail";
dt.Rows[dataGridView_Main.SelectedRows[0].Index]["Check"] = "Fail";
}
private void buttonFail_Click(object sender, EventArgs e)
{
Fail();
}
Hope this makes sense?
I think your DataGrid is already bound to the Data Table. What you need to do is send the changes back to the data source so that they would be reflected in the second data table which is bound to the same data source. To do this, write an event handler for CellChanging event on the DataGrid and in that you can the call Update() method on your Data Adapter (if you are using one, that is) to send changes to the data source. Then, in the same event handler, update the items in the combo box by refreshing the data bind so that the combo box gets latest values from the second data table.
This way, whenever the cell changes its value in the DataGrid, you can check if it is the relevant cell which you want and update the combo box based on the changes in the data grid.
Apologies my bad..i am a boof head.
Tha datagrid IS bound automatically. Ive just realised i was calling my initial dataset() method - which is calling my database, in my datagrid_load method. thus everytime i was populating the datagrid, it was actually refreshing from the database not the datatable.
Thankyou your repy tho..
I have a windows forms application containing a datagridview control. The datagridview is populated by the contents of an xml file. At the moment, all of the columns are displayed as datagridviewtextboxcolumns. I want to select one that is populated by a particular xml tag and display it's content in a datagridviewcomboboxcolumn along with 2 other options.
EXAMPLE:
<SMS>
<Number>+447931663542</Number>
<DateTime>2009-07-12T17:00:02</DateTime>
<Message>YES</Message>
<FollowedUpBy>Unassigned</FollowedUpBy>
<Outcome>Resolved</Outcome>
</SMS>
The OUTCOME tag is the column that I would like to be displayed as a comboboxcolumn in the datagridview. If for example the tag is empty and contains no data, then I want to display nothing, but have the comboboxcolumn populated with 3 possible options to choose from (Unresolved, Resolved, Pending). If however the tag contains data, I want that particular item to be displayed in the comboboxcolumn, and have the other two options available to be selected.
Help in achieving this would be appreciated greatly!
Regards,
EDIT:
Currently I use this code:
colOutcome = new DataGridViewComboBoxColumn();
colOutcome.HeaderText = "Outcome";
colOutcome.Width = 90;
colOutcome.Items.AddRange("Resolved", "Unresolved", "Pending");
this.dataGridView1.Columns.Insert(1, colOutcome);
this.dataGridView1.Columns[1].Name = "OutcomeColumn";
This code above populates the combobox. THE PROBLEM IS: When The xml document populates the datagridview, the outcome column just appears as a textbox column, containing the data inbetween the outcome tags in the xml file. My point is, how can i get the datagridview to realise when it reads the outcome column that it needs to be changed into a combobox column and then display the data that way, along with the other potentially selectable options in the combobox?! Currently the datagridview gets populated with all columns as textboxcolumns containing the data, as well as a seperate combobox column which is not what I want. I need the application to merge the outcome column and its data with the code above.
Any ideas?
Updated Answer
You could pass in the XML document to a function that will loop through each node and determine whether it should be a ComboBox one or not i.e. if the name is "Outcome".
private void CreateColumns(XmlDocument doc)
{
foreach (...) // loop through each node in xml document
{
if (node.Name == "Outcome")
{
var items = new List<string>() { "Resolved", "Unresolved", "Pending" };
this.dataGridView1.Columns.Add(CreateComboBoxColumn(node.Name, items));
}
else
{
this.dataGridView1.Columns.Add(String.Format("col{0}", node.Name), node.Name);
}
}
}
Then your code for creating the Outcome column would be:
private DataGridViewComboBoxColumn CreateComboBoxColumn(string colHeaderText, List<string> items)
{
var colOutcome = new DataGridViewComboBoxColumn();
colOutcome.HeaderText = colHeaderText;
colOutcome.Width = 90;
colOutcome.Items.AddRange(items.ToArray());
colOutcome.Name = String.Format("col{0}", colHeaderText);
return colOutcome;
}
You would then just call CreateColumns on the form load event and pass in your XML. You should only need to create the columns once.
My advice would be to have a similar function that will find all the SMS elements and add a new row populating it with the information in each node.
public void MyForm_Load(object sender, EventArgs e)
{
var doc = new XmlDocument(filename);
CreateColumns(doc);
CreateRows(doc);
}
Hope that helps.
Answer #2 for me, based on the updated question.
The problem you are experiencing is with the AutoGeneratedColumns functionality of the DataGridView. You will need to create your columns manually before databinding. This can be done at design-time or run-time. I prefer design-time because it gives you a bit more direction with the look/feel of the grid but either way works.
You will need to disable the AutoGeneratedColumns property of the grid:
private void Form1_Load(object sender, EventArgs e)
{
// Define your columns at run-time here if that's what you prefer
this.dataGridView1.AutoGeneratedColumns = false;
this.dataGridView1.DataSource = myDataSource;
}
I'm not sitting in front of VS so this might not compile but should give you direction.
You need to either pre-populate the ResolvedColumn with the 3-4 possible values at design-time or assign it to another datasource at runtime. If you chose the design-time approach, simply open the DataGridView "Edit Columns" dialog, find the ResolvedColumn, go to Items, and add your values ("", "Unresolved", "Pending", "Resolved"). The empty value might help the ComboBox to render if there is the possiblity of rendering the grid with SMS records that have no Outcome.
To bind the possible options at runtime do something like this:
private List<string> _outcomeDataSource;
private void Form1_Load(object sender, EventArgs e)
{
_outcomeDataSource = new List<string>;
_outcomeDataSource.Add("");
_outcomeDataSource.Add("Unresolved");
_outcomeDataSource.Add("Pending");
_outcomeDataSource.Add("Resolved");
ResolvedColumn.DataSource = _outcomeDataSource;
ResolvedColumn.PropertyName = "Outcome";
}