0

我创建了一个 UserControl 用作数据导航器。我在此控件中定义了两个 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)); }
}

然后,我放置了四个按钮来执行基本的导航操作(第一个、上一个、下一个、最后一个)。每个按钮都有以下样式:

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

这个触发器所做的就是检查 DataCollection DependencyProperty 是否为空,假设将 RelativeResource TemplatedParent 作为每个按钮的 DataContext 传递,如下所示:

<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。

在实践中,这意味着您的用户控件将包含(或本身是)某种微型 ViewModel(带有 ICommand 实例及其 Execute 和 CanExecute 方法的实现),这无论如何都是 WPF 开发的最佳方式。在这种情况下,您需要在 XAML 中将按钮的 Command 属性绑定到适当的 ICommand。这也将干净地将命令(从功能的角度来看可以被视为“任务”)暴露给任何其他调用者,包括单元测试,如果你愿意的话。

例如,让控件公开 ICommand 并在控件模板中将按钮绑定到相同的命令是完全合法的(使用 RelativeSource Self);你甚至可以将它们的可见性绑定到其他一些属性(UseBuiltInButtons),如果你想稍后将你的控件与一些花哨的界面集成,你可以简单地隐藏按钮并将外部按钮链接到相同的 ICommands。

让我知道这是否有帮助或只是令人困惑,我将尝试对此事进行更多说明!当然,这只是一个想法,可能还有其他同样好的想法。

于 2010-11-21T13:36:14.897 回答