I am new in MVVM and I have a strange behavior that I haven't succeed to resolve: I have some buttons (and other elements) displayed via a list:
Even if the other elements behave correctly to data-binding (updating when the objects changes their states), the buttons only deactivates correctly but do not reactivate in relation to the command state: I must click on the GUI to refresh and get the updated and correct state.
I found on StackOverflow that this issue could be corrected by using:
CommandManager.InvalidateRequerySuggested();
But I didn't succeed to find how to use it: or this doesn't have any impact, or (when place in my RelayCommand - and that doesn't see to be a good idea anyway) it gives the good behavior to the buttons but makes the other items behave incorrectly.
Please find my 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>
My ViewModel:
public ObservableCollection<Project> Projects
{
get
{
return _projects;
}
set
{
if (_projects == value)
return;
_projects = value;
OnPropertyChanged("Projects");
}
}
And my Model:
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");
}
}
Thanks for any help!
EDIT: Here is the process where the model is used:
I'm using a Task to handle a Queue in which I add the projects I want to process:
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: The RelayCommand I use:
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
}