How to use GtkTreeView correctly - mono

I am using a TreeView with a ListStore as model. When the user clicks on a row I want to take some action but not using the values in the cells, but using the data I created the row from...
Currently I have the TreeView, the TreeModel (ListStore) and my own data (which I ironically call model)..
So the Questions are:
Is it "right" to have a model - an object representation of the data I want to display and fill a ListStore with that data to display in a TreeView, or would it be better to implement an own version of TreeModel (wrapping my data-model) to display the data?
And also:
If someone double-clicks in a row I can get the RowActivated event (using C#/Gtk#) which provides a Path to the activated row. With that I can get a TreeIter and using that I can get the value of a cell. But what is the best practice to find the data object from which the row was constructed in the first place?\
(Somehow this question got me to the first one - by thinking would getting the data object more easy if I tried to implement my own TreeModel...)

It's quite awkward/difficult to implement TreeModel, so most people simply synch the data from their "real" model into a TreeStore or ListStore.
The columns in the store do not have to match the columns in the view in any way. For example, you can have a column that contains your real managed data objects.
When you add a cellrenderer to a TreeView (visual) column, you can add mappings between its properties and the columns of the store. For example, you could map one store column to the font of a text cellrenderer, and another store column to the text property of the same cellrenderer. Each time the cellrenderer is used to render a particular cell, the mappings will be used to retrieve the values from the store and apply them to the properties of the renderer before it renders.
Here's an example of a mapping:
treeView.AppendColumn ("Title", renderer, "text", 0, "editable", 4);
This maps store column 0 to the renderer's text GTK property and maps store column 4 to the editable property. For GTK property names you can check the GTK docs. Note that the example above uses a convenience method that adds a column, adds a renderer to it and add an arbitrary number of mapping via params. To add mappings directly to a column, for example a column with multiple renderers, pack the renderers into the column then use TreeViewColumn.AddAttribute or TreeViewColumn.SetAttributes.
You can also set up a custom data function that will be used instead of mappings. This allows you to set the properties of the renderer directly, given a TreeIter and the store - so, if all the data you want to display is trivially derived from your real data objects, you could even have your store only contain a single column of these objects, and use data funcs for all the view columns.
Here's an example of a data func that does exactly what the mapping example above does:
treeColumn.SetCellDataFunc (renderer, delegate (TreeViewColumn col,
CellRenderer cell, TreeModel model, TreeIter iter)
{
var textCell = (CellRendererText) cell;
textCell.Text = (string) model.GetValue (iter, 0);
textCell.Editable = (bool) model.GetValue (iter, 4);
});
Obviously data functions are much more powerful because they enable you not only to use properties of more complex GTK objects, but also to implement more complex display logic - for example, lazily processing derived values only when the cell is actually rendered.

Related

EaselJS - Storing Shape Graphics in Database

I'm using EaselJS for a project, and have the need to store the details of various 'Shape' objects in a database, so that they can be re-drawn at another time.
My project has the 'Stage' set up so that I can drag-draw a box, which is saved into the database when completed. I intend to make it usable for drawing polygons also.
My database has a table in it called 'polyshapes', which stores the Shape.id, Shape.name, Shape.x, Shape.y, Shape.rotation. It also has a field called 'graphics' where I can store some sort of text string representing the Shape.graphics object.
When I draw a box, I'm able to store a string of the 'graphics' using: JSON.stringify(Myshape.graphics).
When I load this shape from the database, I create a new createjs.Shape() object, and then use Myshape.graphics = JSON.parse(graphicsString) to load the graphics.
Unfortunately this doesn't seem to work, as it doesn't load the required functions within the object.
I have also tried just individually updating some specific graphics attributes such as 'command', '_instructions', '_stroke', '_strokeStyle'. But I still can get the required graphic to re-draw.
Ideally, I'd only need to store the 'instructions' attribute of the Shape.graphics when I drag-draw it, and then when loading from the DB, I would just use a method to re-create those graphics when provided with the 'instructions' data. However I can't seem to see a suitable method for doing this.
I have also looked into the decodePath() graphics method. This would be useful, except that there doesn't appear to be an inverse method 'encodePath()' to allow me to serialize it in the first place.
How can I store the Shape.graphics data in a database, and then recall it later to rebuild a new Shape() object?
Thanks,
Hugh.

dgrid sorting based on "get" function

I have grid with columns with get method (an optional function that, given a data item, will return the value to render in the cell). I want to use values of this function for sorting purpose. What is the right way to do that?
No, It is not possible. The sorting is done on the data in Store/Collection. whereas, the get method is related to the particular column of the grid and is called while rendering. Sorting is done before the rendering of grid.
You probably would want to add a new property to the data/store with the values in the get function. apply sort on that property.

How to share one AJAX call between two stores?

I have two Sencha/ExtJS4 grids which use the exact same data (ie, same store.proxy.url), but each uses different filters, so each has its own separate store. The problem is I am making an unnecessary AJAX call to retrieve the extra copy to work with.
What is the recommended approach to make a single AJAX call and then share the data between two stores, for independent filtering?
potential solutions:
create two classes that extend the same store ?
use the same proxy instance ?
retrieve one store then cloning it ?
The Ext JS 4 framework seems to be built with the intention that each view receives its own store. As mentioned in other answers, your best option is to create a second store and copy all the records from one to the other.
function cloneStore(src, dest) {
var recs = src.getRange(); // returns array of records
dest.loadRecords(recs); // removes existing records before batch add
}
The exact implementation of that function may vary depending on how you need your data spread out. If each grid only needs a subset of the data to begin with, you can initialize a master store from your Ajax call, then create two sub-stores using filters directly on the store.data MixedCollection.
// Note: This function isn't exactly "good practice"
// Actual implementation may vary
function populateSubStores(master, storeA, storeB) {
var dataA = master.data.filter(/* filter criteria for store A */),
dataB = master.data.filter(/* filter criteria for store B */);
// dataA and dataB are MixedCollections of records
storeA.loadRecords(dataA.getRange());
storeB.loadRecords(dataB.getRange());
}
Or some variation thereof. This should be enough to get you started in the right direction.
If you're really gung-ho, you could create a new type of store that maintains separate MixedCollections representing filter states from different views, then return each filter state as a store with an identical interface to Ext.data.Store but with an implementation that operates on the "master" store's internal representation so that existing views can operate without overrides. But I don't recommend it.
You can create two instances of one store and then just copy data from one store to another using getRange() and add() methods. Creating two classes doesn't seem reasonable.

How do I use a GtkComboBox with objects, as opposed to strings?

The usual use for a combo box is to let it display options to the user, and then you get an OBJECT out of it. In Win32, you do it by using the CB_SETITEMDATA and CB_GETITEMDATA messages, casting between int and object pointers. In XAML, you set up a data template and the item in the list IS the object.
What is the Correct way to get this effect with a GtkComboBox?
GtkComboBox normally uses a GtkListStore as the underlaying model.
You need to create one with an extra column for the object you want to store and as you insert new items in the combo's model you also need to provide the object you want to associate with that row/item.

Specify valid values for a Core Data attribute

When I'm using Core Data I'm sometimes in a situation where I have an
attribute than can only have a specific set of possible values. As an
example let's say that you make a bug tracker and you have an entity
called Bug. Then it's possible that you want an attribute for the
state that the bug is in. Let's say that possible states are Open,
Closed and In Progress.
What I've done so far is that I've had attributes like that defined as
strings. Usually I've had a combo box or a radio group in the user
interface and the possible input values have been defined by what the
possible selections the combo box or radio group offers.
One of the problems with this approach is that it doesn't work if the
application is localized it into a different language. Then the word
which indicates the state is stored differently depending on the
current language. I need to save the selection in a language
independent manner.
How would you do that?
The proper way to do this:
In the Data Model Editor, define an Integer 16 property for your Bug entity to keep the three states. In your Bug class this will be an NSNumber object.
The actual displayed string of this number should be handled by your program. To keep this logic neatly encapsulated, add the appropriate methods to your Bug.m. For example like this:
-(NSString *)stateTitle {
NSString *title = NSLocalizedString(#"None", nil);
if ([self.state intValue]==1) return NSLocalizedString(#"Open", nil);
if ([self.state intValue]==2) return NSLocalizedString(#"Closed", nil);
if ([self.state intValue]==3) return NSLocalizedString(#"In Progress", nil);
return title;
}
Now, in your UI classes you can simply use [theBug stateTitle] to display the proper localized description e.g. in a UILabel.