0

I'm working on a WPF app using MVVM where I'm reusing a view model for viewing/creating/editing/copying a business object (called Flow). There are around 25 fields on the view and some of them need to be disabled depending on whether the user is viewing/editing/... the flow. In an attempt to keep the view and the view model clean I've come up with the following solution:

  • I use an enum GuiAction that contains all the different GuiActions (FlowView, FlowEdit, etc..)
  • I have a property on the Flow view model of type GuiAction that represents whether we're viewing/editing/..
  • All the fields on the flow have their IsEnabled attribute databound to the GuiAction property with a converter ControlAvailability taking the name of the property as a parameter:

    <CheckBox
        IsEnabled="{Binding GuiAction,
            Converter={StaticResource ControlAvailability},
            ConverterParameter=Runnable}"></CheckBox>
    
  • ControlAvailability receives both the GuiAction and the parameter name and from that it should return true or false, enabling or disabling the control.

As for the logic in ControlAvailability, my initial idea to look up availability was to use a two-tier switch statement like so:

public object Convert(object value, ..., object parameter, ...)
{
    GuiAction guiAction = (GuiAction)value;
    string control = (string)parameter;

    switch (guiAction)
    {
        case GuiAction.FlowView:
            switch (control)
            {
                case "Runnable":
                    return false;
                    break;
                case "Path":
                    return false;
                    break;
                ...
            }
            break;
        case GuiAction.FlowEdit:
            switch (control)
            {
                case "Runnable":
                    return true;
                    break;
                ...
            }
            break;
        ...
    }
}

but with 7 GuiActions and 25 controls this will turn into hundreds of lines of code.. I feel that there has to be a better way to do this.. probably in a flat file somewhere.

So.. Is the massive switch statement the way to go or is there a better solution?

jbb
  • 346
  • 3
  • 9
  • 1
    How about not reusing the same view model but create a specific one for each action? A class should only have a single responsibility. If the same class may be used in many different scenarios depending on the values of its properties your switch-statement will inevitably become large. – mm8 Feb 08 '17 at 13:16
  • One of the strengths of the MVVM pattern is this separation making it possible to reuse viewmodels for diffferent views. Having multiple Flow view models for each scenario, of which there are 7, would mean that any modifications to the view model would have to be done 7 times. Then I'd rather have a large switch somewhere :) I'm just thinking if this data structure (essentially a two-dimensional matrix) can be expressed in a more robust way.. – jbb Feb 08 '17 at 14:51

2 Answers2

0

I think that you need only this in the converter:

public object Convert(object value, ..., object parameter, ...)
{
    GuiAction guiAction = (GuiAction)value;
    return guiAction == GuiAction.FlowEdit;
}

And bind only the controls that you want to disable so you do not need the binding parameter.

Update:

You can have a dictionary in a static variable declared in another file:

public static class StaticData
{
    public static Dictionary<GuiAction, Dictionary<string, bool>> dict = new Dictionary<GuiAction, Dictionary<string, bool>>
        {
            {
                GuiAction.FlowView, new Dictionary<string, bool>
                {
                    {"Runnable", false},
                    {"Path", false}
                }
            },
            {
                GuiAction.FlowEdit, new Dictionary<string, bool>
                {
                    {"Runnable", true}
                }
            }
        };
}

And then you can convert like this:

public object Convert(object value, ..., object parameter, ...)
{
    GuiAction guiAction = (GuiAction)value;
    string control = (string)parameter;
    return StaticData.dict[guiAction][control];
}
dvjanm
  • 2,351
  • 1
  • 28
  • 42
  • That would be fine if there were only FlowView and FlowEdit, but I have 7 different GuiActions that disable various controls on the form – jbb Feb 08 '17 at 14:46
  • @jbb Based on your requirement, I updated my answer. – dvjanm Feb 08 '17 at 15:55
  • This solution does solve the problem but it's relatively similar to the switch solution. The main problem I have with that and this solution as well is maintainability. If I add a control to the form I have to go to seven different places in the code file and fill it in. – jbb Feb 10 '17 at 10:22
0

I ended up simply using a CSV file to store this data. It's ideal for storing two-dimensional matrices like this and it's easy to implement. Visual Studio does not have a CSV editor but all my team members have Microsoft Excel which can open and edit CSV files pretty conveniently. I implemented a CSV reader based on the second answer in this question and I read the CSV file into a dictionary that I use to look up values. After all this my Convert method simply looks like this:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    GuiAction guiAction = (GuiAction)value;
    string control = (string)parameter;

    //If this is the first time we use the control availability matrix we initialise it
    if (controlAvailabilityMatrix == null)
    {
        controlAvailabilityMatrix = InitControlAvailability();
    }
    try
    {
        return controlAvailabilityMatrix[guiAction][control];
    }
    catch (KeyNotFoundException)
    {
        throw new AvailabilityNotSetForControlException("Control availability for control " + control + " not set for GUI action " + guiAction);
    }
}
Community
  • 1
  • 1
jbb
  • 346
  • 3
  • 9