0

Working on an mvvm demo project (not homework)

  1. Class Friends which inherits ObservableCollection
  2. Class Friends ToString is FirstName + " " + LastName, etc
  3. ViewModel with a Friends and SelectedFriend property
  4. ComboBox ItemsSource is bound to the ViewModel Friends property
  5. ComboBox SelectedItem is bound to the ViewModel Friends property
  6. TextBoxes Texts are bound to the ViewModel SelectedFriend.FirstName properties, etc

I make the following changes

  1. Select Steve
  2. Change to Stephen - changes take because if data is saved and reloaded is shows Stephen
  3. ComboBox still shows Steve
  4. Drop ComboBox list and select Sara
  5. Drop ComboBox list - shows Steve
  6. Select Steve out of ComboBox list
  7. ComboBox now shows Stephen as does FirstName TextBox
  8. Select Sara
  9. Drop ComboBox list and it shows Steve
  10. ComboBox list doesn't change until data is saved and reloaded

The data is persisted to an xml file. What piece am I missing so that the ComboBox lists show the updated changes to SelectedItem?

Model classes

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace MVVM_Example.Models
{
    public enum Gender : int
    {
        None = 0,
        Male,
        Female
    }

    public class Friend : INotifyPropertyChanged
    {
        private string _firstName;

        public string FirstName 
        {
            get { return _firstName; }
            set
            {
                if (_firstName != value)
                {
                    _firstName = value;
                    OnPropertyChanged("FirstName");
                }
            }
        }

        public string LastName { get; set; }
        public DateTime HireDate { get; set; }
        public Gender Gender { get; set; }
        public bool IsContractor { get; set; }
        public string FullName { get { return string.Format("{0}, {1}", LastName, FirstName); } }

        public override string ToString()
        {
            return string.Format("{0} {1}: Hired {2}, Gender {3}, Status: {4}", FirstName, LastName, HireDate.ToString("MMM dd, yyyy"), Gender, IsContractor ? "Contractor" : "Employee");
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;

using MVVM_Example.Models;

namespace MVVM_Example.Models
{
    public class Friends : ObservableCollection<Friend>
    {
        public Friends()
            : this(loadFriends())
        {
        }

        public Friends(List<Friend> friends)
            : this(friends.ToArray())
        {
        }

        public Friends(IEnumerable<Friend> friends)
            : base(friends)
        {
        }

        public static implicit operator Friends(List<Friend> friends)
        {
            return new Friends(friends);
        }

        public Friends Sort()
        {
            return this.Cast<Friend>().OrderBy(f => f.LastName).ThenBy(f => f.FirstName).ThenByDescending(f => f.HireDate).ToList();
        }

        private static List<Friend> loadFriends()
        {
            return new List<Friend>
            {
                new Friend { FirstName = "Steve", LastName = "Wilson", Gender = Gender.Male, HireDate = new DateTime(2004, 3, 12), IsContractor = false },
                new Friend { FirstName = "Adam", LastName = "Flowers", Gender = Gender.Male, HireDate = new DateTime(2012, 12, 28), IsContractor = false },
                new Friend { FirstName = "Sara", LastName = "Fennema", Gender = Gender.Female, HireDate = new DateTime(2006, 8, 15), IsContractor = false },
                new Friend { FirstName = "Gary", LastName = "Winey", Gender = Gender.Male, HireDate = new DateTime(2012, 4, 6), IsContractor = true },
            }.OrderBy(f => f.LastName).ThenBy(f => f.FirstName).ToList();
        }
    }
}

ViewModel

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Xml;
using System.Xml.Serialization;

using MVVM_Example.Models;

namespace MVVM_Example.ViewModels
{
    public class FriendsViewModel : INotifyPropertyChanged
    {
        #region Properties

        #region AreFriendsLoaded

        public bool AreFriendsLoaded
        {
            get { return _areFriendsLoaded; }
            private set
            {
                if (_areFriendsLoaded != value)
                {
                    _areFriendsLoaded = value;
                    OnPropertyChanged("AreFriendsLoaded");
                }
            }
        }
        private bool _areFriendsLoaded;

        #endregion

        #region Friends

        public Friends Friends
        {
            get { return _friends; }
            private set
            {
                if (_friends != value)
                {
                    _friends = value;
                    OnPropertyChanged("Friends");
                    HasFriends = value != null && value.Count > 0;
                }
            }
        }
        private Friends _friends;

        #endregion

        #region HasFriends

        public bool HasFriends 
        { 
            get { return _hasFriends; }
            private set
            {
                if (_hasFriends != value)
                {
                    _hasFriends = value;
                    OnPropertyChanged("HasFriends");
                }
            }
        }
        private bool _hasFriends;

        #endregion

        #region SelectedFriend

        public Friend SelectedFriend
        {
            get { return _selectedFriend; }
            set
            {
                if (_selectedFriend != value)
                {
                    _selectedFriend = value;
                    OnPropertyChanged("SelectedFriend");
                    transferData(true);
                }
            }
        }
        private Friend _selectedFriend;

        #endregion

        #region FirstName

        public string FirstName
        {
            get { return _firstName; }
            set
            {
                if (_firstName != value)
                {
                    _firstName = value;
                    OnPropertyChanged("FirstName");
                    transferData("FirstName");
                }
            }
        }
        private string _firstName;

        #endregion

        #endregion

        #region Constructors

        public FriendsViewModel()
            : base()
        {
            verifyFriends();
        }

        #endregion

        #region Methods

        protected virtual void DeleteSelectedFriend()
        {
            if (Friends.Contains(SelectedFriend))
            {
                Friends.Remove(SelectedFriend);
            }
        }

        private void transferData(bool fromSelectedFriend)
        {
            transferData(fromSelectedFriend, null);
        }

        private void transferData(string propertyName)
        {
            transferData(false, propertyName);
        }

        private void transferData(bool fromSelectedFriend, string propertyName)
        {
            if (fromSelectedFriend)
            {
                FirstName = SelectedFriend == null ? null : SelectedFriend.FirstName;
            }
            else if (SelectedFriend != null)
            {
                if (propertyName == null)
                {
                    throw new InvalidOperationException("Missing propertyName argument in transferData method.");
                }

                switch (propertyName.Trim().ToLowerInvariant())
                {
                    case "firstname":
                        SelectedFriend.FirstName = FirstName;
                        break;
                    default:
                        throw new InvalidOperationException(string.Format("Unexpected property name '{0} in transferData method.", propertyName));
                }
            }
        }

        private void verifyFriends()
        {
            if (Friends == null)
            {
                Friends = new Friends();
            }
        }

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {

            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}

View - UserControl

<UserControl x:Class="MVVM_Example.Views.FriendsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:j2a="http://j2associates.com/MVVMBase"
             xmlns:sys="clr-namespace:System;assembly=mscorlib"
             xmlns:vms="clr-namespace:MVVM_Example.ViewModels"
             mc:Ignorable="d" 
             d:DesignHeight="320" d:DesignWidth="400" Name="userControl">

    <UserControl.DataContext>
        <vms:FriendsViewModel />
    </UserControl.DataContext>

    <Grid Margin="5">
        <StackPanel>
            <DockPanel LastChildFill="False">
                <Label DockPanel.Dock="Top" Content="Friends" />
                <ComboBox DockPanel.Dock="Bottom" Name="friendsComboBox" Margin="0,5,0,0" ToolTipService.ShowOnDisabled="True" VerticalContentAlignment="Center" 
                          ItemsSource="{Binding Friends}" SelectedItem="{Binding SelectedFriend}" IsEnabled="{Binding HasFriends}" />
            </DockPanel>

            <GroupBox Header="Selected Friend" Margin="0,5,0,0">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="auto"/>
                        <RowDefinition Height="auto"/>
                        <RowDefinition Height="auto"/>
                        <RowDefinition Height="auto"/>
                        <RowDefinition Height="auto"/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="auto"/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>

                    <Label Grid.Row="0" Grid.Column="0" Content="First Name" Margin="10,5,0,0" />
                    <Label Grid.Row="1" Grid.Column="0" Content="Last Name" Margin="10,5,0,0" />
                    <Label Grid.Row="2" Grid.Column="0" Content="Hire Date" Margin="10,5,0,0" />
                    <Label Grid.Row="3" Grid.Column="0" Content="Gender" Margin="10,5,0,0" />
                    <Label Grid.Row="4" Grid.Column="0" Content="Contractor" Margin="10,5,0,0" />

                    <TextBox Grid.Row="0" Grid.Column="1" Margin="5,5,0,0" VerticalContentAlignment="Center" 
                             Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged, TargetNullValue=Undefined}" />
                    <TextBox Grid.Row="1" Grid.Column="1" Margin="5,5,0,0" VerticalContentAlignment="Center" 
                             Text="{Binding SelectedFriend.LastName, UpdateSourceTrigger=PropertyChanged, FallbackValue=Undefined}" />
                    <DatePicker Grid.Row="2" Grid.Column="1" Margin="5,5,0,0" VerticalContentAlignment="Center" 
                                SelectedDate="{Binding SelectedFriend.HireDate}" />
                    <TextBox Grid.Row="3" Grid.Column="1" Margin="5,5,0,0" VerticalContentAlignment="Center" 
                             Text="{Binding SelectedFriend.Gender, UpdateSourceTrigger=PropertyChanged, FallbackValue=Undefined}" />
                    <CheckBox Grid.Row="4" Grid.Column="1" VerticalAlignment="Center" Margin="5,5,0,0" VerticalContentAlignment="Center" 
                              IsChecked="{Binding SelectedFriend.IsContractor, FallbackValue={x:Null}}" />
                </Grid>
            </GroupBox>

            <DockPanel LastChildFill="False">
                <Button DockPanel.Dock="Left" Content="Save data" Margin="5,5,0,0" Width="70" Click="saveDataButton_Click" />
            </DockPanel>
        </StackPanel>
    </Grid>
</UserControl>

ViewModel Code Behind - used event instead of command to minimize code displayed ehre

    private void saveDataButton_Click(object sender, RoutedEventArgs e)
    {
        string path = Directory.GetCurrentDirectory();
        path = new DirectoryInfo(Path.Combine(path, "..\\..\\Data\\friends.xml")).FullName;

        // Create a new file stream to write the serialized object to a file.
        using (TextWriter fileStreamWriter = new StreamWriter(path))
        {
            // Create a new XmlSerializer instance with the type.
            XmlSerializer serializer = new XmlSerializer(typeof(Friends));
            serializer.Serialize(fileStreamWriter, ((MVVM_Example.ViewModels.FriendsViewModel)DataContext).Friends);

            // Cleanup.
            fileStreamWriter.Close();
        }

        MessageBox.Show(File.ReadAllText(path), "Data Saved", MessageBoxButton.OK, MessageBoxImage.Information);
    }

Main Window

<Window x:Class="MVVM_Example.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:views="clr-namespace:MVVM_Example.Views"
        Title="Main Window" Height="350" Width="425">
    <Grid>
        <views:FriendsView x:Name="friendsView1" />
    </Grid>
</Window>
j2associates
  • 1,115
  • 10
  • 19

0 Answers0