9

にコンテキストメニューを配置したいと思いますNSTableView。この部分は完了です。私がやりたいのは、右クリックしたセルの内容に基づいてさまざまなメニューエントリを表示し、特定の列のコンテキストメニューを表示しないことです。

これは:

列0、および1コンテキストメニューなし

他のすべてのセルには、次のようなコンテキストメニューが必要です。

最初のエントリ: "delete" samerow.column1.value
2番目のエントリ: "save" samecolumn.headertext

-編集-

右側は、特定のセルのコンテキストメニューがどのように表示されるかを示しています。

ここに画像の説明を入力してください

4

7 に答える 7

37

そのための代理人がいます!- サブクラス化する必要はありません

IB で をウィンドウ/ビューにドラッグすると、テーブルのアウトレットがNSTableViewあることに気付くでしょう。menu

したがって、コンテキスト メニューを実装する非常に簡単な方法は、そのアウトレットをスタブ メニューに接続し、メニューのデリゲート アウトレットをNSMenuDelegateプロトコル メソッドを実装するオブジェクトに接続することです。- (void)menuNeedsUpdate:(NSMenu *)menu

インターフェイスビルダーのスクリーンショット

通常、メニューのデリゲートは、データソース/デリゲートをテーブルに提供するオブジェクトと同じですが、テーブルを所有するビュー コントローラーである場合もあります。

これに関する詳細については、ドキュメントをご覧ください

プロトコルで実行できる巧妙な機能のバンドルがありますが、非常に単純な実装は次のようになります。

#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プロパティを提供することに注意してください (例: )representedObjectdeleteObject:

編集

注意 - サブクラスに実装- (void)menuNeedsUpdate:(NSMenu *)menuするNSDocumentと、10.8 で表示されるタイトル バーに表示される自動保存/バージョン メニューが停止します。

それはまだ10.7で動作するので、図を見てください。NSDocumentいずれにせよ、メニューデリゲートはサブクラス以外のものである必要があります。

于 2013-03-06T20:39:30.527 に答える
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;
}

それはそれを行う必要があります。ただし、コードは試していません。しかし、これはあなたにアイデアを与えるはずです。

于 2013-03-06T07:18:45.390 に答える
1

ウォーレン・バートンの答えは的を射ています。Swift で作業している場合、次のサンプル フラグメントを使用すると、Objective C から翻訳する作業を省くことができます。私の場合、NSTableView ではなく NSOutlineView のセルにコンテキスト メニューを追加していました。この例では、メニュー コンストラクターが項目を調べ、項目の種類と状態に応じてさまざまなオプションを提供します。デリゲート (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]()
}
于 2016-04-20T12:06:58.530 に答える
0

NSMenuこれは、私が見つけたカスタム/ダイナミックの最も簡単な方法であり、システムの外観(青い選択枠) も保持します。でメニューをサブクラス化NSTableViewし、設定しますmenu(for:)

重要な部分は、テーブル ビューにメニューを設定することですが、呼び出しからメニューを返しますsuper

override func menu(for event: NSEvent) -> NSMenu? {
    let point = convert(event.locationInWindow, from: nil)
    let clickedRow = self.row(at: point)
    var menuRows = selectedRowIndexes

    // The blue selection box should always reflect the
    // returned row indexes.
    if menuRows.isEmpty || !menuRows.contains(clickedRow) {
        menuRows = [clickedRow]
    }

    // Build your custom menu based on the menuRows indexes
    self.menu = <#myMenu#>

    return super.menu(for: event)
}
于 2019-02-14T10:16:31.933 に答える
0

以下は、View Controller 内でプログラムによって NSOutlineView を設定する例です。コンテキスト メニューを起動して実行するために必要な配管はこれだけです。サブクラス化は必要ありません。

以前は NSOutlineView をサブクラス化してメニューをオーバーライドしていましたが (イベントの場合: NSEvent)、ここでのグラハムの回答と上記のウォーレンの回答の助けを借りて、より簡単なセットアップになりました。

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
    }
}
于 2018-02-12T15:20:03.143 に答える
0

TheGoonie が述べたように、私も同じ経験をしました。コンテキスト メニュー項目は無効のままでした。ただし、アイテムが無効になっている理由は、「アイテムを自動的に有効にする」プロパティです。

「Auto Enables Items」プロパティをオフにします。または、プログラムで NO に設定します。

[mTableViewMenu setAutoenablesItems:NO];
于 2014-08-27T10:33:19.170 に答える