我是MVVM的新手,我有一个奇怪的行为,我还没有成功解决:我有一些按钮(和其他元素)通过列表显示:
即使其他元素对数据绑定行为正确(在对象更改其状态时更新),按钮也只能正确停用,但不会重新激活与命令状态相关:我必须单击GUI刷新并获取更新的正确的状态。
我在StackOverflow上发现可以使用以下方法纠正此问题:
CommandManager.InvalidateRequerySuggested();
但是我没有成功找到如何使用它:或者这没有任何影响,或者(当我放在我的RelayCommand中时 - 这并不是一个好主意无论如何)它给按钮带来了良好的行为,但却使其他项目表现不正确。
请找我的XAML:
<DataTemplate x:Key="ProjectTemplate">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch" Margin="0, 2, 0, 2">
<ProgressBar Value="{Binding BuildProgress}" Width="60" Height="15"/>
<TextBox Text="{Binding Label}" MinWidth="120" IsEnabled="{Binding IsLabelAvailable}" Margin="5,0,0,0" />
<CheckBox Content="Archive" IsChecked="{Binding ToBeArchived}" IsEnabled="{Binding IsAvailable}" Margin="5,4,0,0" />
<Button Content="Build" Command="{Binding Path=BuildCommand}" Margin="5,0,0,0" />
<Button Content="Rebuild" Command="{Binding Path=RebuildCommand}" Margin="5,0,0,0" />
<Button Content="Publish" Command="{Binding Path=PublishCommand}" Margin="5,0,0,0" />
<TextBlock Text="{Binding Status}" Margin="10,0,0,0" />
</StackPanel>
</DataTemplate>
<ListBox Grid.Row="0" ItemsSource="{Binding Path=Projects}" ItemTemplate="{StaticResource ProjectTemplate}" >
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True" >
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
</Style.Resources>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
我的ViewModel:
public ObservableCollection<Project> Projects
{
get
{
return _projects;
}
set
{
if (_projects == value)
return;
_projects = value;
OnPropertyChanged("Projects");
}
}
我的模特:
private readonly Lazy<ICommand> _lazyRebuildCommand;
private bool _isAvailable;
public Project()
{
IsAvailable = true;
_lazyRebuildCommand = new Lazy<ICommand>(() =>
new RelayCommand(
param => BuildProject(true),
param => IsAvailable
));
}
public ICommand RebuildCommand
{
get
{
return _lazyRebuildCommand.Value;
}
}
public bool IsAvailable
{
get
{
return _isAvailable;
}
set
{
_isAvailable = value;
OnPropertyChanged("IsAvailable");
}
}
感谢您的帮助!
编辑:以下是使用模型的过程: 我使用Task来处理一个Queue,我在其中添加了我想要处理的项目:
private static readonly Queue<Project> ProjectsToBuild = new Queue<Project>();
private static bool _isInitialized = false;
public static void AddProjectToBuild(Project projectToAdd)
{
projectToAdd.IsAvailable = false;
ProjectsToBuild.Enqueue(projectToAdd);
if (!_isInitialized)
{
Task.Factory.StartNew(() => ProcessQueue());
_isInitialized = true;
}
}
private static void ProcessQueue()
{
while (true)
{
if (ProjectsToBuild.Count > 0)
{
var project = ProjectsToBuild.Dequeue();
ProcessCurrentProject(project);
}
Thread.Sleep(200);
}
}
private static void ProcessCurrentProject(Project project)
{
Thread.Sleep(3000);
project.BuildProgress = 50;
Thread.Sleep(3000);
project.BuildProgress = 100;
project.IsPublishable = true;
project.IsAvailable = true;
project.RaiseProjectProcessedEvent();
return;
}
EDIT2:我使用的RelayCommand:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
//CommandManager.InvalidateRequerySuggested();
return _canExecute == null ? true : _canExecute(parameters);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameters)
{
_execute(parameters);
}
#endregion // ICommand Members
}
答案 0 :(得分:0)
我认为模板找到正确的状态存在问题。
根据模板取出猜测,并将IsEnable
直接绑定到关联的Can..
状态属性(如果您当前拥有这些属性,我可能无法理解,您可能需要添加它们)但是这样的事情:
<DataTemplate x:Key="ProjectTemplate">
<StackPanel Orientation="Horizontal">
<ProgressBar Value="{Binding BuildProgress}" />
<TextBox Text="{Binding Label}" IsEnabled="{Binding IsLabelAvailable}" />
<CheckBox Content="Archive" IsChecked="{Binding ToBeArchived}" IsEnabled="{Binding IsAvailable}" />
<Button Content="Build"
Command="{Binding Path=BuildCommand}"
IsEnabled="{Binding CanBuild}" />
<Button Content="Rebuild"
Command="{Binding Path=RebuildCommand}"
IsEnabled="{Binding CanReBuild}" />
<Button Content="Publish"
Command="{Binding Path=PublishCommand}"
IsEnabled="{Binding CanPublish}" />
<TextBlock Text="{Binding Status}" />
</StackPanel>
</DataTemplate>
然后确保Can...
属性当然符合INotifyProperty的变化。
答案 1 :(得分:0)
我终于解决了这个问题,任何有这个问题的人都可以轻松使用。
在表单的.xaml.cs文件中,订阅Rendering事件以拍摄将重新激活按钮的“InvalidateRequerySuggested”:
public MainWindow()
{
InitializeComponent();
CompositionTarget.Rendering += OnRendering;
}
void OnRendering(object sender, EventArgs e)
{
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(CommandManager.InvalidateRequerySuggested));
}
这是代码背后的问题,但由于它仅用于解决GUI问题,我认为这是正确的。