Add items in DataSource object afterwards - winforms

I am having the following scenario that gives me a headache for some time.
WinForms app that has a form for showing student details.
In the form we have the student name, the math as a subject and the professor that was assigned for the math subject for that particular student.
The professor field is combo box that shows all math professors. Now some of the math professors are retired, and we don't want to initially list them in the dropdown unless a specific professor is the one that was assigned to the current student.
Datasource is assigned something like this:
myDataTable.DefaultView.RowFilter = "isRetired = false";
myCombobox.DataSource = myDataTable.DefaultView;
Somewhere below, the databinding is done like this:
myCombobox.DataBindings.Add(new System.Windows.Forms.Binding("SelectedValue",...
How can this be done?
Basically, the question would be: How can I add that one record in the current DataSource object of my combobox that also uses DataBinding?
I tried using the Binding.Format Event to check if the value that comes form the database (e.value) exists in my current DataSource and if it doesn't I tried to add it, but it doesn't work at all. I can't find easy way to add this one row to the current DataSource of my combobox.
When I say it doesn't work I mean that I am only getting the math professors that are not retired that corresponds to the RowFilter property.
What I would like to see is for that particular student, even if the professor is currently retired, he (the professor) needs to be listed in this dropdown and selected. But no other retired professors should be listed, only the active ones.

Related

How to access DataGrid in Material.ui (React) and modify rows state when a cell is edited by user

I'm trying to implement a DataGrid with the community library of Material.ui.
Here my use case:
The user shall be able to modify the fields of the table -> OK
The columns and rows are states, and these should be updated every time the user edit a cell (if for example the user modify the field "Name" of the row with ID 1, then I should see this modification also in the rows state)... In this way, I can read the data written inside the DataGrid and manipulate it elsewhere.
Unfortunately, I don't find any way to implement the second point, i.e. to update the rows state when the user modify a cell of the data grid.
Can someone help me? Maybe you could indicate me a way to do it considering the basic example provided by Material.ui:
https://mui.com/x/react-data-grid/
Note: i'm writing the code in JavaScript (not TypeScript), and with community version of the material.ui library (no pro version is used).

How to quickly populate a combobox with database data (VCL)

I have read all sorts of documents across the web about what should be a farly common and painless implementation. Since I have found no consistent and slick reply (even the Embarcadero website describes some properties wrong!) I am going to post a "short" howto.
Typical, frequent use case: the developer just wants to painlessy show a couple of database-extracted information in a combo box (i.e. a language selection), get the user's choice out of it and that's it.
Requirements:
Delphi, more or less any version. VCL is covered.
A database table. Let's assume a simple table with an id and value fields.
A DataSet (including queries and ClientDataSets).
A DataSource linked to the DataSet.
A TDBLookupComboBox linked to the DataSource that shall show a list of values and "return" the currently selected id.
Firstly, we decide whether the sort order is the one we want or not and if all the items in that table must be shown. Most often, a simple ORDER BY clause or a DataSet index will be enough. In case it's not, we may add a sort_order field and fill it with integers representing our custom sort order. In case we want to show just some items, we add a visible or enabled field and use it in our SQL. Example:
SELECT id, value
FROM my_database_table
WHERE visible = 1
ORDER BY sort_order
I have defined visible as INTEGER and checking it against 1 and not TRUE because many databases (including the most used SQLite) have little support for booleans.
Then an optional but surprisingly often nice idea: temporarily add a TDBGrid on the form, link it to the same TDataSource of the TLookupComboBox and check that you actually see the wanted data populate it. In fact it's easy to typo something in a query (assuming you are using a SQL DataSet) and get no data and then you are left wondering why the TDBLookupComboBox won't fill in.
Once seen the data correctly show up, remove the grid.
Another sensible idea is to use ClientDataSets for these kinds of implementations: due to how they work, they will "cache" the few rows contained in your look ups at program startup and then no further database access (and slowdown and traffic) will be required.
Now open the TDBLookupComboBox properties and fill in only the following ones:
ListSource (and not DataSource): set it to the TDataSource connected to the DataSet you want to show values of.
ListField: set it to the field name that you want the user to see. In our demo's case it'd be the value field.
KeyField: set it to the field name whose value you want the program to return you. In our demo it'd be the id field.
Don't forget to check the TabOrder property, there are still people who love navigating through the controls by pressing the TAB key and nothing is more annoying than seeing the selection hopping around randomly as your ComboBox was placed last on the form despite graphically showing second!
If all you need is to show a form and read the TDBLookupComboBox selected value when the user presses a button, you are pretty much sorted.
All you'll have to do in the button's OnClick event handler will be to read the combo box value with this code:
SelectedId := MyCombo.KeyValue;
Where SelectedId is any variable where to store the returned value and MyCombo of course is the name of your TDBLookupComboBox. Notice how KeyValue will not contain the text the user sees onscreen but the value of the id field we specified in KeyField. So, if our user selected database row was:
id= 5
value= 'MyText'
MyCombo.KeyValue shall contain 5.
But what if you need to dynamically update stuff on the form, depending un the combo box user selection? There's no OnChange event available for our TDBLookupComboBox! Therefore, if you need to dynamically update stuff around basing on the combo box choices, you apparently cannot. You may try the various "OnExit" etc. events but all of them have serious drawbacks or side effects.
One possible solution is to create a new component inheriting from TDBLookupComboBox whose only task is to make public the hidden OnChange event. But that's overkill, isn't it?
There's another way: go to the DataSet your TDBLookupComboBox is tied to (through a DataSource). Open its events and double click on its OnAfterScroll event.
In there you may simulate an OnChange event pretty well.
For the sake of demonstration, add one integer variable and a TEdit box to the form and call them: SelectedId and EditValue.
procedure TMyForm.MyDataSetAfterScroll(DataSet: TDataSet);
var
SelectedId : integer;
begin
SelectedId := MyDataSet.FieldByName('id').AsInteger;
EditValue.Text := MyDataSet.FieldByName('value').AsString;
end;
That's it: you may replace those two demo lines with your own procedure calls and whatever else you might need to dynamically update your form basing on the user's choices in your combo box.
A little warning: using the DataSet OnAfterScroll has one drawback as well: the event is called more often than you'd think. In example, it may be called when the dataset is opened but also be called more than once during records navigation. Therefore your code must deal with being called more frequently than needed.
At this point you might rub your hands and think your job is done!
Not at all! Just create a short demo application implementing all the above and you'll notice it lacks of an important finishing touch: when it starts, the combo box has an annoying "blank" default choice. That is, even if your database holds say 4 choices, the form with show an empty combo box selected value at first.
How to make one of your choices automatically appear "pre-selected" in the combo box as you and your users expect to?
Simple!
Just assign a value to the KeyValue property we already described above.
That is, on the OnFormCreate or other suitable event, just hard-code a choice like in example:
MyCombo.KeyValue := DefaultId;
For example, by using the sample database row posted above, you'd write:
MyCombo.KeyValue := 5;
and the combo box will display: "MyText" as pre-selected option when the user opens its form.
Of course you may prefer more elegant and involved ways to set a default key than hard-coding its default value. In example you might want to show the first alphabetically ordered textual description contained in your database table or whatever other criterium. But the basic mechanic is the one shown above: obtain the key / id value in any manner you want and then assign it to the KeyValue property.
Thank your for reading this HowTo till the end!

MVVM example with editable collection where View is not using Model types directly

I have read many people say that model types should not be exposed to View, but instead it should be wrapped inside ViewModel types. Is there some example where I can see how is synchronization done between ViewModel and Model data, using Entity Framework. In particular, I need an example of editable collection (example: DataGrid or DataForm displaying Customer list).
So, something like this
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
but with editable collection data, not just read-only.
An example of what I am interested in:
1) a grid needs to show products. 3 columns are shown:
Product Code
Product Name
Product Type
Product Unit
Type is an Enum, lets say: ProductType { TypeA, TypeB }. If you find difficult to implement enum, then have it a int, its not a problem. Important thing is that Type cannot be changed if Product was already used as reference in somce other table. I am using this rule, since you can not do this with attributes (Data annotations), and needs to be done on ViewModel side.
Each property on a product class must be bound to CustomerViewModel property. You can put some rules for Code and Name:
Code Unique
Name: Required, MaxLength(30)
EDIT: my main concern is how we do synchronization between ViewModel and EF Model in batch updates. An example would be:
1) when removing Product, if we remove it from ProductViewModel collection and DBContext, what happens when user decides to cancel (made a mistake or anything)? Do we need to reload all Product from database and recreate all ProductViewModels?
2) user changes product and puts product to invalid state (remember that invalid state is still acceptable for object unless we decide to flush it to database). Then user moves cursor to another product, changes it (this product remains in valid state), and execute save command. What should we do at that point?
I have my solutions for all these questions, but I am not sure if they are correct, are there better ways, so the reason why I asked for other opinions from people that are using this method in daily work.
If you need batch update ability then application needs some updates:
1) Do not call SaveChanges from ProductViewModel. Changes should be saved only on pressing "Save" button
2) In the MainViewModel you need to maintain two additional collections: for new items and deleted items. When user presses "Save" button deleted items will be removed from database and new items will be added to database.
3) When user presses "Cancel" button all changes should be reverted. I.e. all fields should be restored to their original state, deleted items should be added back and new items should be deleted
Please see update on github: https://github.com/alexshakurin/EditableDataGridMVVM

How to add columns dynamically to a TreeView

I'm fairly new to WPF and C#. I am developing a Tool, which reads data from a DB and puts it into a TreeView.
My class model looks like this:
class Developer //Contains a name, a list of categories and some additional info
class Category //Contains a name, a list of products and some additional info
class Product //Contains a name and some additional info
For now, the tool displays all the Names in a hierarchical manner. This is done via two HierarchicalDataTemplates and a DataTemplate for the leafs.
My question now is, if anyone has an idea how to add some simple columns to the TreeView which should display some double values. The problem is, that the number of additional info(double values) is set through user input. So, if the user wants to display a range of 3 months, I need the TreeView to add 6 columns. (column count = months * 2)
Or maybe someone knows a wpf custom control which suits my needs and works under vs10 and .NET4.0.
Thanks in Advance
The basic TreeView control doesn't support columns, there are specialized controls for this. Those controls are often called TreeList or TreeListView, as it includes the features of a tree and of a ListView. There are some commercial controls (the Telerik RadTreeListView comes to mind ) and there are some free versions floating around (one is in the Windows SDK itself, another one can be found here).
I've used none of them, so I can't tell which one is best suited for your problems, but I would give the last one a try, as it looks as it would solve them.

Access 2007, Textbox search box like the Facebook name search box on the top right

so basically I have an AddCompany Form, where theres a textbox [CompanyName], i want to type a new company name in there, but meanwhile check if theres an already existing one.
So for example,say i want to type Microsoft, when i type M, the textbox shows whole bunch of other names with M, and then keeps going until I type finish typing microsoft. Basically jsut how the facebook search box works.
How do i implement this on Microsoft Access 2007?? could it be in the on dirty event/on change/On key down event???
please enlighten me!!
Thank you very much!!!
A much simpler version of the same thing can be done with the Access wizards. If you turn on the form wizards in form design and click the Combo Box button and point to a location in the header of your form, you'll automatically get a choice to create a combo box that will look up a record that matches something listed in the dropdown list.
However, keep these things in mind:
it works only when you've bound your form to an entire table (which is not recommended for large recordsets)
the code it creates is horrendously BAD.
There are a number of ways to solve this problem. If you're happy binding your form to the entire table, bookmark navigation (as with the code created by the wizard) will be fine. However, I recommend you use this code instead of the wizard code (assumes the combo box has a bound column with the PK ID of the record you're trying to find):
Private Sub MyComboBox_AfterUpdate()
If IsNull(Me!MyComboBox) Then Exit Sub
With Me.RecordsetClone
.FindFirst "[MyID]=" & Me!MyCombBox
If Not .NoMatch Then
If Me.Dirty Then Me.Dirty = False
Me.Bookmark = .Bookmark
End If
End With
End Sub
The combo box would need to use a SQL rowsource, and it would be something like:
SELECT CompanyID, CompanyName FROM Company ORDER BY CompanyName
And you'd define the combo box to have 2 columns, the first one the bound column, and you'd set the width for the first column to 0. The wizard walk you through doing this and set it up for you. The only thing it does wrong is write really bad code to do so.
Now, if you're not searching for a unique value, it gets more complicated, and you might want to use a different approach. Suppose you have a form that displays people and you want to see the ones for a particular company. In that case, you might want to filter by CompanyName. In that case, instead of doing a Find operation as outlined above, you might want to apply a filter. In that case the AfterUpdate event of your combo box would be something like this:
Private Sub MyComboBox_AfterUpdate()
If IsNull(Me!MyComboBox) Then Exit Sub
Me.Filter = "[CompanyName]=" & Chr(34) & Me!MyComboBox & Chr(34)
Me.FilterOn = True
End Sub
Now, in that case, your combo box would have but one column, with the company name and no hidden ID field, so it's somewhat different.
I don't like programming this kind of filtering as it's provided by the Access UI (you can right click on the CompanyName field and in the shortcut menu that pops up, type in the company name you want to filter for, including wildcards, ? and *). Also, it can get confusing when you try to filter when there's already a filter in place. If you're viewing just CitiCorp and then you filter by JP Morgan Chase, you won't get anything.
Instead, I'll tend to change the Recordsource of the form instead of applying a filter. This means each search will give you a new resultset and is the most efficient approach. It does make things a little bit more complicated in terms of returning to your original starting point, but in a properly-designed app, you should probably be presenting only subsets of data to the users in the first place, so the problem this approach "causes" is one that you'd need to resolve to build a proper and efficient user interface that retrieves only the data that the user really needs.
In general, this feature is called auto-completion (I've also heard find-as-you-type). There is an article giving an example of how to do this in Access on this page.
The forms in Access have event handler functions. So couldn't you use autocompletion code added to these functions for this functionality? I found a book on Google Books which discusses this.
I can think of the following code which would need to be added to the event handler function:
1. SQL to find existing potential matches
2. Code generated drop-down box to display potential matches

Resources