0

I'm new to both Caliburn and WPF, so excuse me if it is a rather trivial question.

The scenario is the following: I have multiple controls (like buttons and textboxes - the latter is the important part). Their state (Enabled/Disabled) are dependent on a boolean property.

The first suggested method I tried was using the Can[FunctionName] convention and NotifyOfPropertyChange(() => Can[FunctionName]). It worked well with the button, but it did not work with the textbox.

How do I bind IsEnabled property to a state without using the code-behind of the View?

The code I tried in the ViewModel didn't work for the textbox:

private bool _buttonEnableState = true;

public bool ButtonEnableState
{
    get
    {
        return _buttonEnableState;
    }

    set
    {
        _buttonEnableState = value;

        NotifyOfPropertyChange(() => CanTheButton);
        NotifyOfPropertyChange(() => CanTheTextBox);
    }
}

public bool CanTheButton
{
    get
    {
        return ButtonEnableState;
    }
}

public void TheButton()
{
}

public bool CanTheTextBox
{
    get
    {
        return ButtonEnableState;
    }
}

From the View:

<Button x:Name="TheButton" Content="This is the button" ... />
<TextBox x:Name="TheTextBox" ... />

Thanks in advance!

atoth
  • 838
  • 1
  • 9
  • 23

2 Answers2

3

Have you tried the obvious?:

<Button Content="This is the button" IsEnabled="{Binding ButtonEnableState}" />
<TextBox x:Name="TheTextBox" IsEnabled="{Binding ButtonEnableState}" />

UPDATE >>>

So, continuing the conversation from the comments... now you have a public property in your AppViewModel class and an instance of that class is set as the DataContext of your view that contains the Button and TextBox controls?

Let's see if the Binding is really working or not... try changing your code to this:

<Button Content="{Binding ButtonEnableState}" />

If the Button.Content is set then the Binding works just fine and you have a different problem.

UPDATE 2 >>>

As @Charleh mentioned, you also need to make sure that you have notified the INotifyPropertyChanged interface of the change of property value:

NotifyOfPropertyChange(() => ButtonEnableState);
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • Maybe you just need a different binding path. If it does not work , then you *should* see an error in your `Output Window`... what does it say? – Sheridan Sep 04 '13 at 15:50
  • Wow, _that's_ UX! I didn't know that I should check Output window, useful! It says: BindingExpression path error: 'ButtonEnableState' property not found on 'object' ''AppViewModel' [...]. It was private (my fault), I've changed it to public, but it still does not work (at least, no error message in the output). – atoth Sep 04 '13 at 16:03
  • Have you got property change notification set up for your `ButtonEnableState`? You need to tell the UI when the property changes, if you aren't doing this then setting it to true/false in the ViewModel will do nothing (it looks like you did something like that in your original code but have you done it in the new implementation) – Charleh Sep 05 '13 at 09:26
  • 2
    @Charleh Ah, I didn't included `NotifyOfPropertyChange(() => ButtonEnableState);` in the setter of `ButtonEnableState`! Now it works that way. @Sheridan, please, edit the answer. – atoth Sep 05 '13 at 13:17
0

I don't think what I'm about to suggest is necessarily the correct way of doing things, but it may give you the result you're after.

In order to get the control to be disabled based on a Can<name> property, you need to confirm to the conventions that Caliburn uses, so in this case, supplying a function <name> should work:

public void TheTextBox()
{
}

As a result of the default conventions, I believe this will be called every time the KeyDown event is fired.

That said, you probably want to bind your text content to something, and you'll want to use the x:Name property convention to choose which property, that means you'll have to attach the TheTextBox() function in a different way, you should be able to do that using the Message.Attach property in the Caliburn namespace.

So your TextBox could look like this (where you've added the following namespace xmlns:cal="http://www.caliburnproject.org"):

<TextBox cal:Message.Attach="TheTextBox" Name="SomeTextProperty" />

Backing that up in your ViewModel, you'd have:

    // Your Enabled Property (using your existing code).
    public bool CanTheTextBox
    {
        get
        {
            return ButtonEnableState;
        }
    }

    // Your dummy function
    public void TheTextBox()
    {
    }

    // Some text property (Just for demo, you'd probably want to have more complex logic in the get/set 
    public string SomeTextProperty 
    { 
        get; set; 
    }

You should then see the Enabled/Disabled behaviour, and be use the SomeTextProperty.

I'm not entirely sure I like this way of doing things, I just had a quick play to see if it worked. The following answer might be a cleaner solution, and establishes a new re-usable convention:

Adding a convention for IsEnabled to Caliburn.Micro

As a slight aside (not a direct answer), depending on how complicated your control/form is, you could investigate using multiple Views for the same ViewModel, in the past I've set up a ReadOnly and Editable view, and used a single property on the ViewModel to toggle between the two (essentially setting the entire state of the ViewModel). There are already default conventions so you can use multiple views with relative ease.

Community
  • 1
  • 1
Chris
  • 8,268
  • 3
  • 33
  • 46
  • I didn't like the idea of adding dummy functions just to fulfil a convention. An other question remains: does it have a benefit to use only the solutions of Caliburn.Micro and not to use any of the standard WPF way? – atoth Sep 05 '13 at 13:22
  • I wasn't really a huge fan either, which is why if I tend to favour readonly `Views` over the same `Viewmodel`. I find that I get a lot of benefit from CM, it can help remove a lot of boilerplate code, and I like the Event Aggregator and command conventions. As Sheridan points out, there's no reason why you can't utilise standard WPF behaviours in conjunction with CM to achieve the desired result. The benefits you experience will depend entirely on how you like to work, and how complex your program is. – Chris Sep 05 '13 at 19:49