5

I want to use a PRISM delegatecommand with a bool as parameter. This is the relevant code:

public class ChartViewModel : BindableBase
{
    public DelegateCommand<bool?> ChangeZoomPanCommand { get; private set; }

    private bool isInRealtimeMode;
    public bool IsInRealtimeMode
    {
        get { return isInRealtimeMode; }
        set
        {
            SetProperty(ref isInRealtimeMode, value);
            ChangeZoomPanCommand.RaiseCanExecuteChanged();
        }
    }

    private bool dragToZoom;
    public bool DragToZoom
    {
        get { return dragToZoom; }
        set { SetProperty(ref dragToZoom, value); }
    }


    public ChartViewModel()
    {
        ChangeZoomPanCommand = new DelegateCommand<bool?>(ExecuteChangeZoomPan, CanExecuteChangeZoomPan);
        IsInRealtimeMode = true;
        DragToZoom = true;
    }

    private bool CanExecuteChangeZoomPan(bool? arg)
    {
        return !IsInRealtimeMode;
    }

    private void ExecuteChangeZoomPan(bool? enableZoom)
    {
        if (enableZoom.HasValue)
        {
            DragToZoom = enableZoom.Value;
        }
    }
}

When I set a breakpoint in CanExecuteChangeZoomPan it never gets hit. The problem happens after ChangeZoomPanCommand.RaiseCanExecuteChanged().

This is the stacktrace:

>

    at Prism.Commands.DelegateCommand`1.<>c__DisplayClass1_0.<.ctor>b__1(Object o)
   at Prism.Commands.DelegateCommandBase.CanExecute(Object parameter)
   at Prism.Commands.DelegateCommandBase.System.Windows.Input.ICommand.CanExecute(Object parameter)
   at MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(ICommandSource commandSource)
   at System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute()
   at System.Windows.Controls.Primitives.ButtonBase.HookCommand(ICommand command)
   at System.Windows.Controls.Primitives.ButtonBase.OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
   at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
   at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
   at System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp, Boolean preserveCurrentValue)
   at System.Windows.Data.BindingExpressionBase.Invalidate(Boolean isASubPropertyChange)
   at System.Windows.Data.BindingExpression.TransferValue(Object newValue, Boolean isASubPropertyChange)
   at System.Windows.Data.BindingExpression.Activate(Object item)
   at System.Windows.Data.BindingExpression.AttachToContext(AttachAttempt attempt)
   at System.Windows.Data.BindingExpression.MS.Internal.Data.IDataBindEngineClient.AttachToContext(Boolean lastChance)
   at MS.Internal.Data.DataBindEngine.Task.Run(Boolean lastChance)
   at MS.Internal.Data.DataBindEngine.Run(Object arg)
   at MS.Internal.Data.DataBindEngine.OnLayoutUpdated(Object sender, EventArgs e)
   at System.Windows.ContextLayoutManager.fireLayoutUpdateEvent()
   at System.Windows.ContextLayoutManager.UpdateLayout()
   at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
   at System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork()
   at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
   at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
   at System.Windows.Media.MediaContext.AnimatedRenderMessageHandler(Object resizedCompositionTarget)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)

If I change the argument type to string everything seems to work. I then of course get a "False" string as argument because that is my xaml commandparameter. Why is this not working with any T as there seems to be no limit on T. I found out the hard way already that T must be an object or nullable, but even nullable doesn't seem to fit. How to work with a bool argument?

Thanks

Jef Patat
  • 999
  • 1
  • 10
  • 26

3 Answers3

6

The relevant command parameter properties are usually typed as object so any literal is interpreted as a string. You will have to pass in the correct type if Prism is unable to convert to your type automatically, but working with generics (like Nullable<T>) in XAML is generally a pain, so i would not recommend doing so.

To convert to a simple type in XAML you usually should be able to just use a binding like this: {Binding Source=(sys:Boolean)true}, where sys is an XMLNS mapping to the System namespace in mscorlib of course.

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • I can live with that, and at the moment that's my workaround. But why does the above issue appear? The command is not even invoked, only `RaiseCanExecuteChanged` is called. Shouldn't that just call `CanExecuteChangeZoomPan`. Would it make sence to use a generic where constraint? – Jef Patat Jun 10 '16 at 13:43
  • `CanExecute` also gets passed the command parameter, so the conversion takes place there as well. I don't think constraints would change anything here. – H.B. Jun 10 '16 at 13:45
0

You could just create a DelegateCommand<object> instead of a DelegateCommand<bool?> and then handle it like this:

void ExecuteChangeZoomPan(object obj)
{
    if (obj is bool?)
    {
        bool? arg = obj as bool?;

        // The rest of the code goes here.
    }
}

In this way, ExecuteChangeZoomPan can be very flexible, and accept types as complex as System.DateTime!

Prince Owen
  • 1,225
  • 12
  • 20
0

This is how I create a new instance of DelegateCommand

ICommand ConnectCommand = new DelegateCommand<object>(ConnectExecute,ConnectCanExecute);

And then if you want to raise the change

 ((DelegateCommand<object>)ConnectCommand).RaiseCanExecuteChanged();
mikro
  • 21
  • 1
  • 3