gtk#: Tree View object always passed as call-by-reference - mono

It seems as if my arguments in the method call of processing a Tree View is always done as call-by-reference.
I have a visible GTK "Tree View" control on a top level window. The data was written by the respective model.
Now I want to remove some of the columns (based on options set by the user) and pass the manipulated Tree View to an Export-Function.
In order to remove the columns only from the output, not from the GUI itself, I thought of copying the visible Tree View control into a temporary one, manipulating the temportary one and calling the export-functionality on the temporary one.
My problem is: even though I pass my origin, visible Tree View as referenc-by-value (as of my understanding), the origin will be manipulated and the removing of columns will be done on the visual Tree View.
It seem as if my arguments in the method call is always done as call-by-reference.
Code:
"treeview1" is the visual Gtk.Tree View...
I call my Export-function:
...
TreeView treeviewExport = SetExportViewAccordingToCheckboxes(treeview1);
ExportFile(treeviewExport);
...
In the method SetExportViewAccordingToCheckboxes() I just pass the global treeview1 as call-by-value, manipulate it internally and return the manipulated Tree View:
protected static TreeView SetExportViewAccordingToCheckboxes(TreeView tvSource)
{
TreeView tvRet = tvSource;
if (cbName == false)
tvRet.RemoveColumn( ... );
...
return tvRet;
}
But even though I have removed the columns from the internal Tree View "tvRet", my visual control "treeview1" lacks all the columns which were removed from "tvRet"; it looks like "treeview1" was passed as call-by-reference.
Question: why is that?
Note: I also tried with the keyword "in" which made no difference:
protected static TreeView SetExportViewAccordingToCheckboxes(in TreeView p_tvSource)

The problem comes here:
In the method SetExportViewAccordingToCheckboxes() I just pass the
global treeview1 as call-by-value, manipulate it internally and return
the manipulated Tree View:
protected static TreeView SetExportViewAccordingToCheckboxes(TreeView tvSource)
{
TreeView tvRet = tvSource;
if (cbName == false)
tvRet.RemoveColumn( ... );
...
return tvRet;
}
First some background. In C# terminology, value types are those that directly contain a value, while reference types are those that reference the data, instead of holding it directly.
So, int x = 5 means that you are creating the value object 5 of type integer, and storing it in x, while TreeView tree = new TreeView() means that you are creating a reference tree of type TreeView, which points to an object of the same type.
All of this means that you cannot pass an object by value, even if you want to. In the best case, you are passing the reference by value, which has no effect.
So, the next step is to copy the data, and modify the copied object instead of the original one. This is theoretically sound, but the line: TreeView tvRet = tvSource; unfortunately does not achieve that. You are creating a new reference, yes, but that reference points to the same object the original reference points to.
Now, say that we are managing objects of class Point instead of TreeView, with properties x and y.
class Point {
public int X { get; set; }
public int Y { get; set; }
}
You can create a point easily:
Point p1 = new Point { X = 5, Y = 7 };
But this does not copy it:
Point p2 = p1;
This would do:
Point p2 = new Point { X = p1.X, Y = p1.Y };
Now the original problem was that you wanted to pass a few columns to an Export() function. In that case, you only need to pass a vector of the filtered columns to the exporting function, instead of a copy of the TreeView.
void PrepareExporting()
{
var columns = new List<TreeViewColumn>();
foreach(TreeViewColumn col in this.treeView.Columns) {
if ( this.Filter( col ) ) {
columns.add( col );
}
}
this.Export( columns.ToArray() );
}
void Export(TreeViewColumn[] columns)
{
// ...
}
I think that would be easier, since it is not needed to try to achieve a pass-by-reference (impossible), nor copy the tree view.
Hope this helps.

Related

Returning distinct data for a dropdownlist box with selectlistItem

I have a field in my database with duplicates. I want to use it in a dropdown list, which has to return distinct data.
Here is the method that I created to do this:
public IEnumerable<SelectListItem> GetBranches(string username)
{
using (var objData = new BranchEntities())
{
IEnumerable<SelectListItem> objdataresult = objData.ABC_USER.Select(c => new SelectListItem
{
Value = c.BRANCH_CODE.ToString(),
Text = c.BRANCH_CODE
}).Distinct(new Reuseablecomp.SelectListItemComparer());
return objdataresult;
}
}
Here is the class I am using:
public static class Reuseablecomp
{
public class SelectListItemComparer : IEqualityComparer<SelectListItem>
{
public bool Equals(SelectListItem x, SelectListItem y)
{
return x.Text == y.Text && x.Value == y.Value;
}
public int GetHashCode(SelectListItem item)
{
int hashText = item.Text == null ? 0 : item.Text.GetHashCode();
int hashValue = item.Value == null ? 0 : item.Value.GetHashCode();
return hashText ^ hashValue;
}
}
}
Nothing is returned and I get the error below. When I try a basic query without Distinct, everything works fine.
{"The operation cannot be completed because the DbContext has been disposed."}
System.Exception {System.InvalidOperationException}
Inner exception = null
How can I return distinct data for my dropdown?
Technically, your problem can be solved simply by appending .ToList() after your Distinct(...) call. The problem is that queries are evaluated JIT (just in time). In other words, until the actual data the query represents is needed, the query is not actually sent to the database. Calling ToList is one such thing that requires the actual data, and therefore will cause the query to be evaluated immediately.
However, the root cause of your problem is that you are doing this within a using statement. When the method exits, the query has not yet been evaluated, but you have now disposed of your context. Therefore, when it comes time to actually evaluate that query, there's no context to do it with and you get that exception. You should really never use a database context in conjuction with using. It's just a recipe for disaster. Your context should ideally be request-scoped and you should use dependency injection to feed it to whatever objects or methods need it.
Also, for what it's worth, you can simply move your Distinct call to before your Select and you won't need a custom IEqualityComparer any more. For example:
var objdataresult = objData.ABC_USER.Distinct().Select(c => new SelectListItem
{
Value = c.BRANCH_CODE.ToString(),
Text = c.BRANCH_CODE
});
Order of ops does matter here. Calling Distinct first includes it as part of the query to the database, but calling it after, as you're doing, runs it on the in-memory collection, once evaluated. The latter requires, then, custom logic to determine what constitutes distinct items in an IEnumerable<SelectListItem>, which is obviously not necessary for the database query version.

How do I add a lazy loaded column in EntitySpaces?

If you do not have experience with or aren't currently using EntitySpaces ("ES") ORM this question is not meant for you.
I have a 10 year old application that after 4 years now needs my attention. My application uses a now defunct ORM called EntitySpaces and I'm hoping if you're reading this you have experience or maybe still use it too! Switching to another ORM is not an option at this time so I need to find a way to make this work.
Between the time I last actively worked on my application and now (ES Version 2012-09-30), EntitySpaces ("ES") has gone through a significant change in the underlying ADO.net back-end. The scenario that I'm seeking help on is when an entity collection is loaded with only a subset of the columns:
_products = new ProductCollection();
_products.Query.SelectAllExcept(_products.Query.ImageData);
_products.LoadAll();
I then override the properties that weren't loaded in the initial select so that I may lazyload them in the accessor. Here is an example of one such lazy-loaded property that used to work perfectly.
public override byte[] ImageData
{
get
{
bool rowIsDirty = base.es.RowState != DataRowState.Unchanged;
// Check if we have loaded the blob data
if(base.Row.Table != null && base.Row.Table.Columns.Contains(ProductMetadata.ColumnNames.ImageData) == false)
{
// add the column before we can save data to the entity
this.Row.Table.Columns.Add(ProductMetadata.ColumnNames.ImageData, typeof(byte[]));
}
if(base.Row[ProductMetadata.ColumnNames.ImageData] is System.DBNull)
{
// Need to load the data
Product product = new Product();
product.Query.Select(product.Query.ImageData).Where(product.Query.ProductID == base.ProductID);
if(product.Query.Load())
{
if (product.Row[ProductMetadata.ColumnNames.ImageData] is System.DBNull == false)
{
base.ImageData = product.ImageData;
if (rowIsDirty == false)
{
base.AcceptChanges();
}
}
}
}
return base.ImageData;
}
set
{
base.ImageData = value;
}
}
The interesting part is where I add the column to the underlying DataTable DataColumn collection:
this.Row.Table.Columns.Add(ProductMetadata.ColumnNames.ImageData, typeof(byte[]));
I had to comment out all the ADO.net related stuff from that accessor when I updated to the current (and open source) edition of ES (version 2012-09-30). That means that the "ImageData" column isn't properly configured and when I change it's data and attempt to save the entity I receive the following error:
Column 'ImageData' does not belong to table .
I've spent a few days looking through the ES source and experimenting and it appears that they no longer use a DataTable to back the entities, but instead are using a 'esSmartDictionary'.
My question is: Is there a known, supported way to accomplish the same lazy loaded behavior that used to work in the new version of ES? Where I can update a property (i.e. column) that wasn't included in the initial select by telling the ORM to add it to the entity backing store?
After analyzing how ES constructs the DataTable that is uses for updates it became clear that columns not included in the initial select (i.e. load) operation needed to be added to the esEntityCollectionBase.SelectedColumns dictionary. I added the following method to handle this.
/// <summary>
/// Appends the specified column to the SelectedColumns dictionary. The selected columns collection is
/// important as it serves as the basis for DataTable creation when updating an entity collection. If you've
/// lazy loaded a column (i.e. it wasn't included in the initial select) it will not be automatically
/// included in the selected columns collection. If you want to update the collection including the lazy
/// loaded column you need to use this method to add the column to the Select Columns list.
/// </summary>
/// <param name="columnName">The lazy loaded column name. Note: Use the {yourentityname}Metadata.ColumnNames
/// class to access the column names.</param>
public void AddLazyLoadedColumn(string columnName)
{
if(this.selectedColumns == null)
{
throw new Exception(
"You can only append a lazy-loaded Column to a partially selected entity collection");
}
if (this.selectedColumns.ContainsKey(columnName))
{
return;
}
else
{
// Using the count because I can't determine what the value is supposed to be or how it's used. From
// I can tell it's just the number of the column as it was selected: if 8 colums were selected the
// value would be 1 through 8 - ??
int columnValue = selectedColumns.Count;
this.selectedColumns.Add(columnName, columnValue);
}
}
You would use this method like this:
public override System.Byte[] ImageData
{
get
{
var collection = this.GetCollection();
if(collection != null)
{
collection.AddLazyLoadedColumn(ProductMetadata.ColumnNames.ImageData);
}
...
It's a shame that nobody is interested in the open source EntitySpaces. I'd be happy to work on it if I thought it had a future, but it doesn't appear so. :(
I'm still interested in any other approaches or insight from other users.

Javafx: updating cell using choicebox in the GUI doesn't work

I have a problem in a Tableview created using javafx. I have set the edititable="true" on the fxml file of the tabel, then in the controller I execute
#FXML
private TableColumn<ARule,Object> rankCol;
rankCol.setCellValueFactory(new PropertyValueFactory<ARule, Object>("label")); rankCol.setCellFactory(ChoiceBoxTableCell.forTableColumn(Main.getlabelSample()));
rankCol.setOnEditCommit(e -> {System.out.println("something happens!");});
To create in the column rank, a choicebox to change te value of the property.
The ARule has a property field and the getter and setters:
private SimpleObjectProperty label;
public SimpleObjectProperty labelProperty() {
return label;
}
public void setLabel(Object label) {
this.label.set(label);
}
public Object getLabel(){
return this.label.getValue();
}
The function Main.getlabelSample() retrun this object filled with Strings or Integer
private static final ObservableList<Object> labelSample = FXCollections.observableArrayList();
The problem is that in the interface I can edit the column and it displays the correct value in the labelSample list, the problem is that it doesn't change the value of the ARule object, this is highlighted by the missing call of the setOnEditCommit handler. The value on the GUI is the new one selected but the value saved on the items in the table is the old one.
I have also a separated button to change the value of that column on the selected row and if I trigger that, the values changes for "real" (both on the GUI and on the model).
What could be the error in the code?
The default edit commit behavior of the column is set as the onEditCommit property. If you call
rankCol.setOnEditCommit(...);
then you set this property to something else, i.e. you remove the default behavior.
If you want to add additional behavior to the default, use addEventHandler(...) instead of setOnEditCommit(...):
rankCol.addEventHandler(TableColumn.editCommitEvent(), e -> {
System.out.println("Something happens");
});
Find the answer the line of code:
rankCol.setOnEditCommit(e -> {System.out.println("something happens!");});
for some reason overwrite the default behaviour of updating the cell changing the code into
rankCol.setOnEditCommit(e -> {
e.getTableView().getItems().get(e.getTablePosition().getRow()).setLabel(e.getNewValue());
System.out.println("Something happens!");});
Resolved the problem. At the moment I don't know why this is happening.

cellBrowser placed in composite - do I have to attach directly to the rootpanel (or rootlayoutpanel)

when using a cellbrowser and adding that widget to a flowpanel (to be placed wherever, downstream), for some reason the end result is dead (a blank screen)...vs if I add directly to the rootpanel (or layout panel)
Also had the same problem. I had to use a <g:HTMLPanel> as the parent of the CellBrowser (as seen in the GWT Showcase).
Do you have some sample code that will reproduce this?
below is the code for the composite...essentially, what I'd like to do is in another class, attached this composite to a flowpanel and do whatever with it...but, the reality is, I have to attach is directly to the RootPanel (or RootLayoutPanel)...any other abstraction causes it to bail
for example
FlowPanel fp = new FlowPanel();
V2_M76Rolodex v = new V2_M76Rolodex();
fp.add(v); // not going to work
RootPanel.get.add(v) works
public class V2_M76Rolodex extends Composite {
/*
a bunch of code here for getting data and
populating the tree - works, not at issue or relevant
*/
public V2_M76Rolodex() {
TreeViewModel model = new CustomTreeModel();
CellBrowser browser = new CellBrowser(model, null);
browser.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED);
browser.addStyleName("rolodex_cell_browser");
initWidget(browser);
}
}

Recursively listing, and storing, directory contents

I know how to recursively list directory contents. I will be using Snow Leopard's enumeratorAtURL:includingPropertiesForKeys:options:errorHandler: method to do this.
However I want to store my findings into a object hierarchy (of, say, objects of a custom FileOrDirectory class that has isLeaf, children, and count attributes).
I need to pre-load the directory and file structure into such a object hierarchy, in order to do whatever I want with NSTreeController and whatnot. I guess the trickiest thing here is to get the children attribute correct in the object hierarchy.
Any ideas?
If I understand your question correctly, you could solve this by writing a recursive function that takes the current node (a file or a folder) and returns an object representing it's structure.
This is Java, but maybe it conveys my idea. I have omitted isLeaf and count, as their values can be derived from the children.
class FileOrFolder
{
String name;
FileOrFolder[] children;
boolean isFile;
}
private FileOrFolder traverse(File file)
{
FileOrFolder fof = new FileOrFolder();
fof.name = file.getAbsolutePath();
if (file.isDirectory())
{
String[] children = file.list();
fof.children = new FileOrFolder[children.length];
for (int i = 0; i < children.length; i++)
{
fof.children[i] = traverse(new File(fof.name + "/" + children[i]));
}
}
fof.isFile = file.isFile();
return fof;
}