Commands Stop Running

An update changing my relay command helped. although rooting funcs are not

Hi guys, I was making a small MVVM application. It is assumed that he takes a class full of Func<string> and displays a list of buttons, each of which executes a command containing Func<string> , and displays their return values ​​in another list.

The program works fine at first , but after accidentally pressing a button, it simply stops the execution of commands. The user interface is still responsive. As if the binding had broken.

There are too many classes, so I linked the whole project in the following link

http://www.megafileupload.com/en/file/403770/GenericTester-zip.html

Relevant Code:

 namespace AdapterTester.ViewModel { public class MainViewModel : ViewModelBase { public ObservableCollection<ViewableRelayCommand> CommandsList { get; set; } public ObservableCollection<string> Log { get; set; } /// <summary> /// Initializes a new instance of the MainViewModel class. /// </summary> public MainViewModel() { CommandsList = new ObservableCollection<ViewableRelayCommand>(); Log = new ObservableCollection<string>(); MapCommands(); } /// <summary> /// adds a ViewableRelayCommand to the CommandsList /// </summary> public void Add(Func<string> iCommand, string CommandName, Func<bool> CanExecute = null) { CommandsList.Add(new ViewableRelayCommand() { Command = new RelayCommand(() => { Log.Insert(0, "-------------\n" + CommandName + "\n" + (iCommand.Invoke())); }), CommandName = CommandName }); } /// <summary> /// For Each Func<string> in TestFunctions create a ViewableRelayCommand /// </summary> private void MapCommands() { var target = new TestFunctions(); var methods = target.GetType().GetMethods().Where(m => m.DeclaringType == typeof(TestFunctions)); foreach (var method in methods) { if( (method.ReturnType == typeof(string)) && (method.GetParameters().Length ==0)) { Func<string> func = (Func<string>)Delegate.CreateDelegate(typeof(Func<string>), target, method); Add(func, method.Name); } } } } public class ViewableRelayCommand : ViewModelBase { public RelayCommand Command { get; set; } /// <summary> /// The <see cref="CommandName" /> property name. /// </summary> public const string CommandNamePropertyName = "CommandName"; private string _CommandName = "Hello"; /// <summary> /// Sets and gets the CommandName property. /// Changes to that property value raise the PropertyChanged event. /// </summary> public string CommandName { get { return _CommandName; } set { if (_CommandName == value) { return; } RaisePropertyChanging(CommandNamePropertyName); _CommandName = value; RaisePropertyChanged(CommandNamePropertyName); } } } } 

XAML:

 <Window x:Class="AdapterTester.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ignore="http://www.ignore.com" mc:Ignorable="d ignore" Width="500" Height="300" Title="MVVM Light Application" DataContext="{Binding Main, Source={StaticResource Locator}}"> <Window.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Skins/MainSkin.xaml" /> </ResourceDictionary.MergedDictionaries> <DataTemplate x:Key="myButtonTemplate"> <Button Content="{Binding Path=CommandName}" Command="{Binding Path=Command}" Margin="3"></Button> </DataTemplate> </ResourceDictionary> </Window.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <ListBox Name="CommandsListBox" Grid.Column="0" ItemsSource="{Binding CommandsList}" ItemTemplate="{StaticResource myButtonTemplate}"> </ListBox> <ListBox Name="LogListBox" Grid.Column="1" ItemsSource="{Binding Log}" </ListBox> </Grid> </Window> 

update:

the answer should change:

  Command = new RelayCommand(() => { Log.Insert(0, "-------------\n" + CommandName + "\n" + (iCommand.Invoke())); }), 

like that:

 List<Action> actions = new List<Action>(); public void Add(Func<string> iCommand, string CommandName, Func<bool> CanExecute = null) { Action act = () => { Log.Insert(0, "-------------\n" + CommandName + "\n" + (iCommand.Invoke())); }; actions.Add(act); CommandsList.Add(new ViewableRelayCommand() { Command = new RelayCommand(act) , CommandName = CommandName }); } 

because the actions added to the relay command where not implemented

+4
source share
3 answers

Is RelayCommand used from MVVMLight?

If so, you may run into a GC problem. RelayCommand internally uses WeakReference for its callbacks.

If you perform an anonymous function that is not rooted elsewhere, then it can be cleared when the GC is running.

In most cases, this is not a problem, because func is a virtual machine callback, and the virtual machine itself is embedded in a DataContext, ViewModelLocator, or elsewhere. If you create Func that are not rooted, this may be a problem.

One way to root these Func is to have List<Func<string>> in your ViewModel and add them to the list at the same time as creating the relay commands.

+2
source
 Command = new RelayCommand(() => { Log.Insert(0, "-------------\n" + CommandName + "\n" + (Command.Invoke())); }) 

change to:

 Command = new RelayCommand(() => { Log.Insert(0, "-------------\n"); }) 

it may work, but I don’t know why the (Command and CommandName) parameter cannot be used in Lambda expressions?

0
source

Am I right that you are calling a team from a team?

  Command = new RelayCommand(() => { Log.Insert(0, "-------------\n" + CommandName + "\n" + (Command.Invoke())); }) 

Not recursively?

Can you try to remove the call from the expression? and why are you calling it from the inside?

0
source

All Articles