1

我目前正在开发一个 WPF 应用程序,该应用程序的主窗口包含一个面板,其内容可以互换以显示不同的用户控件。基本上,这只是意味着我有一个带有持续可见侧边栏的菜单。

我的问题是关于减少我的类之间的耦合。我的主窗口中有一个通知部分(以 ListBox 的形式),可以在其中弹出消息。可以从某些用户控件 (UC) 中发生的操作“触发”通知。这些用户控件当然是主窗口类的成员。

由于我需要影响主窗口类中的 ListBox 控件,我想知道最佳实践是什么。显然,最简单的解决方案就是通过它们的构造函数向每个 UC 传递对主窗口的引用,但这在许多层面上似乎不是很有效。我当然只能传递 ListBox 元素,但这也不起作用,因为我必须对添加到 ListBox 的数据执行操作,因此我必须以编程方式在每个 UC 中重复这些指令。

我可以使用一个引用 ListBox 并实现通知方法的单例,但我已经在我的项目中有更多我喜欢的单例(而且我不希望每个人都能够访问这些方法,只有某些 UC)。

另一种方法是通过相关 UC 的构造函数传递一个通知管理器实例(将执行相同的工作)。反过来,该经理将拥有将 UC 链接到主窗口的所有必要方法。

可能还有我没有想到的其他更有效的解决方案。我希望您对这种情况下的最佳实践提出意见,特别是考虑到它生成的类耦合。它可能与这个特定问题有关,但是以更一般的方式考虑它,内部聚合类需要访问外部的一些资源。谢谢。

4

2 回答 2

2

UserControl我喜欢 Jesse 提出事件的想法:它非常类似于 WPF,并且允许需要记录内容的窗口与提供日志记录的窗口之间松散耦合。此外,进行日志记录的用户控件不依赖于实际可用的日志记录服务,这将使解决方案更加灵活。

这将如何工作

UserControl需要记录的只是引发一个包含相关信息(要记录的文本消息)的冒泡事件。UC 将不知道如何处理或即使处理该消息,它只是提出它。这是一个轻量级的操作,并且耦合是最小的。

引发的消息在层次结构链中向上冒泡,一直到顶级元素 ( Window, Page)。在此过程中的任何地方,元素都可以为此事件类型提供处理程序,并通过它获得有关日志请求的通知。同样,耦合非常松散:实现的地方并不关心谁发送了消息。可以是 UserControl 或其他任何东西。

问题

如果 anUIElement可用作日志消息事件的来源,这绝对可以正常工作。

下面是一步一步的实现:

日志服务类

我们不能为此目的重用现有事件,需要创建一个新事件(以避免混淆)。这应该这样做:

// Subclass of RoutedEventArgs that allows us to easily and nicely pass the message
// to be logged
public class LogEventArgs : RoutedEventArgs
{
    public string Msg { get; set; }
    public LogEventArgs(RoutedEvent routedEvent, Object sender, string Msg)
        :base(routedEvent, sender)
    {
        this.Msg = Msg;
    }
}

// This is the Delegate that's used to grab the Log message
public delegate void RoutedLogEventHandler(Object sender, RoutedEventArgs e);

// This works as the abstraction layer that will allow UC's to raise LOG messages
// and allow your implementation to alter the way it handles those LOG messages.
// Since we're doing this with a routed event, we need an DependencyObject to
// reigster it.
public class LogServices :UIElement
{        
    public static RoutedEvent LogEvent;

    // Static constructor, registers the event
    static LogServices()
    {
        LogEvent = EventManager.RegisterRoutedEvent("Log", RoutingStrategy.Bubble, typeof(RoutedLogEventHandler), typeof(UIElement));
    }

    // This helps raise the relevant shared event
    public static void RaiseLog(string Msg, UIElement sender)
    {
        sender.RaiseEvent(new LogEventArgs(LogEvent, sender, Msg)); 
    }

}

上面的代码声明了子类,RoutedEventArgs因为我们需要传递我们的字符串 Message。然后它创建一个接受我们LogEventArgs参数的新委托类型,最后从LogServices.

如何发送事件

发送事件非常简单。这是按钮的 Click 处理程序:

LogServices.RaiseLog("Message to be logged.", sender as Button)

接收事件

我们的事件被注册为“冒泡”事件:它将从我们引发它的控件开始,并让每个父级都有机会处理它,一直到窗口。最简单的事情是在进行Window日志记录的事件中处理事件。不幸的是,这种类型的共享不能直接从 中设置XAML,它需要从代码中分配。这就是我尝试从 分配它的方式XAML,但它不起作用:

<Grid local:LogService.Log="HandlerName" />

下一个选项是从代码中分配它。这是我的测试中的一个片段Window1

    // Window Constructor
    public MainWindow()
    {
        InitializeComponent();
        // Set the event handler.
        base.AddHandler(LogServices.LogEvent, new RoutedLogEventHandler(HandleMsgLog));
    }

    // This is the actual handler.
    public void HandleMsgLog(Object sender, RoutedEventArgs e)
    {
        // Put the received message into the ListBox
        LB.Items.Add((e as LogEventArgs).Msg);
    }
于 2013-03-09T09:46:22.440 回答
0

您可以阅读观察者模式(http://en.wikipedia.org/wiki/Observer_pattern),但这种模式比您的情况更广泛使用。

在您的情况下,我认为好的变体是:

1) 另一种方法是通过相关 UC 的构造函数传递一个通知管理器实例(将执行相同的工作)。反过来,该经理将拥有将 UC 链接到主窗口的所有必要方法。(C)

2) 所有 UC 必须实现与“NotificationRaised”等事件的接口,并且在您的主窗口中您需要订阅此事件。杰西在评论你的问题时说

于 2013-03-09T09:25:13.417 回答