C# 如何禁用绑定到当前项目的按钮';当没有当前项目时,s ICOMAND?

C# 如何禁用绑定到当前项目的按钮';当没有当前项目时,s ICOMAND?,c#,.net,wpf,binding,mvvm,C#,.net,Wpf,Binding,Mvvm,假设您有一个按钮,其命令属性绑定到某个集合当前项的某个ICommand 当集合为null时,按钮保持启用状态,单击它似乎是不可操作的。我希望按钮保持禁用状态。我想出了以下方法,可以在集合为null时禁用按钮。然而,对于可能在更自然、更简单、更像MVVM的环境中完成的事情来说,这似乎有点太复杂了 因此,问题是:在理想情况下,如果不使用代码隐藏,是否有更简单的方法来保持该按钮处于禁用状态 .xaml: <Button Content="Do something" > <Bu

假设您有一个按钮,其
命令
属性绑定到某个集合当前项的某个
ICommand

当集合为
null
时,按钮保持启用状态,单击它似乎是不可操作的。我希望按钮保持禁用状态。我想出了以下方法,可以在集合为null时禁用按钮。然而,对于可能在更自然、更简单、更像MVVM的环境中完成的事情来说,这似乎有点太复杂了

因此,问题是:在理想情况下,如果不使用代码隐藏,是否有更简单的方法来保持该按钮处于禁用状态

.xaml:

<Button Content="Do something" >
    <Button.Command>
        <PriorityBinding>
            <Binding Path="Items/DoSomethingCmd"  />
            <Binding Path="DisabledCmd" />
        </PriorityBinding>
    </Button.Command>
</Button>
public class ViewModel : NotificationObject
{
    ObservableCollection<Foo> _items;

    public DelegateCommand DisabledCmd { get; private set; }

    public ObservableCollection<Foo> Items { 
        get { return _items; } 
        set { _items = value; RaisePropertyChanged("Items"); } 
    }

    public ViewModel()
    {
        DisabledCmd = new DelegateCommand(DoNothing, CantDoAnything);
    }

    void DoNothing() { }
    bool CantDoAnything()
    {
        return false;
    }
}

.cs:

<Button Content="Do something" >
    <Button.Command>
        <PriorityBinding>
            <Binding Path="Items/DoSomethingCmd"  />
            <Binding Path="DisabledCmd" />
        </PriorityBinding>
    </Button.Command>
</Button>
public class ViewModel : NotificationObject
{
    ObservableCollection<Foo> _items;

    public DelegateCommand DisabledCmd { get; private set; }

    public ObservableCollection<Foo> Items { 
        get { return _items; } 
        set { _items = value; RaisePropertyChanged("Items"); } 
    }

    public ViewModel()
    {
        DisabledCmd = new DelegateCommand(DoNothing, CantDoAnything);
    }

    void DoNothing() { }
    bool CantDoAnything()
    {
        return false;
    }
}
公共类视图模型:NotificationObject
{
可观察到的收集项目;
public DelegateCommand DisabledCmd{get;private set;}
公共可观察收集项目{
获取{return\u items;}
设置{u items=value;RaisePropertyChanged(“items”);}
}
公共视图模型()
{
DisabledCmd=新的DelegateCommand(DoNothing,CantDoAnything);
}
void DoNothing(){}
布尔·坎多纳
{
返回false;
}
}
编辑

几点注意:

  • 我知道我可以使用lambda表达式,但在这个示例代码中我没有
  • 我知道谓词是什么
  • 我看不出使用
    DoSomethingCmd.CanExecute
    进行操作会有什么帮助,因为在没有当前项目的情况下,没有
    DoSomethingCmd
    可访问
  • 因此,我将重新集中我的问题:如何避免使用
    禁用的DCMD
    ?我对提升DoSomethingCmd不感兴趣,因为它不是我想要的。否则我就不会问这个问题了
  • 另一次编辑:

    <Button Content="Do something" >
        <Button.Command>
            <PriorityBinding>
                <Binding Path="Items/DoSomethingCmd"  />
                <Binding Path="DisabledCmd" />
            </PriorityBinding>
        </Button.Command>
    </Button>
    
    public class ViewModel : NotificationObject
    {
        ObservableCollection<Foo> _items;
    
        public DelegateCommand DisabledCmd { get; private set; }
    
        public ObservableCollection<Foo> Items { 
            get { return _items; } 
            set { _items = value; RaisePropertyChanged("Items"); } 
        }
    
        public ViewModel()
        {
            DisabledCmd = new DelegateCommand(DoNothing, CantDoAnything);
        }
    
        void DoNothing() { }
        bool CantDoAnything()
        {
            return false;
        }
    }
    
    所以我基本上采用了这个答案作为解决方案:


    我相信这正是hbarck所建议的。

    如果你看一下委托命令,第二个参数是一个func,它可以让你做到这一点,我不太清楚,为什么你要把它弄得这么复杂。 如果您这样做,例如:

    DoSomethingCommand = new DelegateCommand(() => SampleAction, () => Items != null);
    
    仅将按钮的Command属性绑定到此命令时,按钮将被禁用,如下所示:

    <Button Command={Binding DoSomethingCommand} />
    
    
    

    当委托命令中的条件变为false时,该按钮将自动禁用。当条件的结果可能发生变化时,您还应该调用
    DoSomethingCommand.RaiseCanceTechChanged()
    ,按钮的IsEnabled更新会反映当前状态。

    我使用了RelayCommand,它有一个构造函数,您可以在其中创建canExecute谓词,如果返回false,则绑定按钮将自动禁用

    在delegate命令上,您应该重写CantDoAnything()方法来表示启用和禁用逻辑。您只需将绑定绑定到命令即可


    查看PresentationFramework.dll中的代码,我看不到任何简单的方法(请参见
    按钮库.updateCancecute
    )。您可能有幸从
    按钮
    派生出一个类,并重写
    CommandProperty
    的元数据以自己处理更改。但是,您可以轻松避免在viewmodel中使用不执行任何操作的命令代码:创建一个特殊的转换器,将
    null
    命令转换为共享的始终禁用的回退
    ICommand
    。如果您有许多按钮需要这种行为,则可能需要附加属性和样式。

    您只需将按钮的IsEnabled属性绑定到当前项并使用转换器即可

    下面是一个完整的演示:

    <Page.Resources>
        <Converters:NullToBoolConverter x:Key="NullToBoolConverter" 
                                        IsNullValue="False" IsNotNullValue="True" />
    </Page.Resources>
    
    <Page.DataContext>
        <Samples:NoCurrentItemViewModel/>
    </Page.DataContext>
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
    
        <ListBox 
            IsSynchronizedWithCurrentItem="True" Grid.Row="0" 
            ItemsSource="{Binding Items}" 
            DisplayMemberPath="Name"/>
    
        <Button 
            Grid.Row="1" 
            Content="Do something" 
            IsEnabled="{Binding Items/, Converter={StaticResource NullToBoolConverter}}"
            Command="{Binding Items/DoSomethingCommand}"/>
    
        <Button Grid.Row="2" Content="Clear" Command="{Binding ClearCommand}"/>
    </Grid>
    

    您可以创建一个
    触发器
    来检查该项(按钮的数据上下文)是否为空,并将按钮的
    属性设置为
    false
    ,类似如下-

    <DataTrigger
        Binding="{Binding Path=Item}"
        Value="{x:Null}">
        <Setter Property="Control.IsEnabled" Value="False" />
    </DataTrigger>
    
    
    

    我现在还不能测试它,但我认为它应该可以工作。

    我会像akjoshi那样做,只是我会使用普通触发器而不是DataTrigger,我会检查Button.Command是否为null。由于禁用没有命令的按钮总是有意义的(特别是在MVVM中,没有click EventHandler),因此最好将此触发器包含到按钮的默认样式中,以便在整个应用程序的所有按钮上都有此行为。。。我看不出使用虚拟命令的理由。

    是的,我知道我可以将命令提升一级,但我必须在viewmodel中跟踪当前项。我认为在viewmodel中获得当前项是一个加号@pickles:是的,我使用
    IsSelected
    间接跟踪当前项,但如果不需要,我不想将其作为特定属性,因为WPF UI已经在视图级别执行了。如果没有当前项,如何访问谓词?@Ludo谓词告诉命令是否可以执行,它基本上是第二种方法,例如当命令绑定到按钮时执行。如果集合发生更改,可以调用命令的raisecannexecutechange,或者我认为您也可以使用NotifyPropertyChanged。@Ludo如果您将命令的CanExecute部分编写为在没有项的情况下返回false,那么您的按钮将非常简单。+1年前,我刚刚想出了一种
    NullToFalseConverter
    方法,如我的编辑中所示。这与我建议的有点不同:您是绑定的
    除了
    命令
    之外,还启用了
    功能,但您可以编写转换器并使用此转换器绑定
    命令
    。不过,您的方法更简单—直到您需要为其他内容启用
    IsEnabled
    :)您的意思是定义静态资源吗