2013-03-03 44 views
9

我想将上下文菜单放到NSTableView上。这部分完成了。我想要做的是根据右击单元格的内容显示不同的菜单条目,并且不显示特定列的上下文菜单。基于NSTableViewCell的上下文菜单

是这样的:

列0和1没有上下文菜单

所有其他细胞应该有这样的上下文菜单:

第一项: “删除” samerow.column1.value
第二项: “保存” samecolumn.headertext

希望这个问题是清楚..

感谢

CNC中

的一个,右边是上下文菜单中应该如何看起来像任何给定的细胞。

enter image description here

回答

33

这是一个代表! - 不需要子类

在IB中,如果您将NSTableView拖到窗口/视图上,您​​会注意到该表为一个menu插座。

因此,为了实现上下文菜单非常容易的方式是到该出口处的菜单的委托出口连接到短截线的菜单,并连接到其实现NSMenuDelegate协议方法- (void)menuNeedsUpdate:(NSMenu *)menu

interface builder screen shot

对象通常,菜单的委托是提供数据源/委托给表的同一个对象,但它也可能是拥有该表的视图控制器。

Have a look at the docs查找有关此

更多信息即使世界的巧妙的东西捆你可以在协议做,但一个非常简单的实现可能会像下面

#pragma mark tableview menu delegates 

- (void)menuNeedsUpdate:(NSMenu *)menu 
{ 
NSInteger clickedrow = [mytable clickedRow]; 
NSInteger clickedcol = [mytable clickedColumn]; 

if (clickedrow > -1 && clickedcol > -1) { 



    //construct a menu based on column and row 
    NSMenu *newmenu = [self constructMenuForRow:clickedrow andColumn:clickedcol]; 

    //strip all the existing stuff  
    [menu removeAllItems]; 

    //then repopulate with the menu that you just created   
    NSArray *itemarr = [NSArray arrayWithArray:[newmenu itemArray]]; 
    for(NSMenuItem *item in itemarr) 
    { 
     [newmenu removeItem:[item retain]]; 
     [menu addItem:item]; 
     [item release]; 
    }   
} 

} 

然后构建菜单的方法。

-(NSMenu *)constructMenuForRow:(int)row andColumn:(int)col 
{ 

    NSMenu *contextMenu = [[[NSMenu alloc] initWithTitle:@"Context"] autorelease]; 

NSString *title1 = [NSString stringWithFormat:@"Delete %@",[self titleForRow:row]]; 

NSMenuItem *item1 = [[[NSMenuItem alloc] initWithTitle:title1 action:@selector(deleteObject:) keyEquivalent:@""] autorelease]; 
    [contextMenu addItem:item1]; 
    // 
NSString *title2 = [NSString stringWithFormat:@"Save %@",[self titleForColumn:col]];  

NSMenuItem *item2 = [[[NSMenuItem alloc] initWithTitle:title1 action:@selector(saveObject:) keyEquivalent:@""] autorelease]; 
    [contextMenu addItem:item2]; 

return contextMenu; 
} 

如何选择实现titleForRow:titleForColumn:是你。

注意NSMenuItem提供财产representedObject允许你任意对象绑定到菜单项,从而将信息发送到您的方法(如deleteObject:

编辑

当心 - 实施- (void)menuNeedsUpdate:(NSMenu *)menu在您的NSDocument子类中将停止出现在10.8中出现的标题栏中的自动保存/版本菜单。

它仍然工作在10.7所以去图。在任何情况下,菜单代表将需要是您的NSDocument子类以外的东西。

+0

谢谢!今天会尝试!这种方法是自动调用还是必须在右击时调用它?谢谢 – sharkyenergy 2013-03-07 06:54:42

+0

很长时间以来,我写它,但它应该只是自动发生,由于菜单插座连接 – 2013-03-07 07:27:42

+0

谢谢!现在尝试,但我无法理解这一点:'//让你的数据对象提供菜单 NSMenu * newmenu = [thing menuThatMyThingProvides]; ' – sharkyenergy 2013-03-07 18:07:59

3

编辑:更好的方式来做到这一点比下面的方法,如在接受的答案是使用委托。

你可以继承你的UITableView并实现menuForEvent:方法:

-(NSMenu *)menuForEvent:(NSEvent *)event{ 
    if (event.type==NSRightMouseDown) { 
     if (self.selectedColumn == 0 || self.selectedColumn ==1) { 
      return nil; 
     }else { 
      //create NSMenu programmatically or get a IBOutlet from one created in IB 
      NSMenu *menu=[[NSMenu alloc] initWithTitle:@"Custom"]; 

      //code to set the menu items 

      //Instead of the following line get the value from your datasource array/dictionary 
      //I used this as I don't know how you have implemented your datasource, but this will also work 
      NSString *deleteValue = [[self preparedCellAtColumn:1 row:self.selectedRow] title]; 

      NSString *deleteString = [NSString stringWithFormat:@"Delete %@",deleteValue]; 
      NSMenuItem *deleteItem = [[NSMenuItem alloc] initWithTitle:deleteString action:@selector(deleteAction:) keyEquivalent:@""]; 
      [menu addItem:deleteItem]; 

      //save item 
      //similarly 
      [menu addItem:saveItem]; 

      return menu; 
     } 
    } 
    return nil; 
} 

应该这样做。尽管我还没有尝试过这些代码。但是这应该给你一个想法。

+0

谢谢你今天测试一下,让你知道! – sharkyenergy 2013-03-06 08:48:07

+0

不是一个好主意,因为它通常与MVC模式冲突。如果您需要表格视图中的值,则不应该在视图中实现菜单创建,而应该在控制器中实现。 – 2016-01-05 14:46:47

1

我也试过沃伦伯顿发布的解决方案,它工作正常。 但在我的情况下,我只好下面添加到菜单项:

[item1 setTarget:self]; 
[item2 setTarget:self]; 

设置没有目标明确导致上下文菜单保持禁用。

干杯!

亚历

PS:我会发布这作为一个评论,但我没有足够的声誉做:(

0

正如TheGoonie提到的,我也得到了同样的体验 - 上下文菜单项,分别为仍然禁止,但是对于项目被禁用的原因是“自动启用物品的财产。

使“自动启用项”财产了。或者通过编程将其设置为NO。

[mTableViewMenu setAutoenablesItems:NO]; 
1

Warren Burton的答案是现货。对于那些在Swift中工作的人来说,以下示例片段可能为您节省从Objective C转换的工作。在我的情况下,我将上下文菜单添加到NSOutlineView中的单元格而不是NSTableView。在这个例子中,菜单构造函数查看该项目并根据项目类型和状态提供不同的选项。代理(在IB中设置)是一个管理NSOutlineView的ViewController。

func menuNeedsUpdate(menu: NSMenu) { 
    // get the row/column from the NSTableView (or a subclasse, as here, an NSOutlineView) 
    let row = outlineView.clickedRow 
    let col = outlineView.clickedColumn 
    if row < 0 || col < 0 { 
     return 
    } 
    let newItems = constructMenuForRow(row, andColumn: col) 
    menu.removeAllItems() 
    for item in newItems { 
     menu.addItem(item) 
     // target this object for handling the actions 
     item.target = self 
    } 
} 

func constructMenuForRow(row: Int, andColumn column: Int) -> [NSMenuItem] 
{ 
    let menuItemSeparator = NSMenuItem.separatorItem() 
    let menuItemRefresh = NSMenuItem(title: "Refresh", action: #selector(refresh), keyEquivalent: "") 
    let item = outlineView.itemAtRow(row) 
    if let block = item as? Block { 
     let menuItem1 = NSMenuItem(title: "Delete \(block.name)", action: #selector(deleteBlock), keyEquivalent: "") 
     let menuItem2 = NSMenuItem(title: "New List", action: #selector(addList), keyEquivalent: "") 
     return [menuItem1, menuItem2, menuItemSeparator, menuItemRefresh] 
    } 
    if let field = item as? Field { 
     let menuItem1 = NSMenuItem(title: "Delete \(field.name)", action: #selector(deleteField), keyEquivalent: "") 
     let menuItem2 = NSMenuItem(title: "New Field", action: #selector(addField), keyEquivalent: "") 
     return [menuItem1, menuItem2, menuItemSeparator, menuItemRefresh] 
    } 
    return [NSMenuItem]() 
} 
0

下面是一个例子的图控制器内编程设定的NSOutlineView。这就是您需要启动和运行上下文菜单的所有工具。不需要子类。

我以前子类NSOutlineView覆盖菜单(事件:NSEvent),而是来到了一个简单的设置了Graham's答案here及以上Warren's答案的帮助。

class OutlineViewController: NSViewController 
{ 
    // ... 
    var outlineView: NSOutlineView! 
    var contextMenu: NSMenu! 

    override func viewDidLoad() 
    { 
     // ... 
     outlineView = NSOutlineView() 
     contextMenu = NSMenu() 
     contextMenu.delegate = self 
     outlineView.menu = contextMenu 
    } 
} 

extension OutlineViewController: NSMenuDelegate 
{ 
    func menuNeedsUpdate(_ menu: NSMenu) { 

     // clickedRow catches the right-click here 
     print("menuNeedsUpdate called. Clicked Row: \(outlineView.clickedRow)") 

     // ... Flesh out the context menu here 
    } 
}