【问题标题】:Using callback interface as a DependencyProperty in WPF?在 WPF 中使用回调接口作为 DependencyProperty?
【发布时间】:2014-12-15 03:55:56
【问题描述】:

对于这个冗长的问题,我深表歉意,但我觉得有必要包含所有这些信息。

直到现在,我一直在使用一种可能是非正统的方式将 UserControls 添加到我的应用程序中。假设我有一个名为Diagnostics 的用户控件,它有一个按钮,当单击该按钮时,将执行一个特定于拥有它的应用程序的功能。例如,如果我将 Diagnostics 放入 AppA,我希望它显示“A”,如果我将其放入 AppB,我希望 AppB 定义行为,使其显示“B”。

我通常通过传递给 UserControl 的构造函数的回调接口来实现这一点,这非常简单。这是一些可能无法编译的示例“代码”,但只是为了阐明我以前基本上做过的事情以及我正在尝试做的事情:

public interface IDiagnosticsCallback {
    void DisplayDiagnostics(); // implemented by owner of Diagnostics UserControl
}

public class MyApp : IDiagnosticsCallback {
    public void DisplayDiagnostics() {
        MessageBox.Show("Diagnostics displayed specifically for MyApp here");
    }
}

public Diagnostics : UserControl {
    private IDiagnosticsCallback _callback { get; private set; }

    public Diagnostics(IDiagnosticsCallback callback) {
        _callback = callback;
    }

    public void ShowDiagnostics_Click(object sender, EventArgs e) {
        _callback.DisplayDiagnostics();
    }
}

我过去遇到的问题是理解如何在 XAML 中声明一个在其构造函数中接受参数的 UserControl(即没有默认构造函数),显然你不能。我用一种相当不优雅的方法解决了这个问题——我会在 XAML 中给主面板起一个名字,然后从代码隐藏中创建诊断,将必要的回调传递给它,然后我会将诊断添加到面板的列表中孩子的。严重违反了 MVVM 的用法,但它确实有效。

这个周末,我决定尝试学习如何为一个类和一个 TextBox 做这件事,结果我所要做的就是在我的 UserControl 中创建一个DependencyProperty 并使用数据绑定。它看起来像这样一些

public ClassA
{
    public void ShowSomethingSpecial()
    {
        MessageBox.Show("Watch me dance!");
    }
}

public MyApp
{
    public ClassA Foo { get; set; }
}

public Diagnostics : UserControl
{
    public static readonly DependencyProperty SomethingProperty = DependencyProperty.Register("Something", typeof(ClassA), typeof(Diagnostics), new PropertyMetadata());

    public ClassA Something
    {
        get { return (MyApp)GetValue(SomethingProperty); }
        set { SetValue(SomethingProperty, value); }
    }

    // now uses default constructor

    public void ShowSomethingSpecial_Click(object sender, EventArgs e)
    {
        Something.ShowSomethingSpecial();
    }
}


MyApp.xaml
<diags:Diagnostics Something="{Binding Foo}" />

所以 Foo 是 MyApp 的一个属性,它数据绑定到 Diagnostics 的 Something DependencyProperty。当我单击 UserControl 中的按钮时,行为由 ClassA 定义。好多了,并且可以与 MVVM 一起使用!

我现在想做的是更进一步,而是将回调接口传递给我的 UserControl,以便它可以获取我的数字输入和输出的状态。我正在寻找这样的东西:

public Diagnostics : UserControl
{
    public interface IDioCallback
    {
        short ReadInputs();
        short ReadOutputs();
        void SetOutput( char bit);
    }

    public IDioCallback DioCallbackInterface {
        get { return (IDioCallback)GetValue(DioCallbackInterfaceProperty); }
        set { SetValue(DioCallbackInterfaceProperty,value); }
    }

    // Using a DependencyProperty as the backing store for DioCallbackInterface.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DioCallbackInterfaceProperty = DependencyProperty.Register("DioCallbackInterface",typeof(IDioCallback),typeof(Diagnostics),new PropertyMetadata(0)); // PropertyMetadata is the problem...
}

public class DIO : IDioCallback
{
    public short ReadInputs() { return 0; }
    public short ReadOutputs() { return 0; }
    public void SetOutput( char bit) {}
}

public class MyApp 
{
    public DIO MyDIO { get; set; }
}

MyApp.xaml
<diags:Diagnostics DioCallbackInterface="{Binding MyDIO}" />

虽然我的代码(可能不是上面的确切代码,而是我的真实项目)编译成功,但传递给 Register 的 PropertyMetadata 似乎有问题。我收到一个异常提示“默认值类型与属性'DioCallbackInterface'的类型不匹配。”

我是在做一些非常不正统的事情,还是这种数据绑定接口的方法实际上可行?如果不是,根据使用它的应用程序定义 UserControl 行为方式的推荐方法是什么?

【问题讨论】:

    标签: wpf xaml c#-4.0 mvvm dependency-properties


    【解决方案1】:

    因此您提到的异常:

    new PropertyMetadata(0)
    

    您已传递 0(类型为 Int32)而不是 null 或任何您喜欢的接口:IDioCallback

    我不能说您选择的方式是错误的,但您应该记住,您的 UserControl 的每个用户都必须实现您定义的接口。如果您有几个想要传递给 UserControl 的属性,基本上可以通过 DependencyProperty 丢弃它们。

    在您的情况下,您还想向UserControl Button 注入一些逻辑。让我假设这个控件只有一个按钮。 MVVM-处理Button.Click 事件的方式是通过ICommand 完成的-您可以在ViewModel 中声明命令属性并将其用作UserControl 中数据绑定的数据源为DependencyProperty,将其正确传递给 Button

    您还可以与所有数据上下文达成协议,并为该属性使用特殊名称。例如:

    public interface IViewModelWithCommand
    {
        public ICommand TheCommand { get; }
    }
    

    为您需要的每个数据上下文实现它,并在 UserControl 的数据模板中使用 TheCommand 属性名称。在代码隐藏中,您可以创建传递给 UserControlDataContext 的类型验证,并在类型未实现您的接口的情况下抛出异常

    这里有几篇您可能应该感兴趣的文章:

    使用 RelayCommand 将简化您的生活,因为您不需要为每个命令重新实现接口,而是需要传递您想要的有效操作。

    【讨论】:

    • 感谢您的回复!因为我想传递一个接口,所以我想做的似乎是不可能的。我通过传入派生类来临时解决它,但这不是我真正想要的。我已经使用 RelayCommand 好几年了,发现它不可或缺。我的具体问题不是关于处理命令的正确方法,而是如何通过 XAML 获取子控件回调接口,以便在单击按钮时,它们可以执行由实现该接口的类定义的特定操作。
    • ICommand 是接口。您可以将 ICommand 实例传递给 Button 控件吗?我想是的。因此,您可以将接口实例传递给 UserControl。您需要在示例代码中修复的是使用new PropertyMetadata(null)
    • 谢谢,我确实使用了 new PropertyMetadata(null) 并防止了崩溃。我最终采用的解决方案是在我最终用于 DependencyProperty 的模型中引用我的回调接口,这似乎工作得很好。
    猜你喜欢
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 2019-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多