【问题标题】:Access to WPF control from a non-window class从非窗口类访问 WPF 控件
【发布时间】:2009-10-28 15:25:35
【问题描述】:

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

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

理查德

【问题讨论】:

标签: wpf controls


【解决方案1】:

已经有一段时间了 - 所以我不会提供太多代码块 - 但听起来您可能想要应用 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 线程上调用。正如您可能猜到的,您用于修改视图模型的线程源自网络中断等。网络线程。恳求。 莫洛克人

无论如何,我离题了。有一个相对无痛的方法来解决这个问题。当您检测到要报告的更改时,将更改分派到 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。我还强烈建议您使用 Google 替代“调用 Dispatcher”,因为这样做可能有更聪明的方法,不涉及控件引用、视图模型中的大量剪切和粘贴等。

只要 Gui 线程更新控件,你就可以为所欲为:)

另外,我的伴侣和 Wpf 门徒 Kent 的无耻插件,他对 Wpf 的了解比我自己多得多。 [http://kentb.blogspot.com/]

干杯:)

【讨论】:

  • 顺便说一句,您最好的 Wpf 资源是 1.) Google 和 2.) Wpf Unleashed [一本书]。试一试:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-16
  • 1970-01-01
相关资源
最近更新 更多