2

Good afternoon,

I'm trying to fire an ICommand in the viewmodel... FROM the viewmodel, instead of from the UI.

The command works fine from the UI xaml, however, in this different scenario, it does not.

private DispatcherTimer telTimer;

public RelayCommand StartT_Command { get { return new RelayCommand(Exe_StartT_Command); } }

void Exe_StartT_Command(object parameter)
{
   if (telTimer != null && telTimer.IsEnabled)
   {
      telTimer.Stop();
      return;
   }
   telTimer = new DispatcherTimer();
   telTimer.Tick += new EventHandler(TelTimerTick);
   telTimer.Interval = new TimeSpan(0, 0, 0, 0, 10);
   telTimer.Start();
}

private void TelTimerTick(object sender, EventArgs e) //Every Tick
{
        Data.Te(Td);
}

Like I said, it runs fine from the UI, however, when called (see below) it runs all the way through telTimer.Start(); and then ... doesn't.

void KeyDown(int vKey)
{
   if (vKey == 0x6A) //Num Pad * Key
   {
      this.StartT_Command.Execute(null);
   }
}

Any ideas??

Thanks in advance.

EDIT1: I checked .IsEnabled, and the timer IS enabled. However, TelTimerTick() is not running.

EDIT2: I didn't mention that KeyDown is being called from different Thread. Would that have an affect on the event hitting TelTimerTick()?

Logan Klenner
  • 415
  • 2
  • 7
  • 15
  • Yes, it must fire on the dispatcher thread, you should get some exsception.. Nasty hack App.Current.Dispatcher.BeginInvoke(new Action(ExeStartTCommand)); Or just feed your vm the dispatcher, you should really have it in you ViewModelBase class. – Stígandr Sep 05 '14 at 23:09

1 Answers1

2

I'm not quite sure if I follow, but if you just want to invoke some command from your viewmodel?

As MvGarnagle points out in his answer, your are allocating a new command everytime, do what he does or:

private ICommand startCommand;
public ICommand StartTCommand
{
   get { return startCommand ?? (startCommand = new RelayCommand(ExeStartTCommand)); }
}

EDIT DispatcherTimer telTimer; // Not allocated void ExeStartTCommand() { // May be null if telTimer!=null && telTimer.IsEnabled) { telTimer.Stop(); return; } telTimer = new DispatcherTimer(); telTimer.Tick += TelTimerTick; telTimer.Interval = new TimeSpan(0, 0, 0, 0, 10); telTimer.Start(); }

private void TelTimerTick(object sender, EventArgs e) //Every Tick
{
    Data.Te(Td);
}

In your viewmodel just call ExeStartTCommand directly, don't fire the command, it's no need for that. Now If this was a DO like a custom control, you would have to fire Commands so the views using the controls would consume these commands or more common, routed events.

EDIT:

Now for the code

// how is this hooked up? W32 wrap?
void KeyDown(int vKey)
{
   if (vKey == 0x6A) //Num Pad * Key
     // Have the dispatchers in your viewmodelbaseclass, this is just for simplicity
     App.Current.Dispatcher.BeginInvoke(new Action(ExeStartTCommand)); 
}

You should really have a Dispatcher in your baseclass that's set to the dispatcher you want it to run on, and use that property instead of the one above. If you are dealing with a threading issue I need more context from you, kind of shooting in the dark here :)

Cheers,

Stian

Stígandr
  • 2,874
  • 21
  • 36
  • 1
    Thanks for your help on the semantics. I've changed it. I'm still running into the same problem with TelTimerTick not firing from the DispatchTimer (Running the method directly OR through ICommand) The DispatchTimer gets init, and starts, but nothing after that... Would being in a different thread mess things up? YES... it's GetAsyncKeyState from user32.dll, running in a loop in a different thread. – Logan Klenner Sep 05 '14 at 23:12
  • 1
    @LoganKlenner Yes indeed it will mess things up, make sure the command is executed on the gui thread, as in the example above. Check your debug output and add global exception handlers to catch our errors. I know to little about your problem, to see if this is some w32 related error (old c++/w32 guy). Just remember that a WinProc belongs to the owning window. – Stígandr Sep 05 '14 at 23:16
  • 2
    You are awesome my friend. Thanks for your help. I had to change it a little bit: `App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => Exe_StartT_Command(null)));` But everything works perfectly. – Logan Klenner Sep 05 '14 at 23:40
  • I took the liberty of changing some of the names there. BTW you should usually check for access before you invoke/BeginInvoke. If not needed you just call your method. A must in a ViuewModelBase class. And as stated you would feed your dispatcher and services in the ctor, or inject them. Static stuff is generally bad M'Kay, you shouldn't do statics when not really needed, because they are bad mkay ;) hehe – Stígandr Sep 05 '14 at 23:44