.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

Das .NET BlogBook bricht die 20.000 Downloads-Schallmauer

02.04.08 - Blog-Intern, .NET, Grundlagen, Base Framework, WPF, ASP.NET, Silverlight, Mobile Devices, Datenverwaltung, Visual Studio, Allerlei, Microsoft Office, SQL Server
Beitrag von Norbert Eder
 Kürzlich wurde der 20.000ste Download vom .NET BlogBook vollführt. Für mich als Herausgeber ist dies natürlich eine große Freude. Schließlich existiert das .NET BlogBook erst seit knapp mehr als einem Jahr.

Das gibt natürlich Motivation für weitere Anpassungen, Ergänzungen und natürlich Verbesserungen.

Vielen Dank auch an alle Leser, die das Team mit Rückmeldungen motivieren oder Verbesserungen anregen.

Danke!

  1 Kommentar - 781 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Artikel: Techniken miteinander verbinden

17.03.08 - .NET, Grundlagen, Base Framework, WPF, Datenverwaltung
Beitrag von Norbert Eder
  Wie die Zeit vergeht. So habe ich doch glatt vergessen, meinen letzten Artikel in der visual studio one anzukündigen. Das Thema: Techniken miteinander verbinden.

Einzelne Technologien und Techniken sind oft schnell verinnerlicht und werden anfänglich für kleinere Projekte eingesetzt. Meist jedoch getrennt voneinander, um etwaige Fehlerquellen oder ›Blocker‹ zu minimieren. Dieser Artikel zeigt, wie LINQ mit der Windows Presentation Foundation zusammen spielt.

Der gesamte Artikel ist in der Ausgabe 02/08 der Zeitschrift visual studio one zu finden.

Eine Liste der von mir verfassten Artikel ist in der Rubrik Zeitschriften-Artikel zu finden.
  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Mitschnitte vom Microsoft Launch Event

16.03.08 - .NET, Grundlagen, Base Framework, WPF, ASP.NET, Silverlight, Mobile Devices, Datenverwaltung, Visual Studio, Allerlei, SQL Server
Beitrag von Norbert Eder
 Unter dem nachfolgenden Link sind einige Livemitschnitte vom Lauchevent 2008 in Frankfurt zu finden. Zum Ansehen wird das Silverlight-Plugin benötigt.

Hier der Link: Mitschnitte Launch 2008

Und hier die Themen:

- Keynote
- Neu in Visual Studio 2008
- SQL Server 2008
- Überblick Windows Server 2008 Management
- Internet Information Server 7
- Virtualisieren mit dem Windows Server

Viel Spass beim Gucken.
  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Undo und Redo implementieren

12.03.08 - Entwicklung, Diskussionen, Patterns, .NET, Grundlagen, Base Framework, WPF, ASP.NET
Beitrag von Norbert Eder
 Es kommt ja doch häufig vor, dass eine Anwendung ein Undo bzw. ein Redo unterstützen muss. Wird diese Funktionalität gefordert wird immer wieder nach einer Umsetzungsmöglichkeit gefragt. Hier eine kleine Lösung, die für diese Zwecke verwendet werden kann.

Hinweis: In diesem Beispiel werden keine Fehlerabfragen gemacht. Es empfiehlt sich, einen Ausführungsstatus zu integrieren, der das Verhalten entsprechend beeinflusst.

Design


Um diese Funktionalität umzusetzen, wird das Command-Pattern verwendet. Dieses eignet sich sehr gut dazu, Aktionen auszuführen und diese auch wieder zurück zu nehmen. Erweitert wird das Command-Pattern um einen CommandManager, der für die Ausführung und Verwaltung der einzelnen Commands zuständig ist.



Implementierung


Zuerst wird eine Basisklasse für den Command implementiert, welche die Methoden Execute(), Undo und Redo zur Verfügung stellt.

public abstract class Command
{
    public abstract void Execute();

    public abstract void Undo();

    public abstract void Redo();
}


Als weiteren Schritt wird der CommandManager implementiert. Dieser ist in diesem Fall ein Singleton (damit er nur einmal pro Instanz vorhanden sein kann) und bietet nach aussen hin die selbe Funktionalität wie ein Command an. Intern werden die einzelnen Commands jedoch in einem Stack behalten, um den Verlauf der einzelnen Commands nachvollziehen zu können. Darüber kann nun ein Undo bzw. ein Redo abgebildet werden.

public class CommandManager
{
    #region Static Attributes

    private static object _lockObject = new object();
    private static CommandManager _instance = null;

    #endregion Static Attributes

    #region Attributes

    private Stack<Command> _commandStack = new Stack<Command>();
    private Stack<Command> _undoneStack = new Stack<Command>();

    #endregion Attributes

    #region ctor

    private CommandManager() { }

    #endregion ctor

    #region Static Methods

    public static CommandManager GetInstance
    {
        get
        {
            if (_instance == null)
            {
                lock (_lockObject)
                {
                    _instance = new CommandManager();
                }
            }
            return _instance;
        }
    }

    #endregion Static Methods

    #region Public Methods

    public void Execute(Command command)
    {
        command.Execute();
        _commandStack.Push(command);
    }

    public void Redo()
    {
        Command redoCommand = _undoneStack.Pop();
        redoCommand.Redo();
        _commandStack.Push(redoCommand);
    }

    public void Undo()
    {
        Command undoCommand = _commandStack.Pop();
        undoCommand.Undo();
        _undoneStack.Push(undoCommand);
    }

    #endregion Public Methods
}


Damit ist die eigentliche Implementierung bereits abgeschlossen und wir können zu einem ersten Test schreiten. Hierzu benötigen wir ein paar kleinere Klassen, die nicht wirklich etwas aufregendes machen.

Hier eine Klasse Person, die zwei Eigenschaften zur Verfügung stellt, anhand derer getestet wird. Zudem implementiert sie das Interface ICloneable (wird im nachfolgenden Command benutzt) und überschreibt die ToString-Methode.

public class Person : ICloneable
{
    #region Properties

    public String Firstname { get; set; }
    public String Lastname { get; set; }

    #endregion Properties

    #region ICloneable Members

    public object Clone()
    {
        Person p = new Person();
        p.Firstname = this.Firstname;
        p.Lastname = this.Lastname;
        return p;
    }

    #endregion ICloneable Members

    #region Overrides

    public override string ToString()
    {
        return String.Format("{0}, {1}", Lastname, Firstname);
    }

    #endregion Overrides
}


Als nächsten Schritt müssen wir noch einen konkreten Command implementieren, der eine Aktion durchführt. Dieser wird sich lediglich ein wenig mit den Eigenschaften der Person spielen:

public class PersonCommand : Command
{
    private Person _person;
    private Person _personStateBefore;

    public PersonCommand(Person person)
    {
        _person = person;
    }

    public override void Execute()
    {
        _personStateBefore = _person.Clone() as Person;

        _person.Firstname = "Norbert";
        _person.Lastname = "Eder";
    }

    public override void Undo()
    {
        _person.Firstname = _personStateBefore.Firstname;
        _person.Lastname = _personStateBefore.Lastname;
    }

    public override void Redo()
    {
        Execute();
    }
}


Wichtig ist an dieser Stelle nur, dass sich der Command den ursprünglichen Status des übergebenen Objektes merkt und somit eine Undo bzw. Redo-Funktionalität anbieten kann.

Schlussendlich ein Stück Code, welches bei mir in einer Konsolen-Anwendung direkt in der static void Main ausgeführt wird:

CommandManager manager = CommandManager.GetInstance;

Person p = new Person();
p.Firstname = "<not set>";
p.Lastname = "<not set>";
Console.WriteLine(
    String.Format("Before first execution: " + p.ToString()));

manager.Execute(new PersonCommand(p));
Console.WriteLine(
    String.Format("After first execution : " + p.ToString()));

manager.Undo();
Console.WriteLine(
    String.Format("After undo            : " + p.ToString()));

manager.Redo();
Console.WriteLine(
    String.Format("After redo            : " + p.ToString()));

Console.Read();


Und nun möchte ich das Ergebnis nicht vorenthalten:



Fazit


Wie zu sehen ist, ist ein Undo bzw. Redo-Mechanismus sehr einfach zu implementieren. Abhängig der Umgebung in welcher diese Methode zu tragen kommt, müssen eventuell weitere Mechanismen eingefügt werden.
  9 Kommentare - 2602 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Februar 2008 im Rückblick

03.03.08 - .NET, Grundlagen, Base Framework, WPF, ASP.NET, Silverlight, Datenverwaltung, Visual Studio, Allerlei
Beitrag von Norbert Eder
 Mensch, die Zeit vergeht. Schon wieder ist ein Monat um und dieses Mal bin ich ja wirklich kaum zu neuen Einträgen gekommen. Das lag wohl an der vielen Arbeit die unbedingt erledigt werden wollte. Dennoch hier eine kleine Übersicht der wichtigsten Beiträge:

Design Patterns


Verhalten einer Anwendung per Konfiguration bzw. Laufzeit verändern

Windows Presentation Foundation


Über die Wichtigkeit der Windows Presentation Foundation
Image aus einem WPF-Control erzeugen

Windows Forms


PropertyGrid löst Event PropertyValueChanged bei Collections nicht aus

Sonstiges


Service-Oase Österreich - Internetprovider unleashed

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


Image aus einem WPF-Control erzeugen

07.02.08 - .NET, Base Framework, WPF
Beitrag von Norbert Eder
 Früher oder später stößt jeder WPF-Entwickler auf diese Aufgabe: Aus unterschiedlichsten Gründen muss von einem dargestellten Control eine Grafik erzeugt werden. Wer bereits mit Windows Forms gearbeitet hat, versucht nun vergeblich den Weg über CreateGraphics. Dieser Ansatz funktioniert unter der Windows Presentation Foundation nicht. Stattdessen muss ein anderer Kniff angewandt werden.
Im ersten Schritt kann mittels RenderTargetBitmap ein Bitmap mit der gewünschten Größe erstellt werden. Darin hinein wird nun das entsprechende Control gerendert. Nun müssen wir uns eines Kniffes behelfen (da eventuell die Größe nicht gänzlich korrekt ist). Dazu erstellen wir ein Image-Control, welches das Bitmap als Source erhält und aktualisieren die Größe des Elements entsprechend des Inhaltes mittels der Methoden Measure und Arrange. Nun wird das Image mit der korrekten Größe erneut gerendert und beispielsweise einem Objekt vom Typ PngBitmapEncoder übergeben. Nun kann das Image in einen Stream geschrieben und in ein System.Drawing.Image umgewandelt werden. Hier nun ein Beispielcode:
public System.Drawing.Image Convert(
  FrameworkElement controlToRender)
{
    RenderTargetBitmap rtb = new RenderTargetBitmap(
      (int)controlToRender.ActualWidth, 
      (int)controlToRender.ActualHeight, 
      90, 
      90, 
      PixelFormats.Default);
    
    Visual vis = (Visual)controlToRender;
    rtb.Render(vis);
    
    System.Windows.Controls.Image img = 
      new System.Windows.Controls.Image();
    img.Source = rtb;
    img.Stretch = Stretch.None;
    img.Measure(new System.Windows.Size(
      (int)controlToRender.ActualWidth, 
      (int)controlToRender.ActualHeight));
    System.Windows.Size sizeImage = img.DesiredSize;
    img.Arrange(new System.Windows.Rect(new 
      System.Windows.Point(0, 0), sizeImage));
    
    RenderTargetBitmap rtb2 = new RenderTargetBitmap(
      (int)rtb.Width, 
      (int)rtb.Height, 
      90, 
      90, 
      PixelFormats.Default);
    rtb2.Render(img);
    
    PngBitmapEncoder png = new PngBitmapEncoder();
    png.Frames.Add(BitmapFrame.Create(rtb2));
    
    Stream ms = new MemoryStream();
    png.Save(ms);
    
    ms.Position = 0;
    
    System.Drawing.Image retImg = 
      System.Drawing.Image.FromStream(ms);
    return retImg;
}

Zum PngBitmapEncoder: Hier stehen weitere Encoder bereit. Zusätzliche Informationen sind auf der MSDN-Seite zum Thema BitmapEncoder zu finden. Der Vollständigkeit halber hier die Liste der zur Verfügung stehenden Encoder:
  • BmpBitmapEncoder
  • GifBitmapEncoder
  • JpegBitmapEncoder
  • PngBitmapEncodery
  • TiffBitmapEncoder
  • WmpBitmapEncoder

  2 Kommentare - 3758 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


PropertyGrid löst Event PropertyValueChanged bei Collections nicht aus

07.02.08 - .NET, Grundlagen, Base Framework, WPF
Beitrag von Norbert Eder
 Das PropertyGrid besitzt ein Event namens PropertyValueChanged. Dieses wird ausgelöst, wenn eine Eigenschaft editiert wird. Dies funktioniert soweit auch gut, außer die Eigenschaft wird durch eine Collection dargestellt. In diesem Falle wird nichts ausgelöst.

Hintergrund


Das Problem liegt darin, dass sich die Collection selbst nicht ändert, wenn sich in einem der Werte eines Child-Objektes ein Wert ändert. Ebenfalls ändert sich die Collection nach außen hin nicht, wenn neue Elemente hinzugefügt werden. Sehen wir uns die Implementierung auf Seiten des PropertyGrids an.
Das PropertyGrid besitzt eine Auflistung GridEntryCollection. Diese beinhält GridEntry-Objekte, welche die einzelnen Properties darstellen. GridEntry besitzt nun eine Methode EditPropertyValue. Diese überprüft, ob sich der Wert verändert hat und setzt darauf hin ein Commit ab, oder nicht (wenn keine Änderung). Im Commit wird anschließend der besagte Event ausgelöst. Hier nun der relevante Teil aus der Methode:
object propertyValue = this.PropertyValue;
object obj3 = this.UITypeEditor.EditValue
    (this, this, propertyValue);
if (!this.Disposed)
{
    if ((obj3 != propertyValue) 
        && this.IsValueEditable)
    {
        iva.CommitValue(this, obj3);
    }
    if (this.InternalExpanded)
    {
        PropertyGridView.GridPositionData data = 
          this.GridEntryHost.CaptureGridPositionData();
        this.InternalExpanded = false;
        this.RecreateChildren();
        data.Restore(this.GridEntryHost);
    }
    else
    {
        this.RecreateChildren();
    }
}

Hier ist nun schön zu erkennen, dass lediglich die Objekte miteinander verglichen werden, was bei einer Collection natürlich grundsätzlich keine Änderung entdecken lässt. Dadurch muss man sich mit einem Workaround behelfen.

Mögliche Workarounds


Der erste Workaround ist vermutlich der, der mit dem geringsten Aufwand verbunden ist: Es gibt einige 3rd Party Produkte, welche diesen „Fehler“ erkannt und ausgemerzt haben. Damit muss selbst keine Implementierung vorgenommen werden, jedoch sind die meisten dieser Produkte kommerzieller Natur und kosten somit einige Euros.
Eine weitere Möglichkeit ist, selbst Hand anzulegen und in die Tasten zu klopfen. Dazu muss man sich nun einen grundsätzlichen Gedanken machen. Wie kann erreicht werden, dass der Objekt-Vergleich in oben gezeigter Methode erkennt, dass das Objekt geändert wurde? Korrekt, wir müssen die Collection klonen. Das heißt, die Objekte der Collection und auch die Collection selbst müssen das Interface ICloneable implementieren.
Die Collections selbst werden im PropertyGrid in einem CollectionEditor angezeigt. Dieser ist standardmäßig für alle Collections der gleiche. Diesen müssen wir nun für unsere Collection-Eigenschaft austauschen, indem wir eine eigene Klasse erstellen, die von UITypeEditor ableitet:
public class CustomCollectionEditor : UITypeEditor
{
    private CollectionEditor _editor = 
      new CollectionEditor(typeof(CustomCollection));
    
    public override object EditValue(
      ITypeDescriptorContext context, 
      IServiceProvider provider, 
      object value)
    {
        if (value != null)
        {
            value = this._editor.EditValue(context, provider, value);
            CustomCollection list = (CustomCollection)value;
            return list.Clone();
        }

        return base.EditValue(context, provider, value);
    }

    public override UITypeEditorEditStyle GetEditStyle(
      ITypeDescriptorContext context)
    {
        return this._editor.GetEditStyle(context);
    } 
}

Im Grunde passiert hier wenig Spannendes. Es wird lediglich bei einem Edit die Collection geklont und die neue Collection zurück geliefert. Dadurch erkennt nun das PropertyGrid eine Änderung und aktiviert ihrerseits das Event PropertyValueChanged. Daraufhin ist es nun möglich festzustellen, dass a) in der Collection eine Änderung passiert sein muss und b) können wir feststellen wo diese passiert ist.
Schlussendlich muss nun noch der jeweiligen Eigenschaft zugewiesen werden, dass diese mit dem eigenen CustomCollectionEditor zu öffnen ist. Dazu sind folgende Attribute bei der entsprechenden Property zu setzen:
private CustomCollection _collection = new CustomCollection();

[TypeConverter(typeof(CustomCollection))]
[Editor(typeof(CustomCollectionEditor), typeof(UITypeEditor))]
public CustomCollection MyCollection
{
  get { return this._collection; }
  set { this._collection = value; }
}

Hierzu gibt es noch einen kleinen Tipp: Es ist darauf zu achten, dass auch der Setter tatsächlich vorhanden ist. Im GridEntry wird abgefragt, ob das Object editierbar ist (IsValueEditable). Wäre nur ein Getter vorhanden, wäre IsValueEditable auf false und der Wert würde nicht committed warden, wodurch auch das Event PropertyValueChanged nicht ausgelöst werden würde.

Fazit


Schön wäre es natürlich, würde das PropertyGrid diese Funktionalität anbieten. Da das nicht der Fall ist, muss man selbst etwas nachhelfen. Die oben gezeigte Variante kommt dem gewünschten schon recht nahe, auch wenn der besprochene Event erst angetriggert wird, nachdem der CollectionEditor geschlossen wird. Zwar in manchen Fällen (Eingabevalidierung) nicht erwünscht, lässt sich aber so einfach nicht beheben. Dennoch bietet diese Variante eine einfache und gute Möglichkeit, das gewünschte Ziel zu erreichen.
  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Jänner 2008 im Rückblick

04.02.08 - .NET, Grundlagen, Base Framework, WPF, ASP.NET, Silverlight, Mobile Devices, Datenverwaltung, Visual Studio, Allerlei
Beitrag von Norbert Eder
 Kommt es nur mir so vor, oder vergeht die Zeit wirklich so schnell? Jedenfalls ist wieder ein Monat um und somit hier ein kurzer Rückblick, was sich auf diesem Blog im Jänner so getan hat.

.NET Base Framework


Mit List.ForEach durch Listen iterieren
String.IsNullOrEmpty als Extension Method

Windows Presentation Foundation


Globales Exception-Handling in WPF
WPF: x:Code Element - eine Diskussion
Animationen mit WPF anhand einer kleinen Foto Gallery
WPF: Validierung von Eingaben

Visual Studio


Visual Studio Templates erstellen

Zu guter Letzt sei noch erwähnt, dass die neue Webpräsenz des .NET BlogBooks online gegangen ist: http://www.dotnet-blogbook.com
  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Mit List.ForEach durch Listen iterieren

31.01.08 - .NET, Grundlagen, Base Framework
Beitrag von Norbert Eder
 Die meisten Leser werden wohl for und foreach kennen, um durch Listen zu iterieren. Es gibt jedoch bei den generischen Listen (auch Arrays) eine Methode ForEach.

Damit ist es möglich, eine Aktion auf jedes Element der Liste auszuführen. Nachfolgend ein Beispiel welches mit einer anonymen Methode arbeitet und die Methode ForEach verwendet:
List<int> testList = new List<int>();

for (int i = 0; i < 100; i++)
    testList.Add(i);

int sum = 0;

testList.ForEach(delegate(int val)
{
    sum += val;
});

Console.WriteLine(sum.ToString());

Der interessierte Leser fragt sich nun an dieser Stelle, wie es mit der Performance der einzelnen Möglichkeiten aussieht, woraus sich auch ergibt, welche Variante eingesetzt werden sollte (nicht ganz, denn in Bereichen an denen Performance nicht ganz so wichtig ist, entscheiden auch andere Aspekte).

Hierzu kann ich auf den Eintrag Performance of foreach vs. List.ForEach von Dustin Campbell verweisen. Diesbezügliche Fragen sollten damit aus dem Weg geräumt werden.
  2 Kommentare - 1568 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


String.IsNullOrEmpty als Extension Method

29.01.08 - .NET, Grundlagen, Base Framework
Beitrag von Norbert Eder
 Über Extension Methods hatte ich bereits berichtet. Einfach können hier beliebige Typen um Methoden erweitert werden.

Nun gibt es die statische Methode String.IsNullOrEmpty um zu überprüfen, ob ein String einen Wert enthält oder nicht.

Thomas Freudenberg hat hierzu ein kleines Beispiel erweitert, wie diese statische Methode als Extension Method genutzt werden kann und daher bei öfterer Verwendung doch einiges an Zeit sparen kann. Nette Idee.
  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL



Zurück Weiter