Auf den Spuren von .NET

Hier erscheinen automatisch die letzten vier Einträge meines Blogs "Auf den Spuren von .NET".

Unter Artikel auf dieser Webseite finden Sie eine kategorisierte Auswahl von Einträgen aus meinem Blog.


Zu einem Eintrag springen:





CSS: Vertikaler Scrollbalken erzwingen

Mittwoch, 16. Oktober 2013 19:17

Bei Webseiten die nicht über die Höhe des Browsers (Viewports) hinausgehen, also relativ wenig Inhalt haben, gibt es beim Wechsel zu einer Seite die darüber hinausgeht ein horizontales verschieben der Seite.

Das Problem entsteht, weil z.B. Firefox / Chrome den vertikalen Scrollbalken bei nicht ausreichendem vertikalem Inhalt weglassen.
Der Internetexplorer hat dieses Problem nicht und zeigt das meines Erachtens korrekte Verhalten: Er zeigt den vertikalen Scrollbalken immer an.

Um dieses korrektere Verhalten in allen Browsern zu erzwingen gibt es verschiedene Lösungen.

Die nicht valide Lösung, die inzwischen in jedem aktuellen Browser laufen sollte:

html {
    overflow-y: scroll;
}

 

 

Oder die valide Lösung, die auch in älteren Browsern läuft, jedoch durch das 1% erzwungene mehr an Höhe, diese etwa 10 Pixel unten auch an die sonst leere Seite anhängt:

html {
    height: 100%;
}
body {
    height: 101%;
}

 

 

Alternativ dazu sollte auch folgendes funktionieren:

html {
    height: 100%;
    margin-top: 1px;
}

Habt ihr andere Lösungen oder Kommentare zu diesen hier? Schreibt in die Kommentare.

Tags: Firefox, CSS, IE, Scrollbalken





IE: neu geöffnetes Tab bleibt leer, obwohl Einstellung geändert

Montag, 17. Juni 2013 19:24

Darauf muss man zuerst kommen: Die Toolbar des Antiviren Programms "Free AVG" verpfuscht die Internet Explorer Einstellungen.

Das wirkt sich so aus, dass nach jedem Neustart und obwohl schön neu eingestellt, beim öffnen eines neuen Tabs nur eine leere Seite kommt.
Und nicht wie gewünscht die erste Startseite als neues Tab.

Der Übeltäter kann entweder auf die angenehme oder auf die harte Tour entfernt werden.
Nachdem entfernen einmal das gewünschte einstellen und es bleibt auch.

Quelle: http://answers.microsoft.com/en-us/ie/forum/ie9-windows_vista/new-tab-page-on-internet-explorer-keeps-changing/6dd63d7a-1181-4ffb-a256-225ba5a528e1

Tags: Antivirus, Tab, Startseite, IE, Toolbar, AVG





LightCore 1.5.1 Release

Samstag, 9. Februar 2013 01:45

Es wurde soeben die Version 1.5.1 von LightCore veröffentlicht.

LightCore ist auf Constructor Injection optimiert. In der Version 1.5 hat sich leider ein Fehler eingeschlichen.
Die PropertyInjection wurde bei jeder Typenauflösung ausgeführt. Dies führte zu einer StackOverlow-Exception wenn eine zirkuläre Abhängigkeit, z.B. View.Presenter / Presenter.View vorhanden ist.

Da LightCore zirkuläre Abhängigkeiten nicht unterstützt wird, wurde die automatische PropertyInjection entfernt. So sollten keine Fehler mehr auftreten und das PropertyInjection ist optional.

Das aktuelle Release findet sich unter http://lightcore.ch/

Ich entschuldige mich für die Unanehmlichkeiten und wünsche viel Erfolg.

Tags: .NET, Release, LightCore:, DepdendencyInjection





Gastbeitrag: MVP oder MVVM? Was gehoert wohin?

Mittwoch, 6. Februar 2013 20:14

Einleitung

Ein Kollege von mir hat sich intensiv mit den Designpatterns MVP und MVVM auseinandergesetzt und unter anderem auch mit mir diskutiert.
Da ich seine Gedanken sehr interessant fand, kam uns schlussendlich die Idee, diese als Gastbeitrag in meinem Blog zu veröffentlichen.
Der Hauptgedanke dieser Veröffentlichung ist Entwirrung und das Einholen von Feedback zu diesem Thema aus meiner Leserschaft.

In diesem Sinne wünsche ich euch viel Spass mit dem Gastbeitrag und wir freuen uns auf reichliches Feedback.

Gastbeitrag

Die Designpatterns MVP (Model - View - Presenter) und MVVM (Model - View - ViewModel) sind Designpatterns für die Strukturierung und Trennung von Daten, Ansicht und Presentationslogik.
Ich habe mich im Zusammenhang mit der Entwicklung eines Applikationsframework für Businessanwendungen damit beschäftigt und schaute dazu im Internet verschiedene Implementierungen und Blogeinträge an. Mein Verständnis hatte sich dabei nicht vergrössert, sondern erstmal mehr Verwirrung geschaffen:

  • Was ist der Unterschied zwischen einem Model und einem Entity?
  • Was ist ein Presenter?
  • Was ist Presentationslogik?
  • Was ist der Unterschied zwischen Presenter und ViewModel?

Ich versuche möglichst knapp meine Ansicht mitzuteilen und auch Beispiel-Code anzubieten. Im folgenden Beispiel verwende ich das MVP-Pattern:

Entity / Model:

Ein Entity enthält Daten wie Name, Adresse und Ort usw. z.B. eines Customers:

 


public class CustomerEntity
    {
        public int CustomerId { get; set; }

        public string Name { get; set; }

        public string Address { get; set; }

        public string Location { get; set; }
    }

 

Ein Model enthält wiederum ein oder mehrere Entities (z.B. ein zu editierender Customer oder ein Suchergebnis).

Das Model stellt die dazugehörenden Methoden, um Entities zu laden, zu suchen oder abzuspeichern zur Verfügung.
Zusätzlich darf im Model der Status des Views gespeichert werden, z.B. IsCustomerSelected, um gebunden mit der Eigenschaft IsEnabled eines Button zu unterscheiden, ob ein Customer im ListView der Ansicht selektiert wurde und somit editiert werden kann oder nicht.
Eigenschaften wie die eines Customers sollten meiner Meinung nach nicht im Model zur Verfügung gestellt werden, sondern getrennt in einem separaten Entity, obwohl dies in den meisten Artikel bezüglich MVP / MVVM genau anders dargestellt wird.
Im MVVM-Pattern wird von Microsoft empfohlen, die Daten sogar im ViewModel (Presenter) einzufügen; diese Mischung zwischen einerseits Daten und Presentationslogik steht die Möglichkeit entgegen, ableitbare Model-BasisKlassen für Daten und Basisklassen für Presentationslogik zu entwickeln.

In der Basisklasse für Modelle sollte INotifyPropertyChanged implementiert werden, damit die Ansicht, falls mit DataBinding gearbeitet, aktualisiert wird.

 


public class Model : INotifyPropertyChanged
    {
        public void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

public class EditCustomerModel : Model
    {
        private CustomerEntity _customer;

        public void NewCustomer()
        {
            this.Customer = new CustomerEntity();
        }

        public void EditCustomer(int customerId)
        {
            // Code, um Customer Entity zu laden
            // this.Customer = ...
        }

        public CustomerEntity Customer
        {
            get { return _customer; }
            set
            {
                _customer = value;

                this.OnPropertyChanged("Customer");
            }
        }
    }

public class SearchCustomerModel : Model
    {
        private IEnumerable<CustomerEntity> _customers =
new List<CustomerEntity>();

        private CustomerEntity _selectedCustomer = null;

        public CustomerEntity SelectedCustomer
        {
            get { return _selectedCustomer; }
            set
            {
                _selectedCustomer = value;

                // Aktualisiert DataBindings für SelectedCustomer
                this.OnPropertyChanged("SelectedCustomer");

                // Aktualisiert die IsEnabled Eigenschaft des Buttons
                this.OnPropertyChanged("IsCustomerSelected");
            }
        }

        public IEnumerable<CustomerEntity> Customers
        {
            get { return _customers; }
            set
            {
                _customers = value;

                // OnPropertyChanged aktiviert das updaten der Liste in der Ansicht, falls DataBinding verwendet wurde
                this.OnPropertyChanged("Customers");
            }
        }

        public void LoadAllCustomers()
        {
            // Code, um alle CustomerEntities zu laden
            // this.Customers = ...
        }

        public void SearchCustomers(string text)
        {
            // Code, um CustomerEntities zu laden
        }

        public bool IsCustomerSelected
        {
            get
            {
                return this.SelectedCustomer != null ? true : false;
            }
        }
    }

 

 

View:

Die Views hier als Code bereitzustellen, sprengt den Rahmen dieses Artikels. Ich bitte hierzu die Vorstellungskraft des Lesers zu bemühen, um sich die Views für EditCustomerView und SearchCustomerView vorzustellen.
Das Databinding für ListView und SearchCustomerModel.Customers, sowie den Edit Button mit IsCustomerSelected zu verknüpfen, können wir uns vorstellen.
Im View ist eine Referenz auf den Presenter zu setzen, um auf die Methoden des Presenters zugreifen zu können.

 


public partial class SearchCustomerView : Page
    {
        private SearchCustomerPresenter Presenter { get; set; }

        public SearchCustomerView()
        {
            InitializeComponent();

            // Neue Presenter Instanz
            this.Presenter = new SearchCustomerPresenter();

            // Model binden mit DataContext des View für DataBinding
            this.DataContext = this.Presenter.Model;

        }

        private void searchButton_Click_1(object sender, RoutedEventArgs e)
        {
            this.Presenter.SearchCustomers(this.searchTextBox.Text);
        }

        private void loadAllCustomers_Click_1(object sender, RoutedEventArgs e)
        {
            this.Presenter.LoadAllCustomers();
        }
    }

 

Presenter / ViewModel:

Persönlich finde ich die Namensgebung Presenter besser als ViewModel; so kann besser zwischen Model-, View- und Presenterklassen unterschieden werden.
Im Presenter ist die Presentationslogik. Der Presenter delegiert meistens nur vom View zum Model.
Microsoft empfiehlt bezüglich MVVM das ViewModel (Presenter) als DataContext im View zu setzen - dies im Zusammenhang mit Commands, die sich im Presenter befinden und mit Steuerelementen des Views gebunden werden; in meinem Beispiel setze ich als DataContext das Model.

Ich meine, dass das MVVM Schema im Sinne von Microsoft hinterfragt werden muss, denn Microsoft trennt im MVVM-Schema nicht klar zwischen Model und Presenter oder besser ausgedrückt zwischen Daten und Presentation. Das Model klar vom ViewModel (Presenter) zu trennen, ist ja aber gerade die ursprüngliche Idee dieser Pattern MVP und MVVM.

Meiner Ansicht nach gehören Daten ins Model und nicht in die Presentationslogik: deshalb setze ich das Model als DataContext im View und nicht das ViewModel (Presenter).

In meinem Beispiel habe ich eine einfache Implementierung verwendet, ohne Kommunikation zwischen Presenter und View:

 


public class EditCustomerPresenter
    {
        public EditCustomerPresenter()
        {
            this.Model = new EditCustomerModel();
        }

        public void NewCustomer()
        {
            this.Model.NewCustomer();
        }

        public void EditCustomer(int customerId)
        {
            this.Model.EditCustomer(customerId);

            // Falls ohne DataBinding per xaml:
            // UpdateView();
        }

        public EditCustomerModel Model { get; set; }
    }

public class SearchCustomerPresenter
    {
        public SearchCustomerPresenter()
        {
            this.Model = new SearchCustomerModel();
        }

        public SearchCustomerModel Model { get; set; }

        public void SearchCustomers(string text)
        {
            this.Model.SearchCustomers(text);
        }

        public void LoadAllCustomers()
        {
            this.Model.LoadAllCustomers();
        }
    }

 

Um nun an einem ganz kleinen Beispiel zu veranschaulichen, was Presentationslogik ist, erweitere ich die SearchCustomers Methode etwas:

 


public void SearchCustomers(string text)
        {
            this.Model.SearchCustomers(text);
               
MessageBox.Show(String.Format("{0} Kunde(n) gefunden.", this.Model.Customers.Count()));
           
        }

 

Wo sonst sollte die MessageBox die Anzahl der Ergebnisse anzeigen, wenn nicht in der Presentationslogik des Presenters? Diese Info soll natürlich nur die Presenationslogik veranschaulichen; solche unnötigen Infos sollten in Businessapplikationen vermieden werden.

Auch erfahrene Entwickler brauchen etwas Zeit, wenn sie sich zum ersten Mal mit MVP und MVVM beschäftigen, um diese Patterns zu verstehen. Das beste Verständnis habe ich persönlich beim Implementieren eigener MVPs gewonnen und kann das nur weiterempfehlen.

Hier eine fragwürdige Implementierung des MVVM:

http://msdn.microsoft.com/de-de/magazine/dd419663.aspx

Hierzu einige Äusserungen:
Erstmal vorne weg: In diesem Artikel scheint ein Model gar nicht erst vorzukommen; die Daten wurden in das ViewModel, bzw. in den Presenter, also in die Presentationlogik hineingesetzt. Deshalb wurde dort auch die Schnittstelle INotifyPropertyChanged implementiert, die klar ins Model gehört.


Fazit:
 
Der MVP Pattern trennt klar Model und Presenter; der MVVM Pattern scheint diese Trennung nicht mehr anzustreben. Auch wenn wir uns vorstellen, Models zu entwickeln, die mit Hilfe von Generics standardmässig Eigenschaften zur Verfügung stellen, scheint mir die Trennung zwischen Model und Presenter im Sinne der Klassenhierarchie wichtig; So würden wir wohl Basisklassen einerseits für Daten-Modelle und andererseits für Presentationslogik erstellen und kaum beides in eine ViewModel-Klasse zwängen.
Wichtig ist für mich auch die Trennung zwischen Entity und Model.
Der MVVM Pattern scheint einige sinnvolle Eigenheiten des MVP Pattern über Bord zu schmeissen (z.B. klare Trennung zwischen Model und Presenter). Die aufwendige Verwendung von Commands sehe ich nicht als existentielle Erweiterung beim MVVM; ich sehe die MVP Implementierung als einfacher und klarer an.


Stefan Werdenberg, IntegralFramework

Tags: Artikel, MVP, Design Patterns, MVVM, Gastbeitrag, IntegralFramework