Search code examples
wpfxamldata-bindingmethodsmarkup-extensions

How do I data bind the result of a view-model method to a TextBox property?


In my view-model and model I have a method with the signature of bool IsPropertyReadOnly(string propertyName). This method determines if the currently logged in user can edit a propery value. A few users will be able to edit property values and most of the others will have read-only access.

Instead of creating a property to return the read-only status of each of the model's properties, I want to bind the result of the IsPropertyReadOny to the TextBox.IsReadOnly property.

This is how I envision the syntax:

<TextBox Text="{Binding Address, Mode=TwoWay}" 
         IsReadOnly="{Binding MethodName=IsPropertyReadOnly MethodParameter=Address}"
/>

The DataContext contains the view-model, so basically I need to bind IsReadOnly to the result of the call ((Class)this.DataContext).IsPropertyReadOnly("Address")

There is much documentation in using an ObjectDataProvider, but the object data provider creates a new object instance which is not what I want. Moreover, to use an existing instance I must make the assignment in code-behind. Again, not what I want to do.

From my research, it seems that a solution that inherits from Binding or MarkupExtension is better suited to my needs.

Any help would be greatly appreciated.


Solution

  • I suggest using a converter. Here is example. Suppose you have a simple ViewModel class:

    class ViewModel
    {
        public string Read
        { get; set; }
    
        public string ReadWrite
        { get; set; }
    
        public bool IsPropertyReadOnly(string propertyName)
        {
            return propertyName != "ReadWrite";
        }
    }
    

    To solve your problem you need to write a converter, such as:

    public class Converter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var vm = value as ViewModel;
            var functionName = (string)parameter;
    
            var result = vm.IsPropertyReadOnly(functionName);
            return result;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException("This method should never be called");
        }
    }
    

    And that's all; now you can use this converter in XAML, like:

    <Window.Resources>
        <temp:Converter x:Key="ReadOnlyMethodConverter"/>
    </Window.Resources>
    <StackPanel>
        <TextBox Text="{Binding Read, Mode=TwoWay}" 
                 IsReadOnly="{Binding Path=.,
            Converter={StaticResource ReadOnlyMethodConverter}, ConverterParameter=Read}"
        />
        <TextBox Text="{Binding ReadWrite, Mode=TwoWay}" 
                 IsReadOnly="{Binding Path=.,
            Converter={StaticResource ReadOnlyMethodConverter}, ConverterParameter=ReadWrite}"
        />
    </StackPanel>
    

    And in code-behind we just create ViewModel and set it as DataContext:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new ViewModel();
        }
    }