0

I am trying to binding a number to a Enum located in the view model. I haven't been able to pass the value from the view to the viewmodel through the converter. Is this even possible? I haven't seen anything online that solves this and none of my attemps have worked either.

Viewmodel

public enum TimerOptions
{
    FifteenMinutes,
    OneHour, 
    Tomorrow
}

private ICommand _timerCommand;
public ICommand TimerCommand => _timerCommand ?? 
                           (_timerCommand = new RelayCommand<TimerOptions>(StartTimer));

private async void StartTimer(TimerOptions option){ .... }

View

<Button Command="{Binding TimerCommand}" 
        Tag="0" 
        CommandParameter="{Binding Path=Tag, Converter={StaticResource BidirectionalEnumConverter}}">15 minutes</Button>

In the above example, when the user clicks the button, I want my function to get the first enum value of FifteenMinutes as the parameter. I've tried this by adding a Tag (as seen), adding an x:Name to the Button and also playing around with Source and Path in the binding. Nothing has worked.

Is this possible? I've been trying to solve this for hours, I haven't found anything online.

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
SaiyanGirl
  • 16,376
  • 11
  • 41
  • 57
  • How is `BidirectionalEnumConverter` declared? Is its `Convert()` method ever reached? – Pavel Anikhouski Jan 31 '20 at 20:11
  • Seems that `Command="{Binding StartTimer}"` should be `Command="{Binding TimerComand}"`. Besides that, in order to pass a enum value to CommandParameter, just write `CommandParameter="{x:Static local:TimerOptions.FifteenMinutes}"` – Clemens Jan 31 '20 at 20:18
  • @PavelAnikhouski I took the converter from this answer https://stackoverflow.com/questions/7110464/is-it-possible-to-create-a-generic-int-to-enum-converter . It works as I've used it in other locations. – SaiyanGirl Jan 31 '20 at 20:25
  • @Clemens Sorry, that was a typo. I took my code and tried to simplify it and copied the wrong thing. I can't use the static because the enum is located in another project, in the view model. I don't know how to import it in the xaml. – SaiyanGirl Jan 31 '20 at 20:51
  • By referencing the library and adding a XAML namespace like `xmlns:lib="clr-namespace:YourLibraryNamespace;assembly=YourLibraryAssembly"`. Then write `lib:TimerOptions.FifteenMinutes`. – Clemens Jan 31 '20 at 20:54
  • @Clemens O, that worked! Is it good practice for the view to have a reference to the viewmodel though? I thought that the view shouldn't know anything about the viewmodel in MVVM, this seems like an antipattern. – SaiyanGirl Jan 31 '20 at 21:03
  • It's the other way round. The view model doesn't know the view. You'll even often see something like `` in a view's XAML. – Clemens Jan 31 '20 at 21:12
  • @Clemens Aa okay, thank you for your help! :) – SaiyanGirl Jan 31 '20 at 21:30

1 Answers1

-1

I want my function to get the first enum value of FifteenMinutes as the parameter.

Why not just do the converter operation in the VM from the info passed in on a string in the command parameter?

Example

<Button Command="{Binding TimerCommand}" 
        Tag="1" 
        CommandParameter=1

Then have a ICommand based on that such as

public ICommand TimerCommand =>
        new OperationCommand(async (oTag) => await StartTime(oTag).ConfigureAwait(true));

private bool IsOperation { get; set; }

private async void StartTimer(object oTag)
{
    int tagId = (int)oTag;
    //.... do the convert operations here...
      ...
   // Access VM properties here
   IsOperation = true;

    await  ....;

}


Here is the commanding structure I use instead of Relay.

public class OperationCommand : ICommand
{

    #region Variables

    private Func<object, bool> CanExecuteHandler { get; set; }
    private Action<object> ExecuteActionHandler { get; set; }

    public bool InSeparateThread { get; set; }
    #endregion

    #region Properties

    #endregion

    #region Construction/Initialization

    public OperationCommand(Action<object> executeAction, bool inSeparateThread = false)
    {
        InSeparateThread = inSeparateThread;
        ExecuteActionHandler = executeAction 
                                   ?? throw new ArgumentNullException(nameof(executeAction));
    }

    public OperationCommand(Action<object> executeAction, 
                            Func<object, bool> canExecute) : this(executeAction)
    {
        CanExecuteHandler = canExecute;
    }

    // Here to adhere to ICommand, change to below if needed
    //public event EventHandler CanExecuteChanged;
    event EventHandler ICommand.CanExecuteChanged
    {
        add {}
        remove {}
    }

    #endregion

    #region Methods

    public bool CanExecute(object parameter) => CanExecuteHandler?.Invoke(parameter) ?? true;

//        public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, new EventArgs());

    public void Execute(object parameter)
    {
        ExecuteActionHandler?.Invoke(parameter);
    }

    #endregion
}
}
ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122