0

データナビゲーターとして使用するUserControlを作成しました。このコントロールで2つのDependencyPropertiesを次のように定義しました(DependencyPropertyを意味します)。

public ICollection DataCollection
{
    get { return GetValue(DataCollectionProperty) as ICollection; }
    set { SetValue(DataCollectionProperty, value); }
}

public ICollectionView View
{
    get { return (DataCollection == null ? null : CollectionViewSource.GetDefaultView(DataCollection)); }
}

次に、基本的なナビゲーション操作(最初、前、次、最後)を実行するための4つのボタンを配置しました。各ボタンのスタイルは次のとおりです。

<Style x:Key="NavButtonStyle" TargetType="{x:Type Button}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding DataCollection}" Value="{x:Null}">
            <Setter Property="IsEnabled" Value="False" />
        </DataTrigger>
    </Style.Triggers>
</Style>

このトリガーは、次のように、RelativeResource TemplatedParentが各ボタンのDataContextとして渡されると仮定して、DataCollectionDependencyPropertyがnullであるかどうかを確認するだけです。

<Button (...) DataContext="{RelativeSource TemplatedParent}">

次に、次のMarkupExtensionを作成して、比較操作と比較された値に基づいて、値を比較し、trueまたはfalseを返します。

[MarkupExtensionReturnType(typeof(bool))]
public class ComparisonBinding : BindingDecoratorBase
{
    public ComparisonOperation Operation { get; set; }
    public object Comparand { get; set; }

    public override object ProvideValue(IServiceProvider provider)
    {
        base.ProvideValue(provider);

        DependencyObject targetObject;
        DependencyProperty targetProperty;
        bool status = TryGetTargetItems(provider, out targetObject, out targetProperty);

        if (status && Comparand != null)
        {
            if (Comparand is MarkupExtension)
                Comparand = (Comparand as MarkupExtension).ProvideValue(provider);
            return Compare(targetObject.GetValue(targetProperty), Comparand, Operation);
        }

        return false;
    }

    private static bool Compare(object source, object target, ComparisonOperation op)
}

最後に、このMEを使用して、各ボタンの「有効化」条件をテストしました。最初のボタンの条件は次のとおりです。

<Button (...) DataContext="{RelativeSource TemplatedParent}"
    IsEnabled="{DynamicResource {mark:ComparisonBinding Path=View.CurrentPosition, RelativeSource={RelativeSource TemplatedParent}, Comparand={Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataCollection.Count}, Operation=EQ}}">

残念ながら、このソリューションは機能しませんでした。私はこの設計時の例外を受け取り続けます:

InvalidOperationException:ビューツリーの一部ではないViewNodeのNodePathを取得できません。

誰かがより良い解決策を持っていますか?たぶん私はここで大砲でハエを殺そうとしています。:)

前もって感謝します。エドゥアルドメロ

4

1 に答える 1

0

私の意見では、ボタンの有効/無効機能を実装する最良の方法は、ICommandインターフェイスとCanExecuteメソッドを使用することです。この目的のために、PrismのDelegateCommandやMVVMLightのRelayCommandのような軽量の実装を使用できます。最高の効率が本当に必要な場合は、コマンドをCommandManagerに登録しないでください。代わりに、コードで使用できる特定の条件でCanExecuteChangedをトリガーします。

実際には、これは、ユーザーコントロールに(ICommandインスタンスとそのExecuteメソッドおよびCanExecuteメソッドの実装を含む)ある種のmicro-ViewModelが含まれる(または含まれる)ことを意味します。これは、とにかくWPF開発に進むための最良の方法です。その場合、XAMLで必要なのは、ボタンのCommandプロパティを適切なICommandにバインドすることだけです。これにより、コマンド(機能的な観点からは「タスク」と見なすことができます)が他の呼び出し元にきれいに公開されます。これには、単体テストも含まれます。

たとえば、コントロールによってICommandを公開し、コントロールテンプレート内のボタンを同じコマンドにバインドすることは完全に合法です(RelativeSource Selfを使用)。それらの可視性を他のプロパティ(UseBuiltInButtons)にバインドすることもできます。後でコントロールをいくつかの凝ったインターフェイスと統合したい場合は、ボタンを非表示にして、外部のボタンを同じICommandにリンクするだけです。

それが助けになるのか、それとも混乱するだけなのかを教えてください。私はこの問題にもっと光を当てようとします!もちろん、これは単なるアイデアであり、他にも同様に優れたアイデアがあるかもしれません。

于 2010-11-21T13:36:14.897 に答える