Listen to events from programatically created NSView

微笑、不失礼 提交于 2020-01-03 05:11:06

问题


I have created a sidebar inside my C#/MonoMac application using an NSOutlineView and got help creating the items in the question Create NSView programatically in Xamarin Studio. But now I have made some items in the outline view editable and of course I want to detect when the items are edited and take action (edit the underlying model, or create a new one if a special "Create new" item was edited).

The items can be edited alright, either by double click or pressing Return when it's selected. But I can't figure out how to detect when an item was edited in my code. I have tried to override CommitEditing in my NSOutlineViewDataSource, and override both CommitEditing and ShouldEditTableColumn in my NSOutlineViewDelegate. Neither of these seems to get called.

I have also tried to hook up an event handler to EditingEnded to the NSTextField which is a subview to the view I get when I call MakeView in my NSOutlineViewDelegate.GetView method. But this makes the application crash. I also get a crash when I try to set the Delegate of the NSTextField and in it override the EditingEnded or TextShouldEndEditing. The crash happens as soon as I press a key after entering edit mode.

Now I am lost. It feels as if I've tried everything.

In case you want to look at the code it's here:

The model: https://code.google.com/p/yet-another-music-application/source/browse/trunk/Player/GUIs/OSX/Models/Navigation.cs

The view controller: https://code.google.com/p/yet-another-music-application/source/browse/trunk/Player/GUIs/OSX/Views/NavigationViewController.cs

Here's a crash from when I subscribe to the Changed event using this code:

    // ...
    view = outlineView.MakeView ("DataCell", this);
    ((NSTextField)view.Subviews [1]).Changed += foo;
}

private void foo(object sender, EventArgs e) {
    Console.WriteLine("changed text field.");
};
2013-08-02 08:53:32.851 Stoffi[6582:1007] -[__NSCFType controlTextDidEndEditing:]: unrecognized selector sent to instance 0x16a6c40
2013-08-02 08:53:32.852 Stoffi[6582:1007] Exception detected while handling key input.
2013-08-02 08:53:32.852 Stoffi[6582:1007] -[__NSCFType controlTextDidEndEditing:]: unrecognized selector sent to instance 0x16a6c40
2013-08-02 08:53:32.863 Stoffi[6582:1007] (
    0   CoreFoundation                      0x9966be8b __raiseError + 219
    1   libobjc.A.dylib                     0x98a8f52e objc_exception_throw + 230
    2   CoreFoundation                      0x9966fafd -[NSObject(NSObject) doesNotRecognizeSelector:] + 253
    3   CoreFoundation                      0x995b7e87 ___forwarding___ + 487
    4   CoreFoundation                      0x995b7c32 _CF_forwarding_prep_0 + 50
    5   Foundation                          0x93d36e52 __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_0 + 49
    6   CoreFoundation                      0x9962d851 ___CFXNotificationPost_block_invoke_0 + 257
    7   CoreFoundation                      0x99578e8a _CFXNotificationPost + 2794
    8   Foundation                          0x93d1f988 -[NSNotificationCenter postNotificationName:object:userInfo:] + 92
    9   AppKit                              0x972b9926 -[NSTextField textDidEndEditing:] + 405
    10  Foundation                          0x93d36e52 __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_0 + 49
    11  CoreFoundation                      0x9962d851 ___CFXNotificationPost_block_invoke_0 + 257
    12  CoreFoundation                      0x99578e8a _CFXNotificationPost + 2794
    13  Foundation                          0x93d1f988 -[NSNotificationCenter postNotificationName:object:userInfo:] + 92
    14  AppKit                              0x96fd762b -[NSTextView(NSPrivate) _giveUpFirstResponder:] + 434
    15  AppKit                              0x96fd746f -[NSTextView(NSKeyBindingCommands) insertNewline:] + 580
    16  libobjc.A.dylib                     0x98a9c5d3 -[NSObject performSelector:withObject:] + 70
    17  AppKit                              0x96fd714e -[NSResponder doCommandBySelector:] + 91
    18  AppKit                              0x96fd6f83 -[NSTextView doCommandBySelector:] + 152
    19  AppKit                              0x97067fe3 -[NSTextInputContext doCommandBySelector:] + 121
    20  AppKit                              0x97067f5d -[NSTextInputContext _handleCommand:] + 84
    21  AppKit                              0x97062929 -[NSKeyBindingManager(NSKeyBindingManager_MultiClients) interpretEventAsCommand:forClient:] + 2006
    22  AppKit                              0x97061db5 -[NSTextInputContext handleEvent:] + 1298
    23  AppKit                              0x97061825 -[NSView interpretKeyEvents:] + 205
    24  AppKit                              0x96fa43b8 -[NSTextView keyDown:] + 680
    25  AppKit                              0x971c8af1 -[NSWindow sendEvent:] + 7432
    26  AppKit                              0x971c390f -[NSApplication sendEvent:] + 4278
    27  AppKit                              0x970dd62c -[NSApplication run] + 951
    28  AppKit                              0x970805f6 NSApplicationMain + 1053
    29  ???                                 0x060c9e76 0x0 + 101490294
    30  ???                                 0x060c9c70 0x0 + 101489776
    31  ???                                 0x007beff8 0x0 + 8122360
    32  ???                                 0x007bf156 0x0 + 8122710
    33  libmono-2.0.dylib                   0x0100be52 mono_jit_runtime_invoke + 722
    34  libmono-2.0.dylib                   0x011a767a mono_runtime_invoke + 170
    35  libmono-2.0.dylib                   0x011aa1f1 mono_runtime_exec_main + 705
    36  libmono-2.0.dylib                   0x011a9401 mono_runtime_run_main + 929
    37  libmono-2.0.dylib                   0x010695e5 mono_jit_exec + 149
    38  libmono-2.0.dylib                   0x0106bb79 mono_main + 9609
    39  Stoffi                              0x0000308f main + 2047
    40  Stoffi                              0x00002885 start + 53
)

回答1:


Subscribing the event on the text field inside GetView works fine for me. The issue must exist somewhere else in your code...


Edit to add:

Be wary of doing stuff like this:

((NSTextField)view.Subviews [1]).Delegate = new PlaylistNavigationDelegate ();

And, assigning the event to an anonymous method as per your original attempt.

In this case, once control leaves the scope of GetView, the only reference to your delegate is on the unmanaged side. The Mono GC can't see that, and the delegate becomes eligible for collection. This can cause all kind of issues when the managed reference is garbage collected out from underneath Cocoa.


Working sample:

// MainWindowController...

        public override void AwakeFromNib()
        {
            rootNode = new Node(new NSString("Root Node"));

            for(int i = 1; i <=5; i++)
            {
                rootNode.Children.Add(new Node(new NSString("Child Node")));
            }

            sourceList.Delegate = new SourceListDelegate(this);
            sourceList.DataSource = new SourceListDataSource(this);

            base.AwakeFromNib();
        }

        private class SourceListDataSource : NSOutlineViewDataSource
        {
            private MainWindowController controller;

            public SourceListDataSource(MainWindowController controller)
            {
                this.controller = controller;
            }

            public override int GetChildrenCount(NSOutlineView outlineView, NSObject item)
            {
                if(item != null)
                {
                    return (int)((Node)item).Count;
                }

                return (int)controller.rootNode.Count;
            }

            public override bool ItemExpandable(NSOutlineView outlineView, NSObject item)
            {
                if(item == null)
                {
                    return false;
                }

                return !((Node)item).Leaf;
            }

            public override NSObject GetChild(NSOutlineView outlineView, int childIndex, NSObject item)
            {
                if(item == null)
                {
                    return (Node)Runtime.GetNSObject(controller.rootNode.Children.ValueAt((uint)childIndex));
                }

                Node theItem = (Node)item;
                return (Node)Runtime.GetNSObject(theItem.Children.ValueAt((uint)childIndex));
            }

            public override NSObject GetObjectValue(NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item)
            {
                if(item != null)
                {
                    return ((Node)item).Text;
                }

                return new NSString("");
            }
        }

        private class SourceListDelegate : NSOutlineViewDelegate
        {
            private MainWindowController controller;

            public SourceListDelegate(MainWindowController controller)
            {
                this.controller = controller;
            }

            public override bool IsGroupItem(NSOutlineView outlineView, NSObject item)
            {
                return (Node)item == controller.rootNode;
            }

            public override bool ShouldEditTableColumn(NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item)
            {
                return true;
            }

            public override NSView GetView(NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item)
            {
                if(IsGroupItem(outlineView, item))
                {
                    return outlineView.MakeView("HeaderCell", this);
                }

                var view = outlineView.MakeView("DataCell", this);
                var node = (Node)item;

                ((NSTextField)view.Subviews[1]).StringValue = node.Text;
                ((NSTextField)view.Subviews[1]).Changed += HandleChanged;

                return view;
            }

            void HandleChanged (object sender, EventArgs e)
            {
                Console.WriteLine("changed");
            }
        }

        private class Node : NSObject
        {
            private NSMutableArray children = new NSMutableArray();

            public NSMutableArray Children
            {
                get
                {
                    return children;
                }
            }

            public uint Count
            {
                get
                {
                    return Children.Count;
                }
            }

            public bool Leaf
            {
                get
                {
                    return this.Count == 0;
                }
            }

            public NSString Text { get; set; }

            public Node(NSString text)
            {
                this.Text = text;
            }
        }


来源:https://stackoverflow.com/questions/17889328/listen-to-events-from-programatically-created-nsview

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!