I am having a class that overrides ReactiveWindowController.
I need to get the reference of Observer that in the base class. But as it is not there. I decided to override it.
/// <inheritdoc/>
public override void WindowDidLoad()
{
base.WindowDidLoad();
// subscribe to listen to window closing
// notification to support (de)activation
NSNotificationCenter
.DefaultCenter
.AddObserver(NSWindow.WillCloseNotification, _ => _deactivated.OnNext(Unit.Default), Window);
_activated.OnNext(Unit.Default);
}
How can I override and change the NSNotificationCenter.... codes with mine. So that I can unregister it as per my requirement.
If there are any other ways to do it please let me know.
PS: I am new to Xamarin.
You cannot solve this if your class is derived from ReactiveWindowController. This is not a correct implementation by React.
You could create your own version of ReactiveWindowController. Copy the source and change the class name.
Related
There are lots of related answers about using menuWillOpen. They all explain that one needs to set the menu's delegate first.
This is easy when I have just one target, like a Preferences window or the main application.
But what if I have a document based app, and I need to have the active document handle menuWillOpen? Then the delegate isn't a constant any more.
What's the proper way to handle this? Do I have to set the delegate to a single object (like the AppDelegate) and then forward the call to the active view controller (but how is that done correctly)? Or is there some other elegant way?
I came up with this code which appears to work:
// This is in my AppDelegate class, and the NSMenu's delegate points to it:
- (void)menuWillOpen:(NSMenu *)menu {
// Forward to active document controller
NSWindow *mainWindow = [NSApplication sharedApplication].mainWindow;
NSResponder *r = mainWindow.firstResponder;
while (r) {
if ([r respondsToSelector:_cmd]) {
[(id<NSMenuDelegate>)r menuWillOpen:menu];
return;
}
r = r.nextResponder;
}
}
It assumes that a controller down the responder chain implements menuWillOpen:
I am having a class extending ReactiveWindowController. As in base class I see this:
// subscribe to listen to window closing
// notification to support (de)activation
NSNotificationCenter
.DefaultCenter
.AddObserver(NSWindow.WillCloseNotification,
_ => deactivated.OnNext(Unit.Default), this.Window);
Hence in my subclass I am writing the callback to remove the observer, as
public partial class SplitViewWindowController : ReactiveWindowController
{
~SplitViewWindowController()
{
Console.WriteLine("Destructor of SplitViewWindowController");
}
public SplitViewWindowController() : base("SplitViewWindow")
{
Console.WriteLine("Constructor of SplitViewWindowController");
this.Deactivated.Take(1).Subscribe(x => {
// NSNotificationCenter.DefaultCenter.RemoveObserver(NSWindow.WillCloseNotification);
// NSNotificationCenter.DefaultCenter.RemoveObserver(this);
//NSNotificationCenter.DefaultCenter.RemoveObserver(Owner);
});
}
But I am lost to find a suitable way to remove the Observer. Or I am doing something wrong here?
Why am I removing the Observer?
The answer is this SplitViewController is not dealloc-ed if any observer remains un-registered. I tried with NSWindowController, here if all the observers are removed, the deallocation works and the destructor's logs prints. If I do not remove observer even in case of subclassing from NSWindowController it doesn't call the destructor.
So the fix is to remove the Observer but how?
Save the observer created and then remove and dispose of it when required:
var observer = NSNotificationCenter.DefaultCenter.AddObserver(NSWindow.WillCloseNotification, HandleAction);
// You can also use the helper method...
// var observer = NSWindow.Notifications.ObserveWillClose(HandleEventHandler);
NSNotificationCenter.DefaultCenter.RemoveObserver(observer);
observer.Dispose();
I want to be able to react if a property of a given window or widget has been changed and found the Gtk.Widget.PropertyNotifyEvent event. The (mono) documentation says that it will be fired if any property has been changed. So I've tried to make use of it but my event handler method is never invoked:
protected void DoSomething()
{
Gtk.Window __Window = new Gtk.Window(Gtk.WindowType.Toplevel);
Gtk.Button __Button = new Gtk.Button();
__Window.Add(__Button);
__Button.PropertyNotifyEvent += this.ButtonPropertyChangedEventHandler;
... // Show the window
__Button.Label = Mono.Unix.Catalog.GetString("This is a test button");
}
protected void ButtonPropertyChangedEventHandler(object o, PropertyNotifyEventArgs e)
{
// Handle the event
}
Do I miss something? Or did I understand something very basic wrong? Or is there another approach to achieve my goal?
Property change notifications need to be explicitly enabled by setting the GDK_PROPERTY_CHANGE_MASK flag, as explained here:
https://developer.gnome.org/gtk3/stable/GtkWidget.html#GtkWidget-property-notify-event
In Gtk# you just need to add the following line to your code:
__Button.Events |= Gdk.EventMask.PropertyChangeMask;
And the handler assigned to the PropertyNotifyEvent event will start to receive property change notifications. Gtk# enables some events implicitly but all others should be enabled from user code or from the stetic designer.
i have a little problem with javafx. i added a change listener like this:
private final ChangeListener<String> pageItemSelected = new ChangeListener<String>()
{
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue){
pageGotSelected(newValue);
}
};
now to the problem: if i change an page item like this:
guiPageList.setValue(model.getCurrentTargetPage());
the event gets also(as it get by selecting something with the mouse or key) fired. is there a way to disable the event firing or another way?
i need the event only, if the element got selected by the user and not if i change it with the setValue() function...
perhaps consuming the event, but i donĀ“t know what kind of event this would be.
thanks in advance!!!
You can temporarily remove the listener and add it again:
guiPageList.getSelectionModel().selectedItemProperty().removeListener(pageItemSelected);
guiPageList.setValue(model.getCurrentTargetPage());
guiPageList.getSelectionModel().selectedItemProperty().addListener(pageItemSelected);
Alternatively you could decorate the listener with another listener implementation, the code would be something like:
class InvalidationListenerEventBlocker implements InvalidationListener {
InvalidationListener decoratedListener;
boolean block;
public void invalidated(Observable observable) {
if(!block) {
decoratedListener.invalidated(observable);
}
}
}
Add a setter for the block boolean and send the listener in through the constructor. Set block to true to stop events.
This is very old question, but I came to some solution I personally use, that's reusable and does not require storing a reference to the listener (but it needs a reference to the exposing/muffling property thou).
So first the concept: we're going to create lambda (InvalidationListener), that is going to be called only if above mentioned exposing/muffling property is set to true/false. For that we are going to define another functional interface that provides described behavior:
#FunctionalInterface
private interface ManageableInvalidationListener
extends InvalidationListener {
public static InvalidationListener exposing(
BooleanProperty expose,
ManageableInvalidationListener listener) {
return ob -> {
if (expose.get()) {
listener.invalidate(ob);
}
};
}
public static InvalidationListener muffling(
BooleanProperty muffle,
ManageableInvalidationListener listener) {
return ob -> {
if (!muffle.get()) {
listener.invalidated(ob);
}
}
}
public abstract void invalidated(Observable ob);
}
This interface defines two static methods we're going to use in our code. We pass a steering property as first argument (it will tell if listener should be called) and actual implementation to be performed, when it will be called. Please note, that there is no need to extend InvalidationListener, but I'd like to keep ManageableInvalidationListener in sync with InvalidationListener.
So we would call exposing if we need to create a (manageabale) listener that would notify the (invalidation) listener if expose property has value of true. In other case we would create the listener with muffling, if true of the steering property would mean, well, to muffle the notification.
How to use it?
//- Let's make life easier and import expose method statically
import static ManageableInvalidationListener.exposing;
// ...
//- This is the steering property.
BooleanProperty notify = new SimpleBooleanProperty(true);
//- This is our main property with the listener.
ObjectProperty<Foobar> foobar = new SimpleObjectProperty<>();
//- Let's say we are going to notify the listener, if the
// notify property is set to true.
foobar.addListener(exposing(notify, ob -> {
//- Here comes the InvalidListener code.
}));
And then somewhere in the code:
//- Listener will be notified as usual.
foobar.set(new Foobar());
//- Now temporarily disable notifications.
notify.set(false);
//- The listener will not get the notification this time.
foobar.set(new Foobar());
//- Re-enable notifications.
notify.set(true);
Hope this somehow helps. You're free to use the code in this post as pleases you.
I didin't completely understand the delegate mechanism in monotouch. Can anyone help me to understand this concept?
The question is simple. I'll try to map what I've done in Objective C in Monotouch.
For example, suppose I've creating a UIPopoverController in Objective C inside MyController. In Objective C the code is the following:
#interface MyController : UIViewController <UIPopoverControllerDelegate> {
// ...
}
// ...
#end
Inside MyController I can istantiate a UIPopoverController like the following:
UIPopoverController *popover = // ...
popover.delegate = self;
and finally methods used in the delegate.
So, what about Monotouch?
Through this code I can istantiate the UIPopoverController inside MyController class that extends UIViewController inside a specific TouchUpInside event handler:
popover = new UIPopoverController(new CustomController());
popover.PopoverContentSize = new SizeF(200f, 200f);
popover.PresentFromRect(button.Frame, containerForButtonView, UIPopoverArrowDirection.Left, true);
P.S. An important aspect is to put popover reference as a member class and not as a local variable inside the handler because the monotouch GC works well!!!
Thank you in advance.
This really has more to do with C# than MonoTouch itself. In MonoTouch, UIPopoverControllerDelegate is a class, and C# doesn't allow multiple inheritance, so you can't translate code one to one with Obj-C. There's an easier way out though (code below compiles, but obviously doesn't work):
public class MyController: UIViewController {
public void mymethod(){
var popover = new UIPopoverController();
popover.DidDismiss += HandlePopoverDidDismiss;
popover.PopoverContentSize = new SizeF(200f, 200f);
popover.PresentFromRect(button.Frame, containerForButtonView, UIPopoverArrowDirection.Left, true);
}
void HandlePopoverDidDismiss (object sender, EventArgs e)
{
Console.WriteLine("Working!");
}
}
}
As you can see, you can add an event handler to to the DidDismiss event in the popover, which will do what you want. In general, all events that in Obj-C are handled by the delegate in all controls can be used this way. You can also write the method inline, like this:
popover.DidDismiss += delegate {
//dosomething
};
Hope this is what you're looking for.
This doesn't answer your question specific to your UIPopovercontroller I think you will find this link from the Monotouch Docs useful. It explains the differences between Objective-C delegates and C# delegates with relation to Monotouch. With regards to your specific problem, I havent got time to whip up a quick test case to understand it fully but figured I'd post that link so you've got something to read in the mean time!