访问外部聚合类的最佳实践

时间:2013-03-09 08:06:00

标签: c# wpf class

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

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

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

我可以选择一个能够引用ListBox并实现通知方法的单例,但我已经有更多的单身人士在我的项目中感到满意(我不希望每个人都能够访问这些方法,只有某些UCs)。

另一种方法是通过相关的UC构造函数传递Notification Manager实例(可以执行相同的工作)。该经理将拥有将UC连接到主窗口的所有必要方法。

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

2 个答案:

答案 0 :(得分:2)

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

这将如何运作

需要记录的UserControl只会引发冒泡事件,其中包含相关信息(要记录的文本消息)。 UC将不知道该消息将如何处理,甚至是否处理,它只是提出它。这是一种轻量级操作,耦合最小。

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

问题

如果UIElement可用作记录消息事件的原点,则此方法非常正常。

这是一步一步的实施:

LogServices类

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

// 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。最简单的方法是处理执行日志记录的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);
    }

答案 1 :(得分:0)

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

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

1)另一种方法是通过相关的UC构造函数传递Notification Manager实例(可以执行相同的工作)。该经理将反过来拥有将UC链接到主窗口的所有必要方法。 (c)中

2)所有UC必须实现“NotificationRaised”之类的事件接口,并且在主窗口中需要订阅此事件。关于那个说杰西评论你的问题