我目前正在开发一个WPF应用程序,它有一个主窗口,其中包含一个面板,其内容可以互换以显示不同的用户控件。基本上,这只意味着我有一个带有持续可见侧栏的菜单。
我的问题是减少我的课程之间的耦合。我的主窗口中有一个通知部分(以ListBox的形式),可以弹出消息。可以从某些用户控件(UCs)中发生的操作“触发”通知。这些用户控件当然是主窗口类的成员。
由于我需要影响主窗口类中的ListBox控件,我想知道最佳实践是什么。显然,最简单的解决方案就是通过构造函数将主窗口的引用传递给每个UC,但这在许多层面上似乎都不是很有效。我当然只能传递ListBox元素,但这不会真正起作用,因为我必须对添加到ListBox的数据执行操作,因此我必须以编程方式在每个UC中重复这些指令。
我可以选择一个能够引用ListBox并实现通知方法的单例,但我已经有更多的单身人士在我的项目中感到满意(我不希望每个人都能够访问这些方法,只有某些UCs)。
另一种方法是通过相关的UC构造函数传递Notification Manager实例(可以执行相同的工作)。该经理将拥有将UC连接到主窗口的所有必要方法。
我可能还有其他更有效的解决方案。我希望你对这种情况下的最佳实践有所了解,特别是考虑到它产生的类耦合。它可以与这个特定问题相关,但是以更一般的方式考虑它,其中内部聚合类需要访问一些外部资源。感谢。
答案 0 :(得分:2)
我喜欢Jesse的想法来提出一个事件:它非常WPF,并且允许在需要记录内容的UserControl
和提供日志记录的窗口之间松散耦合。此外,执行日志记录的UserControl不依赖于实际可用的日志记录服务,这将使解决方案更加灵活。
需要记录的UserControl
只会引发冒泡事件,其中包含相关信息(要记录的文本消息)。 UC将不知道该消息将如何处理,甚至是否处理,它只是提出它。这是一种轻量级操作,耦合最小。
引发的消息在层次结构链中向上冒泡,一直到顶级元素(Window
,Page
)。沿途的任何地方元素都可以为此事件类型提供处理程序,并通过它通知日志请求。同样,耦合非常松散:实现的位置并不关心谁发送了消息。可以是UserControl或其他任何东西。
如果UIElement
可用作记录消息事件的原点,则此方法非常正常。
我们无法为此目的重复使用现有事件,需要创建一个新事件(以避免混淆)。这应该这样做:
// 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”之类的事件接口,并且在主窗口中需要订阅此事件。关于那个说杰西评论你的问题