.NET Blog   ·   .NET Casts   ·   .NET GUI Foren   ·   .NET BlogBook   ·   WPF Blogger   ·   visual studio one   ·   ASP.NET professional

  • ACHTUNG - NEUES BLOG

    Ab sofort steht unter http://devtyr.norberteder.com mein neues Blog zur Verfügung. Dieses Blog wird nicht weiter betreut, bleibt aber erhalten. Neue Eintr%auml;ge erfolgen nur mehr im neuen Blog. Kommentare werden ebenfalls nicht mehr behandelt. Wer weiterhin meinen Einträgen und Aktivitäten folgen möchte, möge bitte RSS-Feeds, Verlinkungen etc. an die neue Location anpassen.
Download .NET Essentials Installer
Trickkiste

WPF: Offene Fenster im Überblick behalten

22.06.08 - .NET, WPF
Beitrag von 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:
public class WindowInformation
{
    private Window _window; 

    public WindowInformation(Window window)
    {
        _window = window;
    }

    public Window Window
    {
        get { return _window; }
    }

    public int 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:
public class WindowInformationCollection : 
    ObservableCollection<WindowInformation>
{
}

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.
public class WindowManager
{
    private static WindowManager _manager;

    private WindowInformationCollection _openWindows = 
        new WindowInformationCollection();

    private WindowManager() { }

    public static 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;
            }
        }
        return null;
    }

    public bool RemoveOpenWindow(int hashCode)
    {
        WindowInformation windowToRemove = null;
        foreach (WindowInformation wi in _openWindows)
        {
            if (wi.WindowHashCode == hashCode)
            {
                windowToRemove = wi;
                break;
            }
        }
        if (windowToRemove != null)
        {
            _openWindows.Remove(windowToRemove);
            return true;
        }
        return false;
    }
}

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:
public class WindowInformationHandler
{
    public static readonly DependencyProperty 
        IsHandledProperty =
    DependencyProperty.RegisterAttached("IsHandled", 
        typeof(bool), typeof(WindowInformationHandler),
        new FrameworkPropertyMetadata((bool)false,
            new PropertyChangedCallback(OnIsHandledChanged)));

    private static void OnIsHandledChanged(
        DependencyObject dObj, 
        DependencyPropertyChangedEventArgs e)
    {
        Window openWindow = dObj as Window;
        if (openWindow != null)
        {
            openWindow.Loaded += 
                new RoutedEventHandler(WindowLoaded);
            openWindow.Closed += 
                new EventHandler(WindowClosed);
        }
    }

    public static bool GetIsManaged(DependencyObject dObj)
    {
        return (bool)dObj.GetValue(IsHandledProperty);
    }

    public static void SetIsManaged(
        DependencyObject dObj, 
        bool value)
    {
        dObj.SetValue(IsHandledProperty, value);
    }

    private static void 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);
        }
    }

    private static void 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:
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        mnuWindows.ItemsSource = 
            WindowManager.GetInstance.OpenWindows;
    }

    private void NewWindow(object sender, 
        RoutedEventArgs e)
    {
        MainWindow window = new MainWindow();
        window.Title = String.Format("Open Window {0}", 
            Application.Current.Windows.Count);
        window.Show();
    }

    private void 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();
            }
        }
    }
}

Voilá!
  1 Kommentar - 719 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Konvertierung von C# nach VB.NET und wieder zurück

20.06.08 - .NET, Allerlei
Beitrag von Norbert Eder
 Viele können mit C#-Code oder VB.NET-Code nichts anfangen und möchten daher den Sourcecode, den sie als Beispiel erhalten haben gerne konvertieren.

Dafür gibt es zahlreiche Wege (beispielsweise den .NET Reflector). Am einfachsten ist die Verwendung einer Weblösung. Hier zwei Links die sich dafür gut eignen:

Convert C# to VB.NET
Convert VB.NET to C#

  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


WPF: Layout-System und Performance

18.06.08 - WPF
Beitrag von Norbert Eder
 Müssen viele Elemente erstellt und dargestellt werden, kann die Performance schon mal sehr schnell in den Keller gehen. Dabei müssen die darzustellenden Elemente keineswegs komplex sein. Vielmehr reichen oft kleine Fallen, die vermieden werden wollen.

Oftmals führt das Verwenden eines falschen Panels zu einem Performance-Problem. Meist ist es so, dass genau das Panel verwendet wird, welches die gewünschte Aufgabe mit dem geringsten Aufwand (für uns als Entwickler) erfüllt. So wird also beispielsweise ein StackPanel verwendet, sollen Elemente untereinander platziert werden - und zwar in der Reihenfolge, wie sie hinzugefügt wurden. Werden ein paar wenige Elemente hinzugefügt, ist keine Auswirkung zu spüren. Sind es hunderte oder gar tausende, dann wird dieser Vorgang spürbar langsamer.

Hierfür verantwortlich ist die unterschiedliche Funktionalität der Panels. Während ein Canvas relativ dumm ist und Elemente basierend auf einer Positionsangabe platziert, ist diese Logik in einem StackPanel oder einem DockPanel wesentlich komplexer.

Layout System


Vom Layout-System werden pro Kindelement zwei Schritte durchgeführt:
  • Measure
  • Arrange

Jedes dieser Kindelemente besitzt seine eigene Implementierung von Measure und Arrange, damit die an das Element gestellten Anforderungen bezüglich des Layout-Verhaltens erfüllt werden können.

Ablauf Layouting
  • Ein Kindelement (UIElement) beginnt den Layoutprozess durch das Erfassen der Basiseigenschaften
  • Dann werden die Eigenschaften bezüglich Höhe, Breite, Randstärke ausgewertet
  • Ausführung von Panel-spezifischer Logik (siehe Orientation-Eigenschaft des StackPanels)
  • Der Inhalt wird ausgerichtet bzw. positioniert, nachdem die Größe etc. aller Kindelemente bemessen wurde.
  • Die Liste der Kindelemente wird am Bildschirm ausgegeben


Wann wird es haarig?

Beim Eintreten bestimmter Aktionen, wird der gesamte Layout-Prozess erneut durchlaufen. Folgende Aktionen sind dafür maßgeblich:
  • Hinzufügen eines neuen Kindelementes
  • Ausführung der Methode UpdateLayout eines Kindelementes
  • Zuweisung von LayoutTransform eines Kindelementes
  • Bei Änderung einer Dependency Property mit Auswirkung auf Measure und Arrange (gesetzt über die Metadaten)


Fazit


Durch das Wissen wie das Layout-System funktioniert und wann es erneut durchlaufen werden muss, lässt sich feststellen, in welchen Fällen worauf verzichtet werden sollte. So kann es beispielsweise effizienter sein, eine ListBox anstatt eines StackPanels zu verwenden (durch das Definieren von Templates kann das auch schön aussehen), oder vielleicht doch einen Canvas.

Ist man sich nicht sicher, ob die geplante Variante die beste Performance bietet, bietet sich auch an, vor der konkreten Implementierung dann eventuell doch eine kleine Beispielanwendung zu schreiben. Auch eingesetzte Profiler sind hilfreich.

Siehe auch: WPF: Performance messen und verbessern

  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Ein Wortspiel gefällig?

17.06.08 - Blog-Intern
Beitrag von Norbert Eder
 Heute habe ich mir aus Spaß die letzten Einträge meines Blogs via wordle.net grafisch darstellen lassen. Eigentlich eine recht nette Sache. Hier das Ergebnis:



Damit hätten sich auch schon ein paar passende Keywords zusammen getan ;-)

  1 Kommentar - 381 mal angesehen   |  1 Trackbacks   |  Permalink  |  Trackback-URL


.NET Blog - Refresh

10.06.08 - Blog-Intern
Beitrag von Norbert Eder
 Mit zunehmender Zahl an Artikeln und Besuchern, musste die Online-Präsenz http://www.dotnet-blog.com (englischsprachig) nun endlich ein Update erfahren:

Neues Layout



Zusätzlich wurden auch die Kategorien angepasst. D.h. es wurde nicht nur die Anzahl verringert, sondern auch die Aussagekraft erhöht.

  Kommentar hinzufügen   |  1 Trackbacks   |  Permalink  |  Trackback-URL


WPF Anwendungen um Add-Ins erweitern

09.06.08 - .NET, WPF
Beitrag von Norbert Eder
 Wer seine WPF Anwendungen um Add-Ins erweitern möchte, der wird sich vielleicht das eine oder andere Szenario überlegen. Die Windows Presentation Foundation steht aber auch hier hilfreich zur Seite. Unter nachfolgendem Link sind zahlreiche Artikel und Beispiele zu finden, die helfen, diese Anforderung entsprechend umzusetzen.

Windows Presentation Foundation Add-Ins Overview

  3 Kommentare - 5836 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


In eigener Sache: Diagramm-Tool mit EPS-Export gesucht

09.06.08 - Blog-Intern
Beitrag von Norbert Eder
 Nach langer Suche und keinem zufriedenstellenden Ergebnis wende ich mich hiermit an meine Leser. Möglicherweise hat jemand einen Tipp für mich.

Gesucht: Ein Werkzeug um Fluss-Diagramme etc. zu erstellen, welche als EPS exportiert werden können.

Anforderungen:
  • Diagramme sollten durchaus nett anzusehen sein. D.h. 80er Jahre Stil ist nicht unbedingt gefragt
  • Preise á la Adobe-Produkte möchte ich definitiv nicht ausgeben, ich bin aber bereit den einen oder anderen Euro auszugeben
  • Sollte unter Vista lauffähig sein

Für sachdienliche Hinweise wäre ich sehr dankbar. Bitte jedoch nur Hinweise auf Anwendungen, die getestet wurden.

  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Artikel: Flexibel und einfach wie HTML

08.06.08 - Blog-Intern
Beitrag von Norbert Eder
  In der Ausgabe 03/08 der Visual Studio One findet sich ein Artikel zum Thema WPF, geschrieben von meiner Wenigkeit:

Nicht nur für Softwareentwickler gibt es (zu Recht) Styleguides. Auch für Windows-Anwendungen stehen von Microsoft offizielle Richtlinien zur Verfügung. So war es schon für Windows XP, und so ist es auch für Windows Vista. In diesem Artikel erfahren Sie mehr über den Windows Vista Styleguide und erhalten Anregungen und Hinweise dazu, wie Sie Ihre WPF-Anwendungen daran ausrichten können.

Weitere Informationen auf der Homepage der Visual Studio One.
  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


WPF und MVC

08.06.08 - Entwicklung, Diskussionen, Patterns, .NET, WPF
Beitrag von Norbert Eder
 Anwendungen sollten nicht nur schön anzusehen sein, sondern auch das robust und korrekt tun, wofür sie geschaffen wurden. Damit dem so ist, muss sich der Entwickler/Architekt natürlich zu Beginn eines Projektes so einige Gedanken machen. Denn nur durch ein gutes Grundgerüst werden Anforderungen á la Erweiterbarkeit, Testbarkeit, einfache Wartung usw. auch erfüllt.

Das Model View Controller Pattern ist da so ein Ansatz. Mittlerweile Jahrzehnte am Buckel hatte sich dieses Pattern immer wieder bewährt (in seiner ursprünglichen Form oder in einer abgewandelten).

Wie nun die Windows Presentation Foundation mit dem Model View Controller Pattern zusammen arbeitet, zeigt der Artikel MVC Pattern mit WPF verwenden, welchen ich gestern auf .NET GUI veröffentlicht habe.
  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


WPF, Layouts und verschwundene Scrollleiste

05.06.08 - .NET, WPF
Beitrag von Norbert Eder
 

Das Problem


Ich habe auf meinem Fenster einige Elemente angeordnet. Darunter auch eine ListView. Diese hatte anfangs eine Scrollleiste, aber jetzt ist diese verschwunden? Warum?

Die Lösung


Diese und ähnliche Fragen werden sehr häufig gestellt. In den meisten Fällen liegt es daran, dass die verwendeten Layout-Elemente nicht gut genug bekannt sind und daher ein Fehlverhalten interpretiert wird, wo eigentlich gar keines ist.

Ein sehr verdächtiges Element ist in diesem Zuge das StackPanel. Es verändert seine Größe abhängig der eingebetteten Elemente.

Hier ein kleiner Beispielfall um dies näher zu beschreiben: Wir gehen davon aus, dass es ein StackPanel gibt. Dieses hat als Kindelement eine ListView. Letztere ist an eine Liste gebunden, welche einige hundert Elemente besitzt. Wird nun die Liste gebunden, vergrößert sich die ListView mit jedem Element das hinzugefügt wird. Bei einigen hundert Elementen kann man davon ausgehen, dass nicht mehr alle am Bildschirm angezeigt werden können und deswegen eine Scrollbar durchaus sehr sinnvoll wäre. Tatsächlich ist es nun so, dass sich das Elternelement, also das StackPanel, an die Größe der ListView anpasst und es somit gar nie notwendig wird, die Scrollbar anzuzeigen.
<DockPanel x:Name="ParentDock" Margin="8" 
           LastChildFill="True">
    <StackPanel DockPanel.Dock="Top">
        <Button Content="Click me first"/>
        <Button Content="Do not click me"/>
        <ListView x:Name="DemoBox">
            <ListView.View>
                <GridView>
                    <GridViewColumn/>
                    <GridViewColumn/>
                    <GridViewColumn/>
                </GridView>
            </ListView.View>
        </ListView>
    </StackPanel>
</DockPanel>

In diesem Beispiel ist das StackPanel zwar in ein DockPanel eingebettet, verändert aber dennoch seine Größe, da der Inhalt wesentlich größer ist. So sieht es aus:



In diesem Fall muss auch gar nicht viel verändert werden. Damit die ListView zu ihrer Scrollbar kommt, ist diese lediglich aus dem StackPanel heraus zu nehmen:
<DockPanel x:Name="ParentDock" Margin="8" 
           LastChildFill="True">
    <StackPanel DockPanel.Dock="Top">
        <Button Content="Click me first"/>
        <Button Content="Do not click me"/>
    </StackPanel>
    <ListView x:Name="DemoBox">
        <ListView.View>
            <GridView>
                <GridViewColumn/>
                <GridViewColumn/>
                <GridViewColumn/>
            </GridView>
        </ListView.View>
    </ListView>
</DockPanel>

Damit ist nun das DockPanel für die ListView zuständig und beschränkt deren Größe. Dadurch wird die Scrollbar sichtbar. So sieht’s aus:



Fazit


Durch dieses Problem muss sich jeder durchkämpfen, der mit WPF beginnt. Die Verhaltensweisen sind unterschiedlich zu Windows Forms, in einigen Fällen ähnlich zu ASP.NET. Wichtig ist, sich mit den jeweiligen Layout-Elementen zu beschäftigen und zu lernen, wie sie mit Inhalten umgehen. Mit ein wenig Spielerei sollten dann zukünftig derartige Probleme vermieden werden können. Es warten ohnehin genug andere …

  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL



Zurück Weiter