更新 改为我自己的接力命令有帮助。 虽然生根Funcs没有
大家好我正在制作一个小型的MVVM应用程序。这应该是一个完整的课程
Func<string>
并显示一个按钮列表,每个按钮执行一个包含Func<string>
的命令,并在另一个列表中显示它们的返回值。
该程序首先工作正常但按下一个按钮后,它只是停止执行命令。用户界面仍然具有响应性。好像绑定破了。
有太多的课程,所以我将整个项目附加在以下链接
http://www.megafileupload.com/en/file/403770/GenericTester-zip.html
相关代码:
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's name.
/// </summary>
public const string CommandNamePropertyName = "CommandName";
private string _CommandName = "Hello";
/// <summary>
/// Sets and gets the CommandName property.
/// Changes to that property's 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>
更新
答案是改变:
Command = new RelayCommand(() => { Log.Insert(0, "-------------\n" + CommandName + "\n" + (iCommand.Invoke())); }),
这样的事情:
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
});
}
因为添加到中继命令的操作没有生根
答案 0 :(得分:2)
是否使用MVVMLight的RelayCommand?
如果是这样,您可能会遇到GC问题。 RelayCommand在内部对其回调使用WeakReference。
如果您传入的是一个未在其他地方生根的匿名函数,那么当GC运行时它可能会被清除。
大多数情况下,这不是问题,因为func是对VM的回调,VM本身植根于DataContext,ViewModelLocator或其他地方。如果您正在创建未经过根植的Func,则可能是一个问题。
根据这些Func的一种方法是在ViewModel中有一个List<Func<string>>
,并在创建RelayCommands的同时将它们添加到列表中。
答案 1 :(得分:0)
Command = new RelayCommand(() => { Log.Insert(0, "-------------\n" + CommandName + "\n" + (Command.Invoke())); })
更改为:
Command = new RelayCommand(() => { Log.Insert(0, "-------------\n"); })
它可以工作,但我不知道为什么不能在Lambda表达式中使用参数(Command和CommandName)?
答案 2 :(得分:0)
我是否正确地从命令中调用命令?
Command = new RelayCommand(() => { Log.Insert(0, "-------------\n" + CommandName + "\n" + (Command.Invoke())); })
这不是递归吗?
您可以尝试从表达式中删除调用吗? 你为什么要从里面调用它?