Question

I'm trying to implement a vertical StackPanel equivalent in my MonoMac project and am having trouble figuring it out. I am not using interface builder but creating the controls programmatically (this is a restriction). I tried using a CollectionView but all items in that control are sizing to the same height.

Looking around the internet, it seems NSTableView is the way to go, but I'm not sure how to do it, especially with C# while using a MonoMac target. CollectionView was somewhat more straightforward with the CollectionViewItem.ItemPrototype allowing me to create the views I want to render. But with an NSTableView, it seems like I can only specify a data source that returns the NSObjects I want to display. How do I grab this data and then bind them to the view I want to stick in there?

I would prefer C# code but I'm at a stage where I'll accept any and all help!

Was it helpful?

Solution

I was finally able to get it working. Here is some code for anyone who wants to try it out. Basically, we need to write NSTableViewDelegates for the required functions. This implementation also doesn't cache the controls or anything. The Cocoa API documentation mentioned using an identifier to reuse the control, or something, but the identifier field is get-only in MonoMac.

I also ended up implementing my NSTableViewDelegate functions in my data-source itself which I am sure is not kosher at all, but I'm not sure what the best practice is.

Here's the data source class:

class MyTableViewDataSource : NSTableViewDataSource
{
    private NSObject[] _data;

    // I'm coming from an NSCollectionView, so my data is already in this format
    public MyTableViewDataSource(NSObject[] data)
    {
        _data = data;
    }

    public override int GetRowCount(NSTableView tableView)
    {
        return _data.Length;
    }

    #region NSTableViewDelegate Methods

    public NSView GetViewForItem(NSTableView tableView, NSTableColumn tableColumn, int row)
    {
        // MyViewClass extends NSView
        MyViewClass result = tableView.MakeView("MyView", this) as MyViewClass;
        if (result == null)
        { 
            result = new MyViewClass(_data[row]);
            result.Frame = new RectangleF(0, 0, tableView.Frame.Width, 100); // height doesn't matter since that is managed by GetRowHeight
            result.NeedsDisplay = true;
            // result.Identifier = "MyView"; // this line doesn't work because Identifier only has a getter
        }

        return result;
    }

    public float GetRowHeight(NSTableView tableView, int row)
    {
        float height = FigureOutHeightFromData(_data[row]); // run whatever algorithm you need to get the row's height
        return height;
    }

    #endregion
}

And here's the snippet that programmatically creates the table:

var tableView = new NSTableView();
var dataSource = new MyTableViewDataSource();

tableView.DataSource = dataSource;
tableView.HeaderView = null; // get rid of header row
tableView.GetViewForItem = dataSource.GetViewForItem;
tableView.GetRowHeight = dataSource.GetRowHeight;

AddSubView(tableView);

So, it is not a perfect StackPanel because one needs to manually calculate row heights, but it's better than nothing.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top