I have a longlistselector and in each row I have ToggleSwitch and I would like to call http request via my ApiService when ToggleSwitch is changed. I have ApiService class in ViewModel thanks to injection and in ViewModel I have ObservableCollection of Modules which have switches. I bind it with datatemplate and there is no problem with bind ToggleSwitch to bool property. But what should I do in setter of that property?
Model - Modul.cs
public int IsLock
{
get { return isLock; }
set {
Set(() => IsLock, ref isLock, value);
// What should I do here? How call ViewModel method?
}
}
ViewModel - ModuleListViewModel.cs
public ObservableCollection<Module> Modules { get; private set; }
// here I have apiService instance
// and here I could call apiService.Lock(module) and so on
View - part of DataTemplate
<toolkit:ToggleSwitch x:Name="LockSwitch"
IsChecked="{Binding IsLock, Mode=TwoWay}"/>
What's the right aproach for this? Maybe I could have ApiService class in each Modul class but I think that's very bad. I think ViewModel should somehow findout that Model was changed and it should call method.
I suggest using the ToggleSwitch's Command
property -- that will get executed every time the user changes the toggle, and will allow you to bind to the parent data context. Use something like this in the XAML:
<ItemsControl x:Name="items" ItemsSource="{Binding Modules}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<toolkit:ToggleSwitch x:Name="LockSwitch"
Command="{Binding ElementName=items,Path=DataContext.LockToggleCommand}"
CommandParameter="{Binding}"
IsChecked="{Binding IsLock, Mode=TwoWay}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Then just add the "LockToggleCommand" to your main view model, and call the service, eg:
public ObservableCollection<Module> Modules { get; private set; }
public ICommand LockToggleCommand { get; private set; }
public ViewModel()
{
LockToggleCommand = new DelegateCommand<Module>(module => {
apiService.Lock(module);
});
}
Here "DelegateCommand" is just the usual implementation of ICommand
-- I am sure that MVVM Light has its own standard implementation.
Edit
I thought that ToggleSwitch
supported Command
, but since it doesn't, you can take a similar approach using an EventTrigger (if you are willing to add the System.Windows.Interactivity and Microsoft.Expression.Interactions DLLs to your project):
<ItemsControl x:Name="items" ItemsSource="{Binding Modules}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<toolkit:ToggleSwitch x:Name="LockSwitch"
IsChecked="{Binding IsLock, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Toggled">
<ei:CallMethodAction TargetObject="{Binding ElementName=items,Path=DataContext}"
MethodName="OnToggled"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now add the "OnToggled" method to the main view model -- use the "sender" parameter to get the current item, something like this:
public void OnToggled(object sender, RoutedEventArgs e)
{
var toggleSwitch = (ToggleSwitch)sender;
var module = (Module)toggleSwitch.DataContext;
apiService.Lock(module);
}