【问题标题】:Enable/Disable WPF button based on ListView population根据 ListView 填充启用/禁用 WPF 按钮
【发布时间】:2013-10-29 14:07:21
【问题描述】:

我在View (AXML) 中定义了一个按钮,代码如下:

<Button Content="Fetch Data" Command="{Binding readInventoryFilesCommand}" CommandParameter="{Binding Path=Text, ElementName=browseFolderTextBox}" Name="button1" />

该按钮按预期工作,但如果 ListView 已填充元素,我希望启用/禁用它。

  • 如果listView中没有元素-->按钮IsEnabled设置为false
  • 如果listView中有元素-->按钮IsEnabled设置为true

我尝试使用IsEnabled="{Binding ...,但在Visual Studio C# Express 中没有得到code completion,而且很难猜出替代方案。

命令如下所示:

internal class ReadInventoryFilesCommand : ICommand
{
    public ReadInventoryFilesCommand(ResourceViewModel viewModel)
    {
        _viewModel = viewModel;
    }

    private ResourceViewModel _viewModel;

    #region ICommand Members

    event EventHandler ICommand.CanExecuteChanged
    {
        add
        {
            CommandManager.RequerySuggested += value;
        }
        remove
        {
            CommandManager.RequerySuggested -= value;
        }
    }

    bool ICommand.CanExecute(object parameter)
    {
        return _viewModel.CanUpdate;
    }

    void ICommand.Execute(object parameter)
    {
        _viewModel.ReadInventroyFiles(parameter.ToString());
    }
    #endregion
}

编辑 这是 Bobs 建议后的当前代码:

internal class ReadInventoryFilesCommand : ICommand
    {
        public ReadInventoryFilesCommand(ResourceViewModel viewModel)
        {
            _viewModel = viewModel;
            _viewModel.PropertyChanged+= (s,e) => { 
                if (e.PropertyName == "CanUpdate") RaiseCanExecuteChanged();
            };
        }

        private ResourceViewModel _viewModel;

        event EventHandler ICommand.CanExecuteChanged;

        bool ICommand.CanExecute(object parameter)
        {
            return _viewModel.CanUpdate;
        }

        void ICommand.Execute(object parameter)
        {
            _viewModel.ReadInventroyFiles(parameter.ToString());
        }

        void RaiseCanExecuteChanged() {
            var handler = this.CanExecuteChanged;
            if (handler != null) handler(this,EventArgs.Empty);
        }
    }

它会产生以下错误:

An explicit interface implementation of an event must use event accessor syntax 

【问题讨论】:

  • 命令是否正确实现canexecute和OnCanExecuteChanged?
  • @BobVale,请查看编辑
  • 你到底从哪里得到了 canexecutechanged 的​​实现?您在哪里举办活动?
  • @BobVale,这是遗留代码。这就是与这个问题相关的所有代码了!

标签: c# visual-studio-2010 user-interface mvvm


【解决方案1】:

您需要在命令可以执行时引发 CanExecuteChanged 事件。

编辑

正如gehho在cmets中提到的,使用ICommand接口时不需要绑定按钮的IsEnabled属性,因为Button会使用CanExecute函数来判断是否启用。

但是,您需要引发 CanExecuteChanged 事件以通知命令该值已更改。

结束编辑

假设 _viewModel 实现 INotifyPropertyChanged 您可以在视图模型更改时挂钩并在您的命令上引发更改的事件:

我提供了两个版本,一个使用 ICommand 的显式实现,另一个仅实现不显式 ICommand 前缀的属性和方法。两者都应该工作。我通常会使用版本 2,除非在具有特定要求的方法上存在冲突。

编辑版本 1 - 显式实现

internal class ReadInventoryFilesCommand : ICommand
{
  public ReadInventoryFilesCommand(ResourceViewModel viewModel)
  {
    _viewModel = viewModel;
    _viewModel.PropertyChanged+= (s,e) => { 
                                  if (e.PropertyName == "CanUpdate") RaiseCanExecuteChanged();
                                 };
  }

  event EventHandler ICommand.CanExecuteChanged { add; remove; }

  private ResourceViewModel _viewModel;

  bool ICommand.CanExecute(object parameter)
  {
    return _viewModel.CanUpdate;
  }

  void ICommand.Execute(object parameter)
  {
    _viewModel.ReadInventroyFiles(parameter.ToString());
  }

  void RaiseCanExecuteChanged() {
    var handler = ((ICommand)this).CanExecuteChanged;
    if (handler != null) handler(this,EventArgs.Empty);
  }
}

编辑版本 2

internal class ReadInventoryFilesCommand : ICommand
{
  public ReadInventoryFilesCommand(ResourceViewModel viewModel)
  {
    _viewModel = viewModel;
    _viewModel.PropertyChanged+= (s,e) => { 
                                  if (e.PropertyName == "CanUpdate") RaiseCanExecuteChanged();
                                 };
  }

  public event EventHandler CanExecuteChanged;

  private ResourceViewModel _viewModel;

  public bool CanExecute(object parameter)
  {
    return _viewModel.CanUpdate;
  }

  public void Execute(object parameter)
  {
    _viewModel.ReadInventroyFiles(parameter.ToString());
  }

  void RaiseCanExecuteChanged() {
    var handler = this.CanExecuteChanged;
    if (handler != null) handler(this,EventArgs.Empty);
  }
}

【讨论】:

  • 对不起,但正如我所解释的那样,我对此并不陌生。上面不同的代码分别属于哪里。
  • 这是为了替换你现有的命令类定义
  • 我试了一下得到:当前上下文Commands\ReadInventoryFilesCommand.cs中不存在名称'RaiseCanExecutedChanged'
  • @theAlse 打错了,我改正答案,应该是RaiseCanExecuteChanged
  • 或许应该提一下,在使用带按钮的命令时,不需要手动设置ButtonIsEnabled属性。这是由Button 本身根据使用的ICommandCanExecute(object) 方法的返回值自动完成的。
【解决方案2】:

我认为您可以将绑定与转换器一起使用: 代码Windows1.xaml:

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300"
        xmlns:converters="clr-namespace:WpfApplication1"
        >
    <Window.Resources>
        <converters:CoverterItemsSource2Enabled x:Key="converter" />
    </Window.Resources>
    <Grid Margin="0,0,-256,0">
        <ListView x:Name="listView" HorizontalAlignment="Left" Margin="10,10,0,10" VerticalAlignment="Stretch" Width="196">
            <ListView.View>
                <GridView>
                    <GridViewColumn/>
                </GridView>
            </ListView.View>
        </ListView>
        <Button Content="Button" HorizontalAlignment="Left" Margin="211,10,0,0" VerticalAlignment="Top" IsEnabled="{Binding Path=Items, ElementName=listView, Converter={StaticResource converter} }" Width="75"/>

    </Grid>
</Window>

代码转换器:

namespace WpfApplication1
{
    class CoverterItemsSource2Enabled : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
                return false;
            if(value is ItemCollection)
            {
                if ((value as ItemCollection).Count > 0)
                    return true;
            }
            return false;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

【讨论】:

  • 但是代码使用了命令,所以CanExecute 逻辑应该由命令来处理。
  • 是的,但我也读过有关 IsEnabled 属性的尝试,它会起作用。
【解决方案3】:

这是将IsEnabled 绑定到Items.Count 时的默认行为。

例如

<ListView x:Name="RulesList" />
<Button IsEnabled="{Binding Items.Count, ElementName=RulesList}" />

在此示例中,Button 将在 ListView 没有项目时禁用,并在它有 1 个或多个项目时启用。

【讨论】:

    【解决方案4】:

    你在说这个吗?:

    private void ListView1_SourceUpdated(object sender, DataTransferEventArgs e)
     {
       Button1.IsEnabled==(ListView1.Items.Count>0);
     }
    

    【讨论】:

    • 是的,但我希望这可以按照 MVVM 指南以某种方式完成,即分离代码和设计!
    猜你喜欢
    • 2011-02-01
    • 1970-01-01
    • 2011-09-22
    • 1970-01-01
    • 2015-04-19
    • 1970-01-01
    • 2021-12-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多