Skip to content
Aug 19 10

WPF – MVVM in Aktion – Teil 3 – Die Commands

by ckruczek

Im 2. Teil meiner drei-teiligen Reihe über das MVVM Pattern hab ich euch die Komponenten und deren Zusammenspiel vorgestellt.
Nun möchte ich dazu übergehen wie das ViewModel mittels Commands mit der Oberfläche kommuniziert. Die WPF bietet dafür das ICommand Interface an, welche aus folgenden Komponenten besteht.

public interface ICommand
    {

        event EventHandler CanExecuteChanged;
        bool CanExecute(object parameter);
        void Execute(object parameter);
    }

Jedes Command muss nun diese Methoden implementieren und in der Execute-Methode findet dann die Magie statt, die das Command darstellen soll.
Ich habe in meinem Projekt eine kleine Simulation gewählt die beim Klick auf den Button “Simulation Starten” ausgeführt werden soll. Vorher möchte ich euch jedoch die konkrete Implementation meines Commands demonstrieren, es ist nicht meinen Gehirnwindungen entsprungen sondern basiert auf dem Standard-RelayCommand von Josh Smith seinem MVVM-Artikel.

public class RelayCommand : ICommand
    {
       

        #region Events

        public event EventHandler CanExecuteChanged;

        #endregion

        #region Fields

        readonly Action<object> execute;
        readonly Predicate<object> canExecute;
       

        #endregion // Fields

        #region Constructors

        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            this.execute = execute;
            this.canExecute = canExecute;
        }

        #endregion

        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            return canExecute == null ? true : canExecute(parameter);
        }

        public void RaiseCanExecuteChanged()
        {
            if (CanExecuteChanged != null) CanExecuteChanged(this, EventArgs.Empty);
        }

        public void Execute(object parameter)
        {
            execute(parameter);
        }

        #endregion
    }

Diese RelayCommand nutze ich jetzt in meiner PersonVMRepository-Klasse um meine Simulation zu erstellen.

public class PersonRepositoryVM : ViewModelBase,IDisposable
    {
        #region Properties

        private Timer timer;
        private Random randomPersonVM = new Random();
       
        private ObservableCollection<PersonVM> _personVmCollection;
        public ObservableCollection<PersonVM> Personen
        {
            get
            {
                return this._personVmCollection;
            }
            private set
            {
                if (this._personVmCollection != value)
                {
                    this._personVmCollection = value;
                    this.OnPropertyChanged(() => this.Personen);
                }
            }
        }

        private PersonVM _currentPerson;
        public PersonVM CurrentPerson
        {
            get
            {
                return this._currentPerson;
            }
            set
            {
                if (this._currentPerson != value)
                {
                    this._currentPerson = value;
                    this.OnPropertyChanged(() => this.CurrentPerson);
                }
            }
        }

        private RelayCommand timerCommand;
        public ICommand SimulationCommand
        {
            get
            {
                if (this.timerCommand == null)
                    this.timerCommand = new RelayCommand(a => this.timer.Start());
                return this.timerCommand;
            }
        }
             

        #endregion

        public PersonRepositoryVM(IEnumerable<PersonVM> personViewModels)
        {
            if (personViewModels == null)
                throw new ArgumentNullException("personViewModels");

            this._personVmCollection = new ObservableCollection<PersonVM>(personViewModels);

            if (this._personVmCollection.Count != 0)
                this.CurrentPerson = this._personVmCollection.FirstOrDefault();

            this.timer = new Timer(1000);
            this.timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
           

        }

        private void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
           
            this.CurrentPerson = this.Personen[this.randomPersonVM.Next(0, this.Personen.Count)];
            this.CurrentPerson.IsOnline = !this.CurrentPerson.IsOnline;
        }

        #region IDisposable Member

        public void Dispose()
        {
            this.timer.Dispose();
        }

        #endregion
    }

Wichtig ist hier das SimulationCommand. Diese Command-Objekt stellt unsere Simulation dar. An dieser Stelle wird nichts weiter gemacht als ein neues RelayCommand Objekt mit dem Aufruf der Timer.Start() Funktion zu erstellen. Dieses Command können wir nun an die GUI binden. Das geschieht wie folgt:

<Button x:Name="btn_startSimulation"
               Grid.Row="1"
               HorizontalAlignment="Left"
               VerticalAlignment="Top"
               Content="Simulation Starten"
               Margin="10"
               Command="{Binding SimulationCommand}"/>

Sobald nun der Button gedrückt wird, wird der Timer gestartet und die Execute-Methode des Commands wird ausgeführt womit die CurrentPerson einen neuen Online-Status bekommt. Ohne das ich dafür in der GUI auch nur einmal auf die Listbox zugreifen musste.
Da merkt man schon einen Vorteil von Commands: Man kann Objekte schaffen welche eine Aktion in der GUI darstellen sollen, durch die Bindung der einzelnen Eigenschaften an die GUI wird diese automatisch aktuallisiert ohne das ich in meinem Command die GUI in irgend einer Form kenne.

Anbei möchte ich euch den Quellcode zur Verfügung stellen. Das Editieren einer Person habe ich jetzt aus Zeitgründen rausgenommen, weil ich euch nicht so lange auf den letzten Teil warten lassen wollte.

MvvmDemo.zip

Aug 16 10

WPF – MVVM in Aktion – Teil 2 – Die Komponenten

by ckruczek

Im 1. Teil habe ich euch kurz erklärt wie die MVVM im groben aufgebaut ist. Jetzt ist sicherlich auch interessant zu wissen wie diese Komponenten zusammenspielen. Dafür möchte ich euch zuerst das einfach Model präsentieren:

Model:

public class Person
    {
        private static readonly string[] FIRSTNAMES ={"Bernd","Thomas","Jens",
                                                    "Christopher","Hannes","Dietmar",
                                                    "Hans","Hermes","Sebastian",
                                                    "Emil","Johannes","Erwin"};

        private static readonly string[] LASTNAMES = {"Kruczek","Hansen","Polanski",
                                                   "Mueller","Schulze","Meier",
                                                   "Schneider","Richter","Lange",
                                                   "Neumann","Wagner","Koch"};
        #region Properties

        public bool IsOnline { get; set; }
        public string FirstName { get;  set; }
        public string LastName { get;  set; }

        #endregion

        private Person(bool isOnline,string firstName,string lastName)
        {
            this.IsOnline = isOnline;
            this.FirstName = firstName;
            this.LastName = lastName;
        }

        /// <summary>
        /// Generates sequence of persons by the given count.
        /// </summary>
        /// <param name="countOfPersonsToCreate">Amount of persons to create.</param>
        /// <returns></returns>
        public static IEnumerable<Person> CreatePersonRepository(uint countOfPersonsToCreate)
        {
            Random isOnlineRandom = new Random();
            Random firstNameRandom = new Random();
            Random lastNameRandom = new Random();

            List<Person> personenList = new List<Person>();
            for (int i = 0; i < countOfPersonsToCreate; i++)
            {
                bool isOnline = Convert.ToBoolean(isOnlineRandom.Next(0, 2));
               
                string firstName = Person.FIRSTNAMES[firstNameRandom.Next(0, Person.FIRSTNAMES.Length)];

                string lastName = Person.LASTNAMES[lastNameRandom.Next(0, Person.LASTNAMES.Length)];

                Person person = new Person(isOnline, firstName, lastName);

                yield return person;
            }
           

        }
    }

Ich habe aus Demonstrationszwecken mit Absicht einen privaten Ctor gewählt, denn die Personen sollen nur über die eine statische Funktion erzeugt werden können.
An dem Model ist jetzt noch nichts außergewöhnliches zu erkennen.

GUI:

Das zugehörige XAML dafür sieht wie folgt aus:

<Window x:Class="MvvmDemo.MainWindow"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:local="clr-namespace:MvvmDemo"
       xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
       Title="MainWindow" Height="369" Width="671">
    <Window.Background>
        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
            <GradientStop Color="#FF838383" Offset="0" />
            <GradientStop Color="#FFA3E809" Offset="1" />
        </LinearGradientBrush>
    </Window.Background>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="222*" />
            <RowDefinition Height="108*" />
        </Grid.RowDefinitions>
        <ListBox x:Name="personListBox" ItemTemplate="{StaticResource PersonListItemTemplate}"
                ItemsSource="{Binding Personen}"
                SelectedItem="{Binding CurrentPerson}" Grid.Row="0" />
       
        <Button x:Name="btn_startSimulation"
               Grid.Row="1"
               HorizontalAlignment="Left"
               VerticalAlignment="Top"
               Content="Simulation Starten"
               Margin="10"
               Command="{Binding SimulationCommand}"/>
       
        <Button x:Name="btn_EditPerson"
               Grid.Row="1"
               HorizontalAlignment="Left"
               VerticalAlignment="Center"
               Margin="10"
               Command="{Binding EditCommand}"
               Content="Bearbeiten"
               IsEnabled="{Binding CanEdit}"/>
       
    </Grid>
</Window>

Wie ihr seht ist das nichts weiter als eine Listbox und zwei Buttons. Für die Listbox gibt es noch ein DataTemplate, was ihr hier seht:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                   xmlns:local="clr-namespace:MvvmDemo.Assets.Converter">
   
    <DataTemplate x:Key="PersonListItemTemplate">
        <DataTemplate.Resources>
            <local:StatusToImageConverter x:Key="StatusToImageConverter"/>
        </DataTemplate.Resources>
        <StackPanel Orientation="Horizontal">
            <Image Source="{Binding IsOnline,Converter={StaticResource StatusToImageConverter}}" Stretch="None" Margin="5"/>
            <TextBlock Text="{Binding FirstName}" Margin="2" FontWeight="Bold"/>
            <TextBlock Text="{Binding LastName}" Margin="2" FontWeight="Bold"/>
        </StackPanel>
    </DataTemplate>
   
</ResourceDictionary>

Jetzt kommt das eigentlich wichtigste an der Sache, das ViewModel und die ViewModelBase Klasse.

ViewModelBase:

public abstract class ViewModelBase : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Member

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged<TResult>(Expression<Func<TResult>> propertyExpression)
        {
            if (!this.CheckExpressionForMemberAccess(propertyExpression.Body))
                throw new ArgumentException("propertyExpression",
                        string.Format("The expected expression is no ‘MemberAccess’; its a ‘{0}’",propertyExpression.Body.NodeType));

            if (propertyExpression == null)
                throw new ArgumentNullException("propertyExpression");

            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(this.GetPropertyNameFromExpression(propertyExpression)));
        }

        private bool CheckExpressionForMemberAccess(System.Linq.Expressions.Expression propertyExpression)
        {
            return propertyExpression.NodeType == ExpressionType.MemberAccess;
        }
        private string GetPropertyNameFromExpression<TResult>(System.Linq.Expressions.Expression<Func<TResult>> propertyExpression)
        {
            System.Linq.Expressions.MemberExpression memberExpression = (System.Linq.Expressions.MemberExpression)propertyExpression.Body;

            if (memberExpression != null)
            {
                return memberExpression.Member.Name;
            }
            else
                throw new ArgumentException("propertyExpression");
        }

        #endregion
    }

Diese Klasse implementiert das bereits Angekündigte INotifyPropertyChanged Interface.
Im nächsten Schritt habe ich ein ViewModel für eine einzelne Person angelegt.

PersonVM:

public class PersonVM : ViewModelBase
    {
        #region Properties

        private Person _person;
       
        public bool IsOnline
        {
            get { return this._person.IsOnline; }
            set
            {
                if (this._person.IsOnline != value)
                {
                    this._person.IsOnline = value;
                    this.OnPropertyChanged(() => this._person.IsOnline);
                }
            }
        }

        public string FirstName
        {
            get { return this._person.FirstName; }
            set
            {
                if (this._person.FirstName != value)
                {
                    this._person.FirstName = value;
                    this.OnPropertyChanged(() => this._person.FirstName);
                }
            }
        }

        public string LastName
        {
            get { return this._person.LastName; }
            set
            {
                if (this._person.LastName != value)
                {
                    this._person.LastName = value;
                    this.OnPropertyChanged(() => this._person.LastName);
                }
            }
        }

        #endregion

        #region ctor

        public PersonVM(Person person)
        {
            if (person != null)
                this._person = person;
        }

        #endregion

        public static IEnumerable<PersonVM> CreatePersonVMsForRepository(IEnumerable<Person> persons)
        {
            foreach (var person in persons)
                yield return new PersonVM(person);
        }
    }

Dieses ViewModel implementiert die einzelnen Komponenten der Person nochmalig und leitet die Änderungen entweder an die GUI weiter oder an die private Person Variable.
Weiterhin habe ich eine PersonRepositoryVM Klasse angelegt welche eine Sammlung von allen Personen und der Momentan ausgewählten Person in der Listbox darstellen soll. Diese Klasse sieht wie folgt aus(Bitte ignoriert vorerst die Commands, auf die möcht ich im letzten Teil eingehen).

PersonRepositoryVM:

#region Properties

        private Timer timer;
        private Random randomPersonVM = new Random();
       
        private ObservableCollection<PersonVM> _personVmCollection;
        public ObservableCollection<PersonVM> Personen
        {
            get
            {
                return this._personVmCollection;
            }
            private set
            {
                if (this._personVmCollection != value)
                {
                    this._personVmCollection = value;
                    this.OnPropertyChanged(() => this.Personen);
                }
            }
        }

        private PersonVM _currentPerson;
        public PersonVM CurrentPerson
        {
            get
            {
                return this._currentPerson;
            }
            set
            {
                if (this._currentPerson != value)
                {
                    this._currentPerson = value;
                    this.OnPropertyChanged(() => this.CurrentPerson);
                }
            }
        }

        private RelayCommand timerCommand;
        public ICommand SimulationCommand
        {
            get
            {
                if (this.timerCommand == null)
                    this.timerCommand = new RelayCommand(a => this.timer.Start());
                return this.timerCommand;
            }
        }

        private RelayCommand editCommand;
        public ICommand EditCommand
        {
            get
            {
                if (this.editCommand == null)
                    this.editCommand = new RelayCommand(a => this.timer.Stop()
                                                        , p => this.CurrentPerson != null);
                return this.editCommand;
            }
        }

        public bool CanEdit
        {
            get
            {
                return this.EditCommand.CanExecute(null);
            }
        }

        #endregion

        public PersonRepositoryVM(IEnumerable<PersonVM> personViewModels)
        {
            if (personViewModels == null)
                throw new ArgumentNullException("personViewModels");

            this._personVmCollection = new ObservableCollection<PersonVM>(personViewModels);

            if (this._personVmCollection.Count != 0)
                this.CurrentPerson = this._personVmCollection.FirstOrDefault();

            this.timer = new Timer(1000);
            this.timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
           

        }

        private void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
           

            this.CurrentPerson = this.Personen[this.randomPersonVM.Next(0, this.Personen.Count)];
            this.CurrentPerson.IsOnline = !this.CurrentPerson.IsOnline;
        }

        #region IDisposable Member

        public void Dispose()
        {
            this.timer.Dispose();
        }

        #endregion
    }

Wie ihr seht existiert in dieser Klasse eine ObservableCollection, in welcher alle PersonVMs gehalten werden welche momentan existieren. Weiterhin gibt es eine Property die die Aktuelle Person darstellen soll, jedoch wie immer gekapselt in das zugehörige ViewModel. Weiterhin habe ich noch einen Timer eingebaut welcher aller einer Sekunde den Online Status einer Person ändern soll damit auch etwas “Bewegung” in der GUI sieht.

Wie wird das ganze nun mit der Oberfläche verbunden?

In dem Mainwindow passieren eigentlich nur drei Schritte:

MainWindow:

public partial class MainWindow : Window
    {
       
        public MainWindow()
        {
            InitializeComponent();

            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
            this.Closing += new System.ComponentModel.CancelEventHandler(MainWindow_Closing);
           
        }

        void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
           
            var personRepVM = this.DataContext as PersonRepositoryVM;
            personRepVM.Dispose();
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {

            var personsList = Person.CreatePersonRepository(10).ToList();
            var personVMs = PersonVM.CreatePersonVMsForRepository(personsList).ToList();

            PersonRepositoryVM repository = new PersonRepositoryVM(personVMs);

            this.DataContext = repository;
       
       
        }

       
    }

Sobald das Fenster geladen wurde, lade ich alle Personen, mache aus diesen Personen die zugehörigen PersonenViewModels, und übergebe diese Liste von PersonenViewModels an das Repository welches zum Schluss als DataContext des Fensters gesetzt wird. Über die Eigenschaften welche ich in der GUI gebunden habe, habe ich nun Zugriff auf das ViewModel. Alle Änderungen am Model, wie die Änderung des Status werden an die Oberfläche durch das Event PropertyChanged weitergeleitet, und meine Oberfläche aktuallisiert sich.
Soweit so gut, im nächsten Teil werde ich nochmal kurz auf die Commands eingehen und euch den Quellcode zur Verfügung stellen.

Aug 13 10

WPF – MVVM in Aktion – Teil 1 – Erklärung

by ckruczek

Ich will euch präsentieren wie man eine WPF-Applikation mit dem Model – View – ViewModel Pattern umsetzt. Ich habe das ganze in mehrere Teile aufgebaut und will heute mit dem ersten Teil beginnen.

Viele kennen das MCV-Pattern welches uns ermöglicht die Oberflächenlogik von der Geschäftslogik zu trennen und so eine skalierbare Anwendung zu schaffen. Mit WPF wurde ein neues Pattern eingeführt was den Controller in dem Sinne wegfallen lässt und ein anderes Model dazwischen schiebt. Das ViewModel. Das ViewModel dient als Brücke zwischen Oberfläche und dem Model. Das ViewModel beinhaltet das Model und leitet die Änderungen an die Oberfläche weiter und empfängt diese. Somit ist das Model immer auf dem neusten Stand. Hierfür implementiert das ViewModel das Interface INotifyPropertyChanged.
Diese Interface bietet ein Event an, welches Änderungen am Model signalisieren soll. Mit diesem Event arbeit die WPF dann um die Änderungen an den Elementen zu publizieren.
Damit die Oberfläche das Model darstellen kann, bindet es nicht etwa das Model an die Oberflächenelemente, sondern die Eigenschaften des ViewModels,welches die nötigen Eigenschaften des Model ,nach außen für die Oberfläche, kapselt. Doch warum sollte man jetzt noch eine Ebene dazwischen schieben? Zum einen hat das den Vorteil das man nun Oberfläche und Model komplett kapseln kann und die Oberfläche unabhängig von dem Model agiern kann, denn das ViewModel kommuniziert zwischen beiden. Weiterhin brauch man im ViewModel keine Referenz auf die Oberfläche, sondern das ViewModel wird an die Eigenschaften der Oberfläche gebunden. Für WPF hat das die Vorteile das man sich nicht darum kümmern muss die Oberfläche zu aktualisieren, denn die Datenbindungsinfrastruktur macht das für einen automatisch.

Soviel zu einer kurzen Einführung. Im nächsten Artikel zeig ich euch dann wie man ein einfaches ViewModel aufsetzt und die Eigenschaften an die Oberfläche bindet und mit INotifyPropertyChanged die Daten aktualisieren lässt.

Jul 13 10

Sharepoint 2010 – Ribbon anpassen

by ckruczek

Wenn ihr wie in dem Blogeintrag von Fabian Moritz: Das Ribbon anpassen. Und dieses dann Debuggt um zu testen, dann läuft das beim ersten Debuggdurchgang ohne Probleme. Wenn ihr nun aber an der XML-Datei was ändert, und erneut debuggt so wird es nicht funktionieren und man hat das Gefühl die Anwendung nutzt die alte XML-Datei.

Lösung: Ihr müsst nach jedem Debuggdurchgang den Cache von eurem Browser leeren!!! Sehr Wichtig!

Jul 6 10

SharePoint – SPRoleDefinition Sprachübergreifend finden

by ckruczek

Um eine SPRoleDefinition im SPWeb zu finden gibt es zwei Wege. Der eine Weg ist es über den Namen der Rolle zu machen, ungefähr so:

SPRoleDefinition roleDefinition = spWeb.RoleDefinitions["Lesen"];

Wenn wir uns jetzt aber auf einem Englischen Sharepoint befinden, wird diese Variante mit Sicherheit eine Exception auslösen weil die Rolle nicht gefunden werden kann. Lösung für diese Problem sieht so aus:

SPRoleDefinition roleDefinition = item.Web.RoleDefinitions.GetByType(SPRoleType.Reader);

Äquivalente Rolen im deutschen SharePoint für die SPRoleTypes sind:
SPRoleType.Reader -> Lesen
SPRoleType.Contributer -> Mitwirken
SPRoleType.Administrator -> Vollzugriff
SPRoleType.Guest -> Besucher SPRoleType.None -> Keine
SPRoleType.WebDesigner -> Entwerfen