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.
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();
}
}
Moreover, to use an existing instance I must make the assignment in code-behind. Again, not what I want to do.
That is not true, your choices however will be limited.
How about indexers?
private readonly Dictionary<string, bool> _PropertyReadOnlyDictionary = new Dictionary<string, bool>();
public Dictionary<string, bool> PropertyReadOnlyDictionary { get { return _PropertyReadOnlyDictionary; } }
<TextBox Text="{Binding Address, Mode=TwoWay}"
IsReadOnly="{Binding PropertyReadOnlyDictionary[Address]}" />
You could of course wrap your method in a new class which allows access via an indexer as well if you don't want to use a dictionary.
private readonly PropertyIsReadOnlyResolver _PropertyIsReadOnlyResolver = new PropertyIsReadOnlyResolver();
public PropertyIsReadOnlyResolver PropertyIsReadOnlyResolver { get { return _PropertyIsReadOnlyResolver; } }
public class PropertyIsReadOnlyResolver
{
public bool this[string propertyName]
{
get
{
return IsPropertyReadOnly(propertyName);
}
}
public bool IsPropertyReadOnly(string propertyName)
{
//...
}
}
<TextBox Text="{Binding Address, Mode=TwoWay}"
IsReadOnly="{Binding PropertyIsReadOnlyResolver[Address]}" />
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With