Binding to Dictionary/Array of objects - wpf

I have large collection of statuses(bool) that are reached by key (address)
the visual should display each status as different control (for example checkboxes, buttons, radios , etc) - each control is provided with the address of the status it will display
for example
button1 <- status[55]
checkbox1 <- status[81]
..
etc
my question is if i put INotifyPropertyChanged on whole indexer(if i do it with indexer) - if one value changes does it update all the controls or only the changed one..
I want only one status change to update only one control - not all of them. Is there a way doing this?

It will update all, in Silverlight you could construct a notification that only updates one index. I cannot think of any solution which would let you keep that structure, if you map everything to objects with key and value you could internally notify of value changes...

Related

AS3 / Animate CC: user can edit table contents

This question relates to strategy rather than specific code. I'm struggling to think of a viable approach and I'm hoping somebody may have encountered a similar challenge.
I'm building an Animate program that will:
Enable the user to draw a line on the stage (by means of a series of mouse clicks).
Capture the coordinates of each mouse click in an array, and add them to a table that is displayed and updated in the UI.
Display the resulting line.
Enable the user to edit any of the coordinates, and update all of (1) (2) and (3) accordingly.
The first three steps are working OK, but darn ... how should I make that data editable? I've put a listener on the textfield that holds the set of coordinates, but I think that's a dead end. It's just a string with line breaks, so it would be hard to edit a particular few characters and have AS3 / Animate detect what had changed.
Is there a good technique for this?
Background: was comfortable with AS1, skipped AS2 completely, and now (12 years later), I'm grappling with AS3. It's been frustrating, but ... I've built a package and it's working.
The most important concept you need is the Model-View-Controller design pattern.
model
Start with defining your model which represents the data in your application.
Do so according to your requirements.
The minimum requirement for an object to work as the model is that it notifies others when it got changed, because MVC is based heavily on the observer pattern and you want to observe when your data changes. In As3, using the built in EventDispatcher makes sense.
In your case, a nested model makes sense, that is, you have a model for each individual point and then another model that acts as a collection of all points.
Add methods to your model classes to modify the data. You want to add and remove points to the collection of points and you want to modify the coordinates of each individual point.
view
Each view receives a reference to the model object that represents the collection of points. One view might draw them into a coordinate system and connect them with lines, another might be a tabular representation.
controller
The controller knows about the model and reacts to user input on the view.
For example, when somebody clicks on the view, a new point is inserted.
Given the simplicity of your application, it might makes sense to combine view and controller, so that each view directly manipulates the model.
how should I make that data editable?
By creating methods on the model that allow you to do exactly that.
Say for example the LineView class receives the PointListModel which includes all points and stores it in a private variable
public LineView (pointList:PointListModel)
{
list = pointList;
Now it will create graphical assets to represent the points, based on the list (this should be executed whenever the PointListModel object signals a change:
private onListChanged(event:Event):void
{
for each(var point:PointModel in list)
{
// draw circle or whatever
As the creation of each visual asset in the view is based on the model, it should be possible to connect them. When the user now modifies the visual asset, the made modifications can be applied to the PointModel object. Remember: any modifications made to a model will cause it to dispatch an event that it was modified. This will inform all other views that this point was modified.
This is how dragging & dropping the circle representing a point in one view will make the coordinates of that point displayed in another view update as if they are linked together, because they are: via the shared model object.
I am still a bit hazy about how I would associate the displayed table with underlying array data. Should I make each row of the table a separate textfield with an associated listener?
This is up to you.
You could make the entire table only listen to the change event of the model that represents all points. This would cause the entire table to be updated.
Or you could make the table observe the point list model to add or remove table entries and each entry in the table observe a point in that list. This allows you to only update a single entry in the table if only one point got updated.
Keep in mind that the components of flash do a lot of this for you. Flex has even more sophisticated components. The way each entry is rendered can be defined with a custom itemrenderer.

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!

DataGrid selected item changed more quickly than data loads from DB

We have the following WPF : a datagrid with row detail template. Selected line on this grid (customer selection) is handled to trigger two queries to retrieve address and contacts data, then row detail template show these data in two tabs, each with a datagrid. All the magic is done with binding, subgrids bind to properties of main Customer object, which we have as an IObservableCollection.
The bug happens when using “move down” arrow on keyboard, selected index changes fast, so fast than queries result “come back late” to interface, thus data is incoherent to what is shown and data update cannot perform. We have an exception. So my question is : how to prevent this in a proper manner ?
Should we have a try-catch the right kind of obscure exception then do nothing (loose data that cannot be applied to interface) ?
We don’t want to wait for data to come back, if user scrolls very quickly, customer selected line should go down and no matter details aren’t shown.
Maybe we should have a delay before selected item details are retrieved ? Thus no query if selected item changes before end of delay ?
Thank you for your ideas.
Gists for code:
Xaml : https://gist.github.com/Xarkam/3b89eb614124bb2f2307
Selected index changed handling : https://gist.github.com/Xarkam/cf28844ce05fd4984807
Edit 10th of July :
I have modified the main datagrid items definition as in following gist : (add https:// prefix, I don't have enough reputation to add more links, sorry) gist.github.com/postb99/d3be79f0ef2544d685f9 (inspired from stackoverflow.com/questions/13374957/datagrid-throws-invalidoperationexception-by-scrolling and proposed answer) but problem still persists...
We've solved it, if I remember well we didn't have observable collections for every collection of objects to display.

How to cancel datagrid edit

I have a view that uses a usercontrol that contains a datagrid. The requirements for the view state that if a user deletes the value out of the "Customer Name" column then leaves the cell, don't commit the edit and change the value back to its original value - i.e. don't allow blank customer names. The usercontrol is shared code and is used between multiple applications. The edit cancellation requirement is specifically for the view mentioned above (not all applications that use the usercontrol). How do i detect the value for the Customer Name cell has been deleted and cancel the edit if the value is empty?
You can handle CellEditEnding - its Occurs before a cell edit is committed or canceled. Here you can validate current value of the cell, and if it satisfy your condition[s] you can do what ever you want. MSDN
Edit:
Its my suggestions, but I can't approve it.
You can get your new value via e.Row.Item, and cast it to object you put on DataGrid.
Another way is cast sender object right way to get access for new cell value.
+1 is to DataGridCellEditEndingEventArgs.EditingElement. Its FrameworkElement, and I think you know what it is (probably TextBox, or something else).
also I think you know what you should to do!

Detect changes to user input controls in Silverlight?

I have a childwindow with a number of Textboxes, Comboboxes, and DatePickers. I want to know if a user has changed any value in these (to know if I need to save to db)
One way I could think of doing this are in the 'on chg' event handlers and set bool. But if a user changes the value, in say a combobox, then changes back to the original this would still be seen as a change.
Are there other alternatives?
(note the project is not set up as MVVM)
If you don't use mvvm but still bind to an object then:
before the window is shown create a copy of the object, save it, and bind it to DataContext
whenever you need to know if user made any changes you can compare the saved object to DataContext (property by property)
I you don't use binding at all then:
before the window is shown save all fields that can be modified to a Dictionary
whenever you need to know if user made any changes you can compare the dictionary values to values of the fields

Resources