This project is read-only.

The CODE way to call a command method in the view model to set a property.

Sep 11, 2013 at 1:57 AM
Markus,

I know how to get reference an action to start a view action as a command (the tips and tricks section helped).

I tried to use the same approach for a control and ran into a slight problem.

I want to have a control that will call a method in the model and pass it the name of the property to which it is bound. Ideally, I would use some sort of self. reference, but I can even hard code the property in the xaml. Then the method in the view model would determine if the use could edit the data or not and return a value which can be used by the IsReadOnly to enable/disable read only.

I already have code that shows all the values and a button that will change the IsEnabled state so they can edit. In our UI, we may have some fields that are not editable by the current user. We may have others that are disabled because they don't apply for the given situation.

So the control passes itself, a reference, or the property name to the method and the return value is used to set the controls readonly property.

I have tried a ton of options, but can't quite seem to get this to work without having to have a readonly property in the view model for each field - which is far from ideal. I can create instances of other classes (converters, validators, etc.) and call methods in those, but I want to reference a method in the current view model/datacontext.

Any ideas?

Thanks!
Sep 12, 2013 at 2:32 AM
Edited Sep 12, 2013 at 7:34 AM
Markus,

Well, I have figured out one way to get it working. I don't know if this is the best or not, but it works. If it is an adequate solution, then at least this will be available for the next person who needs the same feature (so I have included some comments that are not necessary for you, but help others). If there is a better way, let me know (and if I find a better way, I will add that too.)

So again, the goal is to set the read only property of a control based on some criteria that can only be known when the data is available.

Here is the xaml code from the view (the . for binding path refers back to the DataContext object):
<Label Content="Readonly Test:" />
<TextBox Text="{Binding description}">
    <TextBox.IsReadOnly>
        <MultiBinding Converter="{StaticResource readOnlyConverter}"
                ConverterParameter="description">
            <Binding Path="." />
        </MultiBinding>

    </TextBox.IsReadOnly>
</TextBox>
I am using a converter because I can't find an easy way to call a method in the view model and pass it the name of the property being evaluated.

Here is the converter (I don't really think I need a multibinding converter anymore, but haven't had time to test options):
public class readOnlyConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {

        bool isReadOnly = false;
        cuEditViewModelBase viewModel = null;
        string modelProperty = parameter.ToString() ;

            if (values[0] is cuEditViewModelBase)
            {
                viewModel = (cuEditViewModelBase)values[0];

            // Ok, we have a field and an object reference
            isReadOnly = viewModel.checkForReadOnly(modelProperty);
        }

        return isReadOnly;
    } // public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        return null;
    } // object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
} // public class readOnlyConverter : IMultiValueConverter
So I have a base class (cuEditViewModelBase) that is subclassed from ViewModel which has the base functionality (although the checkForReadOnly method there always returns false - if a subclass needs to have some fields read only, it can add the code. So here is an example for the above xaml:
// Process any requests to check for read only state
public override bool checkForReadOnly(string propertyToCheck)
{
    switch (propertyToCheck)
    {
        case "description":
            {
                // Assuming that this property is set somewhere
                // or this could be code of any type
                return _someBoolValue;
                break;
            }
    }
    return false;
}
The idea is that only a few fields on the form may need this test.

Again, I have to believe that there are better ways, but this poor WPF novice hasn't been able to figure them out. Feel free to make some suggestions.
Sep 19, 2013 at 5:47 AM
Wouldn't this also be a cool use for an attached property? Let's say you create an object called ReadOnlyManager. You could then use attached property like this:
<TextBox ReadOnlyManager.ReadOnly="{Binding CompanyNameIsReadOnly}" />
So now all you need in your view model is a property called CompanyNameIsReadOnly that returns true of false depending on whatever your logic comes up with.

The beauty of this is that it results in relatively little code and it is pretty clean. You can now also arbitrarily attach this to any object you want and you are completely decoupled from the UI. You never have a need to pass the UI object back to the model or anything else like that.


Markus
Sep 20, 2013 at 10:45 PM
Markus,

2 things.
1) what if I wanted to bind the ReadOnly property to a value that could look up the property name and, if found, return true or false as appropriate, otherwise, return false (or whatever should be the default value.)
2) Where do you recommend I put the Attachedproperty code? In one of the CODE.Framework.Wpf.Layout classes (editForm.cs, FlowForm.cs, etc.), in a subclass of those (and then change the code to use the subclass), in the viewModel, or somewhere else?

Thanks,

Fletcher
Sep 23, 2013 at 7:16 AM
I would just make a new class (in my example above, I called it ReadOnlyManager). You just need to make sure it derrives from DependencyObject somehow (either directly or by subclassing from any of the other WPF objects, such as Panel).

I am not sure how you'd really "look up the property name". You'd be binding to something like ReadOnlyManager.ReadOnly. So the property name on the receiving end would always be "ReadOnly". The source could be anything, such as "CompanyNameIsReadOnly". So whatever you specify as the source is going to be it. I don't see how that source property would be more generic. If you just make it "MyReadOnlyProperty" then would would the code within that look up exactly? It already knows that it is called what it's called, but what good does that do you? So I am not sure I understand the scenario/question. The only thing I could see there is that you could make the source you bind to a dynamic object and then use the try-get method and look for whatever property the code was bound to there.


Markus