NSTableView set column to disabled - nstableview

I have a NSTableView tied to a NSArrayController. The first column are checkboxes. I want to have the users select the rows to perform an action on via the checkboxes and when the user clicks the "Run" button I want to set the state of that column to be un-editable.
How do I go about this?
Thanks!

rather than disabling the column I disable the individual checkbox cells in my app based on a global app state.
Preconditions:
1.) your "Run button" sets some sort of a global state in your controller class. For me its a "Play" button that sets a member in my controller like:
if ([self gotoNextSong])
_currentPlayMode = PLAYMODE_PLAY;
else
_currentPlayMode = PLAYMODE_STOP;
2.) Your controller is already a delegate to the NSTableView. If not:
In your MyController.h:
#interface MyController : NSObject <NSTableViewDelegate>
In your MyController.m make your controller to a delegate of the table - e.g. in your init: method
[self.myTableView setDelegate:self];
3.) You know the identifier of the column that holds the checkboxes. If not, either its the automatic name like column1, column2, ... or you can set it in Interface Builder while the column is active in Identity Inspector / Identity / Identifier. In my case the column name is "checkboxes"
Solution:
1.) Add a delegate method for the table to inform you when the cells are rendered. Inside this method enable or disable the cells of the checkbox-row as desired
- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn: (NSTableColumn *)aTableColumn row:(int)rowIndex
{
if(([[aTableColumn identifier] isEqualToString:#"checkboxes"]))
{
if (_currentPlayMode == PLAYMODE_PLAY)
[aCell setEnabled:NO];
else
[aCell setEnabled:YES];
}
}
2.) Make sure that your RUN-button not only sets the global state (in my case _currentPlayMode), but also force the NSTableView to redraw afterwards:
[self.myTableView setNeedsDisplay:YES];

Related

Appmaker Input Form, change options based on earlier dropdown choices

Hypothetical:
Say i'm having someone order a cake and they choose vanilla or chocolate, and then they have to choose a frosting
if vanilla: strawberry or buttercream
if chocolate: mocha, dark chocolate or buttercream.
Right now the frosting value in the data model can accept all four options, so all four show up in the dropdown.
a) is there a way to change the binding for the second dropdown after the first is chosen?
This can be done without having the data saved in any datasource. Simply do the following...
Suppose the first dropdown is named primaryDropdown and the second dropdown is called secondaryDropdown. In the primaryDropdown options set the following:
["Vanilla", "Chocolate"]
Also, make sure to uncheck the option allowNull and on the onAttach event handler, place the following:
widget.value = "Vanilla";
Now we move on to the secondaryDropdown. Here, we will do a binding so place the following in the options value:
getAvailableOpts(#widget.root.descendants.primaryDropdown.value)
In the client script, we need to make sure that function exists so please paste the following in any client script:
function getAvailableOpts(primaryValue){
var options;
switch(primaryValue){
case "Vanilla":
options =["Strawberry","Buttercream"];
break;
case "Chocolate":
options = ["Mocha","Dark Chocolate","Buttercream"];
break;
default:
options = [];
}
return options;
}
From here, you are good; However we can still make it better. For that, make sure to also uncheck the allowNull option for the secondaryDropdown and then we need to add some logic in the onValueChange event handler of the primaryDropdown.
widget.root.descendants.secondaryDropdown.value = widget.root.descendants.secondaryDropdown.options[0];
References:
https://developers.google.com/appmaker/ui/input-widgets#dropdown
https://developers.google.com/appmaker/ui/binding#bindings
https://developers.google.com/appmaker/scripting/client#use_scripts_in_binding_expressions
Im guessing you set the dropdown "possible value" options in the data source and you have those fields as just string fields in your table.
the quick and dirty way to do this is go to the Cake drop down and on the Property Editor>Events>OnValueChange select Custom Action and use this Code
if(widget.value==="Vanilla"){
widget.root.descendants.Field.options = ['Strawberry','Buttercream'];
}else if (widget.value === "Chocolate"){
widget.root.descendants.Field.options = ['Mocha','Dark Chocolate','Buttercream'];
}
"Field" here is replaced with the name of the dropdown box for your frostings
also go to the property editor of the frostings dropdown and delete everything in the options field there.
I was trying to solve for a similar situation and created an additional table to serve as a lookup.
I have a drop down for CATEGORY with possible options set on the field in the main data source.
Then I have a second table with 2 Columns: CATEGORY, and SUB_CATEGORY.
On the entry form I have options for the SUB_CATEGORY drop down set to SUB_CATEGORY on the CATEGORY_MATRIX lookup table.
Then for an onValueEdit event on the CATEOGRY drop down I have a script to reload the CATEGORY_MATRIX table source based on the value of the CATEGORY drop down in the query so when the CATEGORY is selected only valid SUB_CATEGORY options are shown in the second drop down.
var datasource = app.datasources.CATEGORY_MATRIX
datasource.query.filters.CATEGORY._equals = newValue;
datasource.load();
There may be more efficient ways to do this but the benefit is I could create another form page and dynamically add new combos so there isn't any required code change as new combos are needed.

How to handle buttonclicked event of DDDW?

Situation:
I have a button in my DDDW and i want to capture buttonclicked event.
Problem:
when i click on the button in DDDW the buttonclicked event of DW Control is not fired and ItemChanged event is fired for DW control.
Question:
How do i capture buttonclicked event for the button in DDDW? Or is there any other way i can delete a row from DDDW when delete button is clicked for a particular row?
PowerBuilder 12.5
According to the PB help, a DataWindowChild has no events :|
But, that doesn't mean we still can't hook into it via the DW control's itemchanged event. Note: this is a hack, and underwent some very-limited testing. But, it demonstrates a point, I guess.
Here's what I did:
Created a DataWindow with the code and name columns, and a computed field (for the red X) named delete_button
Created another DataWindow and painted that DW as a DDDW on there, named profession
In my window control's open event, I got the DDDW from the DW and stuck it in an instance variable:
dw_1.GetChild("profession", REF idwc_profession)
Then, coded the itemchanged event for the DW control:
// dw_1::itemchanged
//
// - DDDW is named "profession"
IF dwo.Name = "profession" THEN
IF IsValid(idwc_profession) THEN
string ls_clickedobject
// Get the DataWindowCHILD object where the pointer was clicked:
ls_clickedobject = idwc_profession.GetObjectAtPointer()
IF IsNull(ls_clickedObject) OR (ls_clickedobject = "") THEN RETURN
// Return from GetChild is <column name>~t<row number>; let's get
// the position of the tab character so we can parse it
long ll_tabPos
ll_tabPos = Pos(ls_clickedObject, "~t")
IF ll_tabPos > 0 THEN
string ls_clickedDddwColumn
ls_clickedDddwColumn = Trim(Left(ls_clickedObject, ll_tabPos - 1))
// Check to see if we've clicked on the computed field with the delete button
IF Lower(ls_clickedDddwColumn) = "delete_button" THEN
long ll_clickedDddwRow
// grab the row we want to delete
ll_clickedDddwRow = Long(Trim(Right(ls_clickedObject, Len(ls_clickedObject) - ll_tabPos)))
IF ll_clickedDddwRow > 0 THEN
// delete the row from the DDDW
idwc_profession.DeleteRow(ll_clickedDddwRow)
SetNull(data) // reset our data
END IF
END IF
END IF
END IF
END IF
RETURN
Also note that you may have to play around with the return value from itemchanged to get it to do what you want. And, if you want to automatically dropdown the DDDW again after the deletion happens, you'd might be able to use the Send() method to do so (I don't know the right "number" for that, though).

Add grid columns dynamically

I created a form with a single data source: InventJournalTable.
I also added a grid on it and two field from the data source : JournalType and JournalId
The ActionPane has a button and in its clicked event handler I am trying to do the following:
1. add a new data source and join it with the current one on JournalId
2. add to fields from the newly added data source to the current Grid.
This is what I have until now ... just for testing.. I have tried to access data source and add a range. It works nice, maybe the join is working too, but how can I add those two fields?
void clicked()
{
Query query;
QueryBuildDataSource qbdsInventJournalTable;
QueryBuildDataSource qbdsvwInventJournals;
super();
query = InventJournalTable_ds.query();
qbdsInventJournalTable = query.dataSourceTable(tableNum(InventJournalTable));
qbdsInventJournalTable.addRange(fieldNum(InventJournalTable, JournalType)).value(queryValue(InventJournalType::LossProfit));
qbdsvwInventJournals = qbdsInventJournalTable.addDataSource(tableNum(vwInventAdjJrnlCostAmount));
qbdsvwInventJournals.addLink(fieldNum(InventJournalTable, JournalId), fieldNum(vwInventAdjJrnlCostAmount, JournalId));
qbdsvwInventJournals.joinMode(JoinMode::OuterJoin);
//gridOverview.addDataField(
InventJournalTable_ds.executeQuery();
}
One more thing, I plan to add another button named "Remove details" which will remove the second data source and the grid should return to its initial state.
Am I on the right track at least? Can I get some hints about this?
Instead of dynamically adding the datasource/fields/etc have you considered just adding them on the form, but disabling the joined datasource until you need it? Seems to me like a simpler/cleaner solution.
See here:
http://olondono.blogspot.com/2008/06/how-to-enable-or-disable-joined.html

Combobox selection inside datagrid

I have a datagrid. In this datagrid I have a combobox item editor. This datagrid also has multiple columns where a user inputs numbers in each column. These numbers are then calculated by formula where the sum is posted in the "total" column. In this combobox there are two options for the user to choose from and each option has a different formula for calculating the inputted numbers. What I want is for when a user chooses "option 1" one formula is used to do the calculation, when "option 2" is chosen by the user then formula two is used to the calculation.
Here's an example:
Combobox Option 1 (formula 1) is chosen by user = (Column2 - Column1) x column3 = "total" column
Combobox Option 2 (formula 2) is chosen by user = (Column1 - Column2) x column3 ="total" column
I realize you would use a conditional such as "if else" statement, but im just not sure how to do it. I've been trying to implement this for a while with no success so any help or suggestions would be greatly appreciated.
Listen for the combobox's change event and implement the formula calculation in the change event handler according to the selectedItem.
public function changeEventHandler(event:Event){
if(ComboBox(evt.target).selectedItem.label == forumla1) {
//logic
} else if(ComboBox(evt.target).selectedItem.label == formula2) {
//logic
} else {
//do nothing
}
}
That's interesting. You cannot add listeners directly because item renderers are reused and don't keep their identity. Some thoughts on problem:
when combobox' selected item is changed, it dispatches bubbling event EVENT.CHANGE.
you should make custom renderer for computed columns. When renderer is added to datagrid (use EVENT.ADDED), use owner property (that should be datagrid) to add listener to EVENT.CHANGE. Check that you getting that event (change renderer's text to "got it", for example).
now all your computed cells are getting notification when any combobox changed. First, you need to discard events from rows other than item's row. To do that, renderer needs to know its own rowIndex - see Creating custom List renderers, item 2. Compare rowIndex and datagrid's selected index to bail out if they don't match.
now you have combobox in event.target, rowIndex and datagrid - that should be enough to get needed formula and data from datagrid's columns.

ASP.NET Dynamic Data setting the default value of a Dropdown list

I have a dropdown list (FK) which I would like to set and display a default value based on a login userid. Can you please tell me how to do it? I only want to affect the dropdown filter that appear at the top above the Gridview.
Thanks
Nikos
If you only want this functionality in the DropDown list that appears in the Filter Criteria section, just modify the URL by adding the QueryString parameters you would like to filter by. The DynamicFilter will pick up the values from the QueryString and set the DropDown lists accordingly. (fyi. this is the same functionality that the ForeignKey.ascx FieldTemplate provides)
It would be nice if there was a better way to actually create this URL (instead of using string), but as of right now, any solution I provide is probably going to break in a subsequent version.
example (in page_load)
Response.Redirect("~/Employees/List.aspx?ReportsTo=1234");
Is this a universal change, or just for one foreign key relationship?
Assuming it is for just one foreign key relationship, you could create a new FieldTemplate, to be used just for that relationship. The New FieldTemplate would be a copy of the default "ForeignKey" FieldTemplate. In the New FieldTemplate modify the OnDataBinding (or Page_PreRender) event to set the "default value" of the DropDownList.
Then, to force the New FieldTemplate to be used for that relationship, you would need to use the System.ComponentModel.DataAnnotations.UIHint attribute on the member of your Entity Class that represents that foreign key relationship. (links below)
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.uihintattribute.uihint.aspx
or http://www.asp.net/learn/3.5-SP1/video-291.aspx (around 07:30 mins)
For a little help, you could take a look at the DynamicData Futures release on CodePlex. Specifically the "Populate Insert templates with values from filters" section. http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=14475
I have figured out a workaround, but am open to a more elegant solution.
I edited the FilterUserControl.ascx.cs by inserting the following line in the Page_Init after PopulateListControl(DropDownList1);
DropDownList1.SelectedIndex = DropDownList1.Items.IndexOf(DropDownList1.Items.FindByText("Bob")); // Username is hardcoded just for test
This seems to work but I would prefer using the custom Partial Entity Class with metadata to solve this if possible.
I have solved this in an application I am working on, in your insert view template code behind:
In ItemCreated event for the details view:
foreach (DetailsViewRow row in DetailsView1.Rows)
{
foreach (Control ctl in row.Controls)
foreach (Control c in ctl.Controls)
foreach (Control x in c.Controls)
{
if (x.ClientID.Contains("tblName"))
{
foreach (Control y in x.Controls)
{
if (y.ClientID.Contains("DropDownList"))
{
ddl = y as DropDownList;
ddl.SelectedValue = Convert.ToString(UserId);
ddl.Enabled = false;
}
}
}
}
}
With this code, when a user is logged in and they go to insert some entity (tblName) the drop down list (fk to userId) is already selected and disabled.

Resources