9

あるコレクションの現在のアイテムの一部にcommandプロパティがバインドされているボタンがあるとします。ICommand

コレクションがのnull場合、ボタンは有効なままで、クリックしても何も起こらないようです。代わりに、ボタンを無効のままにしておく必要があります。コレクションがnullの場合は常にボタンを無効にしておくために、次のことがわかりました。ただし、より自然で、より単純で、よりMVVMのようなもので実現できる可能性があるものには少し複雑すぎるようです

したがって、質問:理想的にはコードビハインドが使用されていない場合に、そのボタンを無効に保つためのより簡単な方法はありますか?

.xaml:

<Button Content="Do something" >
    <Button.Command>
        <PriorityBinding>
            <Binding Path="Items/DoSomethingCmd"  />
            <Binding Path="DisabledCmd" />
        </PriorityBinding>
    </Button.Command>
</Button>

.cs:

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;
    }
}

編集

いくつかのメモ:

  1. ラムダ式を使用できることは承知していますが、このサンプルコードでは使用できません
  2. 私は述語が何であるかを知っています。
  3. 現在のアイテムがない間はアクセスできないので、何かをDoSomethingCmd.CanExecuteすることがどのように役立つかわかりません。DoSomethingCmd
  4. だから私は私の質問を再び中心に置きます:どうすれば使用を避けることができDisabledCmdますか?DoSomethingCmdそれは私が探しているものではないので、私は上に移動することに興味がありません。そうでなければ、私はこの質問をしません。

別の編集:

だから私は基本的にこの答えを解決策として採用しました:WPF / MVVM:UserControlの背後にあるViewModelがまだ初期化されていないときにボタンの状態を無効にしますか?

それはまさにhbarckが提案していることだと私は信じています。

4

6 に答える 6

6

私は akjoshi と同様に行いますが、DataTrigger の代わりに通常の Trigger を使用し、Button.Command が null であることを確認します。コマンドを持たないボタンを無効にすることは常に意味があるため (特に、クリック イベント ハンドラーがない MVVM では)、このトリガーをボタンの既定のスタイルに含めて、この動作を実現することもお勧めします。アプリケーション全体のすべてのボタンで...ダミーコマンドを使用する理由がわかりません。

于 2012-05-23T22:52:51.150 に答える
4

Item (ボタンのデータ コンテキスト) が null かどうかを確認する を作成し、TriggerButton の (または Anton が述べたように親コンテナーの場合もある)プロパティIsEnabledをに設定できます。false

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

私は今それをテストする立場にありませんが、これはうまくいくと思います。

于 2012-05-22T13:55:26.530 に答える
3

PresentationFramework.dll のコードを見ると、それを行う簡単な方法がわかりません (「参考文献」を参照ButtonBase.UpdateCanExecute)。自分で変更を処理するために、クラスを派生させButtonてメタデータをオーバーライドする運があるかもしれませんCommandPropertynullただし、ビューモデルに何もしないコマンド コードを含めることは簡単に回避できます。コマンドを共有の常に無効なフォールバックに変換する特別なコンバーターを作成しますICommand。この種の動作を必要とするボタンがたくさんある場合は、添付プロパティとスタイルが適している可能性があります。

于 2012-05-18T09:18:40.307 に答える
1

私は RelayCommands を使用しましたが、これには canExecute 述語を作成できるコンストラクターがあり、それが false を返す場合、バインドされたボタンは自動的に無効になります。

デリゲート コマンドでは、CantDoAnything() メソッドを書き直して、有効化および無効化ロジックを表す必要があります。そして、単にコマンドにバインドする必要があるバインディング。

MSDN の DelegateCommand コンストラクター

DelegateCommand CanExecute バグ修正

于 2012-05-18T07:46:44.373 に答える
1

デリゲート コマンドを見ると、2 番目のパラメーターはまさにこれを可能にする func です。なぜそんなに複雑にしているのかよくわかりません。たとえば、次のようにします。

DoSomethingCommand = new DelegateCommand(() => SampleAction, () => Items != null);

次のように Command プロパティをこのコマンドにバインドするだけで、ボタンは無効になります。

<Button Command={Binding DoSomethingCommand} />

デリゲート コマンドの条件が false になると、ボタンは自動的に無効になります。DoSomethingCommand.RaiseCanExecuteChanged()ボタンの IsEnabled が現在の状態を反映するように更新されるよりも、条件の結果が変わる可能性がある場合にも呼び出す必要があります。

于 2012-05-18T07:40:51.973 に答える
1

ボタンの 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>

モデルの表示 - MVVM Light からの RelayCommand

public class NoCurrentItemViewModel
{
    public NoCurrentItemViewModel()
    {
        _items = new ObservableCollection<NoCurrentItemDetail>
                    {
                        new NoCurrentItemDetail{Name = "one"},
                        new NoCurrentItemDetail{Name = "two"},
                    };

        ClearCommand = new RelayCommand(Clear);
    }

    public ICommand ClearCommand { get; private set; }

    private void Clear()
    {
        _items.Clear();
    }

    private readonly ObservableCollection<NoCurrentItemDetail> _items;

    public IEnumerable<NoCurrentItemDetail> Items
    {
        get { return _items; }
    }
}

public class NoCurrentItemDetail
{
    public NoCurrentItemDetail()
    {
        DoSomethingCommand = new RelayCommand(DoSomething);
    }

    private void DoSomething()
    {
        Debug.WriteLine("Do something: " + Name);
    }

    public ICommand DoSomethingCommand { get; private set; }

    public string Name { get; set; }
}

コンバーター

public class NullToBoolConverter : IValueConverter
{
    public NullToBoolConverter()
    {
        IsNullValue = true;
        IsNotNullValue = false;
    }

    public bool IsNullValue { get; set; }
    public bool IsNotNullValue { get; set; }

    #region Implementation of IValueConverter

    public object Convert(object value, 
        Type targetType, object parameter, CultureInfo culture)
    {
        return value == null ? IsNullValue : IsNotNullValue;
    }

    public object ConvertBack(object value, 
        Type targetType, object parameter, CultureInfo culture)
    {
        return Binding.DoNothing;
    }

    #endregion
}
于 2012-05-21T12:27:55.697 に答える