Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/291.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 用于动态更改WPF UserControl中按钮的启用状态的条件DataTrigger_C#_Wpf_User Controls_Datatrigger_Markup Extensions - Fatal编程技术网

C# 用于动态更改WPF UserControl中按钮的启用状态的条件DataTrigger

C# 用于动态更改WPF UserControl中按钮的启用状态的条件DataTrigger,c#,wpf,user-controls,datatrigger,markup-extensions,C#,Wpf,User Controls,Datatrigger,Markup Extensions,我创建了一个UserControl用作数据导航器。我在此控件中定义了两个dependencProperty,如下所示(dependencProperty隐含): 然后,我放置了四个按钮来执行基本的导航操作(first、prev、next、last)。每个按钮都具有以下样式: <Style x:Key="NavButtonStyle" TargetType="{x:Type Button}"> <Style.Triggers> <DataTri

我创建了一个UserControl用作数据导航器。我在此控件中定义了两个dependencProperty,如下所示(dependencProperty隐含):

然后,我放置了四个按钮来执行基本的导航操作(first、prev、next、last)。每个按钮都具有以下样式:

<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>
最后,我使用这个ME来测试每个按钮的“启用”条件。以下是第一个按钮的条件:


不幸的是,这个解决方案不起作用。我一直得到这个设计时异常:

InvalidOperationException:无法获取不属于视图树的ViewNode的NodePath

有谁有更好的解决方案吗?也许我想用大炮杀死一只苍蝇

提前谢谢。
Eduardo Melo

在我看来,实现按钮启用/禁用功能的最佳方法是使用ICommand界面和CanExecute方法。为此,您可以使用一个轻量级实现,如Prism中的DelegateCommand或MVVMLight中的RelayCommand,如果您确实想要最高效率,请不要向CommandManager注册这些命令,而是在代码中应提供的特定条件下触发CanExecuteChanged

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

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


让我知道,如果这有帮助或只是混乱,我会试图阐明更多的问题!当然,这只是一个想法,可能还有其他同样好的想法。

嗨,亚历克斯!我不熟悉WPF和.NET框架。最近我读了一些关于命令的书,以及它的用途。按照你的思路,我注意到,事实上,这是一个比我提出的更好的解决方案。但我没有得到的是你提到的micro viewmodel。你能再详细说明一下吗?谢谢我只是非常倾向于到处提到ViewModel,因为我喜欢MVVM模式。实际上,您可以将处理命令的代码放在几乎任何地方,包括在命令对象本身中(如果它接收到正确的参数);我通常的第一反应是将其放在处理逻辑的ViewModel中,并将该ViewModel与视图(在本例中为控件)关联。然而,对于用户控件开发,我在控件本身上定义了ICommand,并在ControlTemplate中使用TemplatedParent绑定到它们,这一点同样成功。
<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>
<Button (...) DataContext="{RelativeSource TemplatedParent}">
[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)
}
<Button (...) DataContext="{RelativeSource TemplatedParent}"
    IsEnabled="{DynamicResource {mark:ComparisonBinding Path=View.CurrentPosition, RelativeSource={RelativeSource TemplatedParent}, Comparand={Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataCollection.Count}, Operation=EQ}}">