Xamarin.Mac and iOS Key-Value coding and property binding

Starting to go through the book “Cocoa Programming for Mac Os X” by Big Nerd Ranch, chapter 7 talks about Key-Value coding and observing. In the Objective-C world, simply adding an instance member to a class will allow you to set and get the value of that member using setValue:forKey: and valueForKey: methods:

// Add member variable to class
int fido;

// In constructor:
- (id)init
{
    self = [super init];
    if (self) {
        // Set the value of fido
        [self setValue:[NSNumber numberWithInt: 5] forKey:@"fido"];
        // Get the value of fido
        NSNumber n = [self valueForKey:@"fido"];
        NSLog(@"fido = %@", n);
    }
    return self;
}

However doing this in C# with Xamarin does not work out of the box and results in an exception:

// Add member to MainWindowController class
int fido;

// In constructor's shared initializer method:
void Initialize()
{
    // Set the value of fido
    this.SetValueForKey(new NSNumber(5), new NSString("fido"));
    // Get the value of fido
    NSNumber n = (NSNumber)this.ValueForKey(new NSString("fido"));
    Console.WriteLine("fido = {0}", n);
}

// Objective-C runtime exception:
[<AppDelegate 0x1e6a80> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key fido.

First, you need fido to be a property and you need to export it to the Objective-C runtime:

// Add property to class and export it
[Export("fido")]
int fido {get; set;}

So that is how to set up properties of a class to be able to set and get properties using the SetValueForKey() and ValueForKey() methods. This will allow you to bind values to user interface elements in Xcode’s Interface Builder. Lets say you do the above in MainWindowController, the default first window controller in a Xamarin.Mac template project. To try this, open MainWindow.xib in Xcode’s Interface Builder. You will note that the File’s Owner is MainWindowController:

MainWindowXibFilesOwner

To create a binding, drag a Slider control onto the window, select it and then select the Bindings tab in the inspector. Expand the Value field under the Value section and check “Bind to” and select “File’s Owner”. Then in the Model Key Path field, enter self.fido.

BindASliderToAValue

That’s it, the slider is now bound to the fido property of MainWindowController. When you move the slider, fido will be updated with the slider’s value and setting fido‘s value will move the slider. To see the effects in the console, implement the getter and setter for fido:

int _fido;

[Export("fido")]
int Fido {
    get
    {
          Console.WriteLine("fido is returning {0}", _fido);
          return _fido;
    }
     set
    {
          Console.WriteLine("setFido is called with {0}", value);
        _fido = value;
    }
}

Now the updated value of fido will be shown in the console. If you set the value of fido in code, you do need to set it using the SetValueForKey() method for the slider to recognize the change, or call WillChangeValue(“fido”) and DidChangeValue(“fido”) before and after setting the member variable _fido:

// Set fido using SetValueForKey - is reflected in the slider position
this.SetValueForKey(new NSNumber(++_fido), new NSString("fido"));
// Or call WillChangeValue and DidChangeValue
this.WillChangeValue("fido");
_fido++;
Console.WriteLine("fido is now {0}", _fido);
this.DidChangeValue("fido");

I will be adding more little hints like this as I progress through this book. Stay tuned. 🙂

2 Comments


  1. Thanks for writing this – it’s been a big help to me as I try to find my way with Xamarin.Mac.

    I noticed one small typo: You have “Modal Key Path” instead of “Model Key Path” in the text.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.