This post originated from an RSS feed registered with .NET Buzz
by -.
Original Post: WPF: Offene Fenster im Überblick behalten
Feed Title: Norbert Eder - Living .NET
Feed URL: http://feeds.feedburner.com/NorbertEder-Livingnet
Feed Description: Copyright (c)2005, 2006 by Norbert Eder
Wir kennen es wohl alle: Das "Window"-Menü mit allen offenen Fenstern derselben Applikation. Vor allem in den Office-Produkten war dies immer wieder zu finden. Doch wie kann dies unter WPF implementiert werden? Dieser Artikel zeigt wie's geht. Und so kann das Endresultat aussehen (es ist zwar nicht hübsch, aber es funktioniert):
Zu Beginn stellt sich natürlich dir Frage ob es da nicht schon etwas Fertiges gibt. Ja. Gibt es. Unter Application.Current.Windows findet sich die aktuelle Auflistung aller geöffneten Fenster. Einziger Nachteil: Diese stecken in einer eigenen WindowCollection, welche lediglich ICollection und IEnumerable implementiert. Damit jedoch Data Binding möglich ist, wäre eine ObservableCollection notwendig.
Damit es uns Entwicklern nicht fad wird, müssen wir daher selbst in die Tasten klopfen und unser eigenes System entwickeln, wollen wir doch Data Binding verwenden!
Zuerst benötigen wir eine simple Datenklasse, welche Informationen für uns hält und auch später wieder zugänglich macht:
publicclass WindowInformation
{
private Window _window;
public WindowInformation(Window window)
{
_window = window;
}
public Window Window
{
get { return _window; }
}
publicint WindowHashCode
{
get { return _window.GetHashCode(); }
}
public String Title
{
get { return _window.Title; }
}
public ImageSource Icon
{
get { return _window.Icon; }
}
}
Die Datenklasse fungiert in diesem Fall lediglich als Wrapper-Klasse, könnte aber ohne großen Aufwand um weitere Informationen angereichert werden.
Damit nun Data Binding verwendet werden kann, benötigen wir eine - wie schon angesprochen - ObserableCollection:
Nun wird eine Manager-Klasse benötigt, welche die Liste der geöffneten Fenster hält und zusätzliche Funktionen wie Entfernen eines Fensters oder das Zurückgeben eines speziellen Fensters zur Verfügung stellt. Dieser Manager wurde mit dem Singleton-Pattern umgesetzt, damit sichergestellt ist, dass nur eine Instanz davon pro gestarteter Anwendungsinstanz vorhanden ist.
publicclass WindowManager
{
privatestatic WindowManager _manager;
private WindowInformationCollection _openWindows =
new WindowInformationCollection();
private WindowManager() { }
publicstatic WindowManager GetInstance
{
get
{
if (_manager == null)
_manager = new WindowManager();
return _manager;
}
}
public WindowInformationCollection OpenWindows
{
get { return _openWindows; }
}
public Window GetOpenWindow(int hashCode)
{
foreach (WindowInformation wi in _openWindows)
{
if (wi.WindowHashCode == hashCode)
{
return wi.Window;
}
}
returnnull;
}
publicbool RemoveOpenWindow(int hashCode)
{
WindowInformation windowToRemove = null;
foreach (WindowInformation wi in _openWindows)
{
if (wi.WindowHashCode == hashCode)
{
windowToRemove = wi;
break;
}
}
if (windowToRemove != null)
{
_openWindows.Remove(windowToRemove);
returntrue;
}
returnfalse;
}
}
Zu guter Letzt wird noch ein Handler implementiert, der schlussendlich in das Window-Element des XAMLs eingebunden werden kann und somit ein Fenster in das System einbindet:
publicclass WindowInformationHandler
{
publicstaticreadonly DependencyProperty
IsHandledProperty =
DependencyProperty.RegisterAttached("IsHandled",
typeof(bool), typeof(WindowInformationHandler),
new FrameworkPropertyMetadata((bool)false,
new PropertyChangedCallback(OnIsHandledChanged)));
privatestaticvoid OnIsHandledChanged(
DependencyObject dObj,
DependencyPropertyChangedEventArgs e)
{
Window openWindow = dObj as Window;
if (openWindow != null)
{
openWindow.Loaded +=
new RoutedEventHandler(WindowLoaded);
openWindow.Closed +=
new EventHandler(WindowClosed);
}
}
publicstaticbool GetIsManaged(DependencyObject dObj)
{
return (bool)dObj.GetValue(IsHandledProperty);
}
publicstaticvoid SetIsManaged(
DependencyObject dObj,
boolvalue)
{
dObj.SetValue(IsHandledProperty, value);
}
privatestaticvoid WindowClosed(
object sender, EventArgs e)
{
Window closedWindow = sender as Window;
if (closedWindow != null)
{
WindowManager.GetInstance.RemoveOpenWindow(
closedWindow.GetHashCode()
);
openWindow.Loaded -=
new RoutedEventHandler(WindowLoaded);
openWindow.Closed -=
new EventHandler(WindowClosed);
}
}
privatestaticvoid WindowLoaded(
object sender, RoutedEventArgs e)
{
Window openWindow = sender as Window;
if (openWindow != null)
{
WindowInformation winInfo =
new WindowInformation(openWindow);
WindowManager.GetInstance.OpenWindows.Add(winInfo);
}
}
}
Wird die Eigenschaft IsHandled auf true gesetzt, werden zwei Events (Loaded und Closed) registriert. Auf diese wird in weiterer Folge reagiert, um das Fenster nach erfolgtem Laden in die Liste der geöffneten Fenster aufzunehmen bzw. davon auch wieder zu entfernen.
In XAML wird dies folgendermaßen in das Window-Element eingebunden:
local:WindowInformationHandler.IsManaged="true"
Damit ist dieses System fertig und kann angewandt werden.
Das XAML-Markup für oben gezeigten Screenshot sieht so aus:
Und hier noch der Inhalt aus dem Code Behind:
publicpartialclass MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
mnuWindows.ItemsSource =
WindowManager.GetInstance.OpenWindows;
}
privatevoid NewWindow(object sender,
RoutedEventArgs e)
{
MainWindow window = new MainWindow();
window.Title = String.Format("Open Window {0}",
Application.Current.Windows.Count);
window.Show();
}
privatevoid ShowWindow(object sender,
RoutedEventArgs e)
{
MenuItem mi = sender as MenuItem;
if (mi != null)
{
WindowInformation wi = mi.DataContext
as WindowInformation;
if (wi != null)
{
wi.Window.Focus();
}
}
}
}