列表中的按钮数据绑定行为不正确

时间:2014-05-07 14:35:05

标签: c# wpf xaml data-binding mvvm

我是MVVM的新手,我有一个奇怪的行为,我还没有成功解决:我有一些按钮(和其他元素)通过列表显示:

My application GUI

即使其他元素对数据绑定行为正确(在对象更改其状态时更新),按钮也只能正确停用,但不会重新激活与命令状态相关:我必须单击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
}

2 个答案:

答案 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问题,我认为这是正确的。