从非窗口类访问WPF控件

时间:2009-10-28 15:25:35

标签: wpf controls

我是WPF的新手,对小项目有疑问。我正在构建一个测试器来模拟由UDP控制的设备。我有几个类将UDP实现为异步实现。我有主窗口代码隐藏轮询接收缓冲区的数据和更新。它奏效了,但非常笨重。所以我移动了EndReceiveFrom所在的异步接收中的所有代码。它更快,摆脱了民意调查。但我无法再访问窗口控件来更新它们。

如何从窗外访问控件?该窗口实例化运行它的UDP类,因此我可以在构造函数调用上传递内容。当然,我可以访问App。我知道我在这里遗漏了一些东西。

理查德

1 个答案:

答案 0 :(得分:2)

过了一段时间 - 所以我不会提供太多的代码块 - 但听起来你可能想要应用MVC,或它的堂兄MVP或MVVM。不要过于担心它们的含义,但一般来说,它们会尝试定义View [您的窗口],Data [您的网络层结果]和Business [您的网络层]之间的关系。

在你的情况下,我会推荐MVVM。如果你谷歌它,你应该能够找到有关设计模式和应用方式的大量信息。再说一遍,不要过分夸大理论。只是一个假。

接下来,一些肉,

从概念上讲,您希望在“视图”中表示“数据”。在不担心视图的情况下,让我们首先封装您的数据。

// name is a little too general, so just rename it to something
// specific in your source.
public class NetworkViewModel 
{
    // enumerate your data here
    public int Trouble { get; set; }
    public int Tribbles { get; set; }
    ...
}

您的观点就是您的窗口。让您的视图实例化此“视图模型”的实例

public class MyNetworkMonitor
{
    ...
    // advanced: if you were injecting this - say from another source
    // or intended to replace the instance during runtime, there are
    // ways to deal with that. for now, we presume one instance for
    // application lifetime.
    public NetworkViewModel ViewModel { get; set; }
    public MyNetworkMonitor ()
    {
        ViewModel = new NetworkViewModel ();
    }
    ...
}

并在您的Xaml中绑定到属性

// Xaml omitted, a limit to my memory :P
// but just regular binding to say ViewModel.Trouble

好的。因此,如果您的视图模型具有初始值,它将显示给绑定控件。不幸的是,如果视图模型属性更新,则无法告知绑定控件值已更改。呃,除非你实现了Wpf绑定寻找的通用接口,

public class NetworkViewModel : INotifyPropertyChanged
{
    // defined by interface
    public event PropertyChangedEventHandler PropertyChanged;
    private int _trouble = 0;
    public int Trouble 
    {
        get { return _trouble; }
        set 
        {
            // 1. if value changes
            if (_trouble != value)
            {
                _trouble = value;
                // 2. inform whomever is listening!
                if (PropertyChanged != null) 
                {
                    PropertyChanged (
                        this, 
                        new PropertyChangedEventArgs ("Trouble"));
                }
            }
        }
    }
}

当然,你可以包装并做任何你喜欢的事情来简化语法,但是在模型方面你的基本实现是什么。

如果您要在窗口中按下按钮并增加ViewModel.Trouble,您会在绑定控件中看到实时更新。好哇!

然后剩下的就是将业务层(即您的网络层)连接到ViewModel。你可以通过多种方式实现这一目标。您可以将视图模型的实例传递到网络层,您可以让视图模型直接响应网络层上的事件 - 这完全取决于您。

现在,如果您继续这样做,它将无法正常工作。抱歉。现在,这不完全是我的错,你看到Wpf不喜欢任何人修改它的数据。更具体地说,任何可能影响Wpf控件的内容必须在拥有该控件的Gui线程上调用。正如您可能猜到的,您用于修改视图模型的线程源自网络中断和诸如此类的东西。网络线程。 Pleabs。 莫洛克 Pah中

无论如何,我离题了。对此有一种相对无痛的方式。当您检测到要报告的更改时,将更改分派给Gui线程。

someWpfControl.Dispatcher.Invoke (
    DispatcherPriority.Normal,
    new Action(
    delegate()
    {
        ViewModel.Trouble = someNewValue;
    });

但这有点无用,因为你的网络层不应该知道关于Wpf的任何信息。那么在ViewModel

中如何呢?
...
// within property, after detecting change and 
// testing for listeners
someWpfControl.Dispatcher.Invoke (
    DispatcherPriority.Normal,
    new Action(
    delegate()
    {
        PropertyChanged (
            this, 
            new PropertyChangedEventArgs ("Trouble"));
    });
...

和以前一样,随意把它包装在其他任何地方。就像在辅助方法中一样。顺便说一句,'someWpfControl'可以成为窗口。所以你可以把它传递给ctor。我也强烈建议你选择“调用Dispatcher”的Google替代方案,因为可能有更聪明的方法,不涉及控件引用,在视图模型中进行大量剪切和粘贴等等。

只要Gui线程更新控件,您就可以执行任何操作:)

另外,我的伴侣和Wpf弟子肯特的无耻插件,他对Wpf的了解比我自己更多。 [http://kentb.blogspot.com/]

干杯:)