6

How do I add a custom convention to Caliburn.Micro for the IsEnabled property of controls - something like having NameEnabled bound to IsEnabled in parallel to Name bound to Text on a TextBox.

In a way, what I want to achieve is similar to the way that a CanSave property can be used to enable/disable a button bound to a Save method, but generic for all controls.

Anatoliy Nikolaev
  • 22,370
  • 15
  • 69
  • 68
Bevan
  • 43,618
  • 10
  • 81
  • 133

2 Answers2

14

Caliburn.Micro right now (1.3.1) doesn't really support this "multiple" conventions for the same FrameworkElement, what you have described.

EDIT:

However you can hook into the ViewModelBinder.BindProperties method and there you can implement your own extra convetion.

I went one step further and implemented a prototype which works, but it's not robust, nor elegant and probably not the correct way to do this. But it can be a starting point:

static AppBootstrapper()
{
    ConventionManager.AddElementConvention<FrameworkElement>(
         UIElement.IsEnabledProperty, 
         "IsEnabled", 
         "IsEnabledChanged");
    var baseBindProperties = ViewModelBinder.BindProperties;
    ViewModelBinder.BindProperties =
        (frameWorkElements, viewModels) =>
        {
            foreach (var frameworkElement in frameWorkElements)
            {
                var propertyName = frameworkElement.Name + "Enabled";
                var property = viewModels
                     .GetPropertyCaseInsensitive(propertyName);
                if (property != null)
                {
                    var convention = ConventionManager
                        .GetElementConvention(typeof(FrameworkElement));
                    ConventionManager.SetBindingWithoutBindingOverwrite(
                        viewModels,
                        propertyName,
                        property,
                        frameworkElement,
                        convention,                                          
                        convention.GetBindableProperty(frameworkElement));
                }
            }
            return baseBindProperties(frameWorkElements, viewModels);
       };
}
nemesv
  • 138,284
  • 16
  • 416
  • 359
  • Seems like a relatively painless way to achieve the end result I want - though I'm really suprised that Caliburn Micro doesn't have explicit support for this kind of thing, as having bindings for `IsEnabled` and `IsReadOnly` is pretty common in a UI. [I assumed my GoogleFu was at fault when I couldn't find an answer myself.] – Bevan Jan 28 '12 at 23:49
  • 1
    I've updated my solution because the original one killed the default conventions :) Now it works also with your sample `` Bindings: Text-> Property: `Name` and IsEnabled -> Property `NameEnabled`. – nemesv Jan 29 '12 at 08:23
  • @juanagui thanks for the update. I didn't know that the API changed with version 1.3.1 – nemesv Mar 28 '12 at 21:48
  • @nemesv, np, and thanks for the snippet, it was really helpful – juanagui Mar 28 '12 at 22:22
  • @nemesv how did you change the code to enable it to work with existing conventions? Did you change the elementType to FrameworkElement so it didn't conflict with existing TextBox conventions? I've just tried this code replacing FrameworkElement with TextBox and it breaks default CM Text convention. I'd like to use your solution but notice CM also has a default convention for FrameworkElement (DataContext) that I'm loathed to break. – Matt Cotton Dec 03 '12 at 12:03
  • @nemesv Is this solution up to date or have similar functionality been added to the framework? – Linus Jun 03 '14 at 11:49
  • @Linus I'm not really into Caliburn.Micro anymore... but having a quick look on the source code on github it seems nothing has changed this regard... – nemesv Jun 03 '14 at 12:31
  • This still works for Caliburn Micro v3.2.0 as of 20 Oct 2019. I just pasted the above code into public Bootstrapper() after the Initialize(); and managed to get my buttons in the View to enable and disable by setting NameEnabled in the ViewModel. Thanks. – Alan Oct 29 '19 at 15:52
-2

You can enable/disable a control by setting a boolean property in your ViewModel and you just bind to IsEnabled in XAML:

TextBox  Name="SerialNumber" IsEnabled="{Binding IsReadOnly}"...

ViewModel:
   private bool isReadOnly;
    public bool IsReadOnly
    {
        get { return isReadOnly; }
        set
        {
            this.isReadOnly = value;
            NotifyOfPropertyChange( () => IsReadOnly);
        }
    }
Derek Henderson
  • 9,388
  • 4
  • 42
  • 71
KurtP
  • 27
  • 4
    Sure, but if I wanted to manually set up all my binding I wouldn't be using Caliburn.Micro in the first place. – Bevan May 31 '13 at 02:42