当前位置:编程学习 > C#/ASP.NET >>

跟我学MVVM模式开发(2)

第一贴在这里:http://topic.csdn.net/u/20110311/22/5736a4e7-b7d2-4053-b3ca-9f86b288c6f5.html
第一贴可以说不怎么成功,我个人有时候特别喜欢打口水仗,所以第一贴最后成了口水战贴了,实在是不好意思,在这里做自我批评。 第二贴不会出现这种情况了,大家可以自由攻击我,如果觉得有用就拿回去用,如果觉得不好就一起讨论。 第一贴里我讲了一点点DataContext,但是我发现,这样讲效率太低下,而且不容易让初学者吃饱——一次能教的东西太少。这次我打算尝试个新讲法,直接从一个比较全面的例子开始。好,下面言归正传了。

我们还是纠结于Student。我们最终的目标是实现一个关于Student类的CRUD(即Create, Read, Update, Delete)。我们首先从Read说起。首先我们需要Model,也就是Student。打开你的VS,新建一个名叫"MvvmTutorial"的WPFApplication工程。之后在当前Solution下添加一个新的Project,名叫"MvvmTutorial.Model"。

下面我说的话是对新手说的:一个Solution里可以包含若干个Project,所以以后写程序时不要把所有层的代码都混到一个Project里,这样不是很有利于代码复用。

有了MvvmTutorial.Model这个Project,我们便有了Model层。在MvvmTutorial.Model下添加一个新的class,名叫Student。代码如下:

using System;

namespace MvvmTutorial.Model
{
    public class Student
    {
        #region Constructors
        public Student()
        {
            StudentID = Guid.NewGuid();
        }
        #endregion // Constructors

        #region Properties
        public Guid StudentID
        {
            get;
            private set;
        }

        public string Name
        {
            get;
            set;
        }

        public int Age
        {
            get;
            set;
        }

        public Gender Gender
        {
            get;
            set;
        }

        public string Description
        {
            get;
            set;
        }
        #endregion // Properties
    }

    public enum Gender
    {
        Male,
        Female
    }
}


这里恳请各位读者不要纠结于一些细节问题,比如Student的Age怎么没有做验证之类的问题。我们的例子目前还很粗糙,因为我们的侧重点在于MVVM。至于一些细节方面的东西,以后自然会提到。至于我为什么不把Gender这个枚举单独放到一个文件里——我乐意。

好了,Model有了,还缺ViewModel。那么下面我们就添加一个新的Project名叫MvvmTutorial.ViewModel,然后在其下添加一个类名叫StudentViewModel,代码如下:

using MvvmTutorial.Model;
using System;

namespace MvvmTutorial.ViewModel
{
    public class StudentViewModel
    {
        #region Fields
        private readonly Student _student;
        #endregion // Fields

        #region Constructors
        public StudentViewModel(Student student)
        {
            if (student == null)
            {
                throw new ArgumentNullException("student");
            }
            _student = student;
        }
        #endregion // Constructors

        #region Properties
        public string Name
        {
            get
            {
                return _student.Name;
            }
            set
            {
                _student.Name = value;
            }
        }

        public int Age
        {
            get
            {
                return _student.Age;
            }
            set
            {
                _student.Age = value; 
                // Yes, we don't have validation here, so you can create a student whose age is over 1000!
            }
        }

        public Gender Gender
        {
            get
            {
                return _student.Gender;
            }
            set
            {
                _student.Gender = value;
            }
        }

        public string Description
        {
            get
            {
                return _student.Description;
            }
            set
            {
                _student.Description = value;
            }
        }
        #endregion // Properties
    }
}

注意,你需要在MvvmTutorial.ViewModel中添加MvvmTutorial.Model的reference。下面,为了一切从简,我不打算添加MvvmTutorial.View这个Project,我打算把所有的XAML文件,也就是View层的东西,放到MvvmTutorial下面。在添加StudentView之前,我们还需要做一件事:由于我们没有数据,所以我们要写一个类来生成一些随机数据以供测试之用。在ViewModel层下添加这样一个名叫StudentDesignDataService的类,代码如下:

using System;
using System.Collections.Generic;
using MvvmTutorial.Model;

namespace MvvmTutorial.ViewModel
{
    /// <summary>
    /// A static class whose sole purpose is to generate a collection of StudentViewModel for testing.
    /// </summary>
    public static class StudentDesignDataService
    {
        public static IList<StudentViewModel> GetRandomStudents(int howMany)
        {
            List<StudentViewModel> result = new List<StudentViewModel>();
            for (int i = 0; i < howMany; ++i)
            {
                Student randomStudent = new Student()
                {
                    Name = "Name " + i,
                    Age = new Random(Guid.NewGuid().GetHashCode()).Next(17, 30), // Just give a random age
                    // ranging from 17 to 30. I assume all the students are actually college students...
                    Gender = i % 2 == 0 ? Gender.Male : Gender.Female,
                    Description = "Description " + i
                };
                result.Add(new StudentViewModel(randomStudent));
            }
            return result;
        }
    }
}


OK,现在数据有了,可谓万事俱备只欠View。我们的View就是你在创建MvvmTutorial这个Project的时候,VS为你自动添加的那个MainWindow.xaml。不过首先你要进入App.xaml中,把里面的StartupUri="MainWindow.xaml"删掉——这段代码的作用是在程序启动之时自动创建一个MainWindow的实例,并将其设为主程序的主窗口——我们不想要这个自动化过程,所以删除它。
删除后的App.xaml的代码为:

<Application x:Class="MvvmTutorial.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources>
         <!-- Here is the place where we put our app-level resources, but we won't care it at this moment -->
    </Application.Resources>
</Application>

在MainWindow.xaml这个文件里,你需要做出以下修改,代码如下:

<Window x:Class="MvvmTutorial.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="480" Width="640">
    <Grid>
        <ListView ItemsSource="{Binding}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Name">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Name, Mode=OneWay}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Age">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Age, Mode=OneWay}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Gender">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Gender, Mode=OneWay}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Description">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Description, Mode=OneWay}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

这些代码看起来很多层,其实无非就是添加了一个ListView,并将其View设置为GridView,用来显示Student的数据而已。现在什么都有了,还差最后一步——我们要给View,也就是MainWindow设置其DataContext。打开你的App.xaml.cs文件,修改如下:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
using MvvmTutorial.ViewModel;

namespace MvvmTutorial
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            var mainWindow = new MainWindow(); // Create a new instance of MainWindow
            Application.Current.MainWindow = mainWindow; // Set mainWindow as current app's main window
            mainWindow.DataContext = StudentDesignDataService.GetRandomStudents(20); // Set DataContext as
            // a list of our randomly generated StudentViewModels
            mainWindow.Show();
        }
    }
}

这段代码的作用就是在程序启动之时创建一个MainWindow实例,然后设置它的DataContext为我们随机生成的一系列StudentViewModel,最后Show出这个窗口。

现在你可以按F5跑一下程序试试了。以上代码我测试过,应该没有什么问题。我有可能出现粘贴错误,所以如果编译器抱怨有问题,你得自己动脑筋debug一下了。

到目前为止,我们表面上是建立了ViewModel,Model,View等东西,但这其实与MVVM关系不大——我们还没有开始讲MVVM真正强大的地方。以上这些只是为我后面的讲解奠定一点基础,也让新人有个感性认识罢了。如果你觉得感兴趣,请持续关注本贴。 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 友情帮顶,建议LZ可以搞到博客里面去。 --------------------编程问答--------------------
只看看。。。 --------------------编程问答-------------------- HAO LI HAI --------------------编程问答-------------------- 简单说。。。 --------------------编程问答-------------------- --------------------编程问答-------------------- 不错不错 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 定不错 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 下载学习,谢谢发帖。 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 楼主你为什么不一次讲完呢?这样看的多累啊 --------------------编程问答--------------------
引用 22 楼 zzxap 的回复:
楼主你为什么不一次讲完呢?这样看的多累啊

老兄,我一次打那么多字也不容易啊。。。。我是左窗口VS2010,右边开着CSDN……而且这个是面向没有基础的人的,也得让别人有时间消化一下。 --------------------编程问答-------------------- 强力顶起。。。。继续继续啊,楼主。。。。写完请你吃巧乐兹啊 --------------------编程问答-------------------- 首先感谢大家的支持以及版主的加精。废话少说,我们继续。昨天简单讲了一下如何用MVVM的方式读取数据,但那个只是最最基本的一点概念而已,如果你是MVVM新人,那么我们要学的还有很多。不过今天我们先把MVVM暂且一放,因为我们有一个问题尚未解决——即数据层。我们昨天是自己创建了一个静态类StudentDesignDataService,利用它返回一些随机生成的StudentViewModel,为的是使我们快速看到结果。不过我们不能一直没有数据层,否则就无法保存任何Student的数据了。为此,我们今天就抽出一点时间来写点简单的数据层代码,来为后来的MVVM讲解做铺垫。

首先我说一下大体思路:其实很简单,由于这是讲解,所以我希望一切从简。原本我想使用JSON作为数据源,后来觉得有些人还得下载C#的JSON类库,干脆还是用XML吧。思路大体是这样:我们利用ADO.NET提供的DataSet和DataTable这两个类来操作数据,最后将其转换为XML数据保存到本地硬盘里。

还记得昨天的Student类吗?我们需要将其改写一下,代码如下:

using System;
using System.Data;

namespace MvvmTutorial.Model
{
    public class Student
    {
        #region Fields
        public const string StudentIDPropertyName = "StudentID";
        public const string NamePropertyName = "Name";
        public const string AgePropertyName = "Age";
        public const string GenderPropertyName = "Gender";
        public const string DescriptionPropertyName = "Description";
        #endregion // Fields

        #region Constructors
        /// <summary>
        /// Creates an instance of Student from DataRow
        /// </summary>
        public static Student FromDataRow(DataRow dataRow)
        {
            return new Student()
            {
                StudentID = dataRow.Field<Guid>(Student.StudentIDPropertyName),
                Name = dataRow.Field<string>(Student.NamePropertyName),
                Age = dataRow.Field<int>(Student.AgePropertyName),
                Gender = dataRow.Field<Gender>(Student.GenderPropertyName),
                Description = dataRow.Field<string>(Student.DescriptionPropertyName)
            };
        }

        /// <summary>
        /// Creates a brand new instance of Student
        /// </summary>
        public static Student CreateNewStudent(string name, int age, Gender gender, string description)
        {
            return new Student()
            {
                IsNew = true,
                StudentID = Guid.NewGuid(),
                Name = name,
                Age = age,
                Gender = Gender.Male,
                Description = description
            };
        }

        private Student() { }
        #endregion // Constructors

        #region Properties
        public Guid StudentID
        {
            get;
            private set;
        }

        public string Name
        {
            get;
            set;
        }

        public int Age
        {
            get;
            set;
        }

        public Gender Gender
        {
            get;
            set;
        }

        public string Description
        {
            get;
            set;
        }

        public bool IsNew
        {
            get;
            private set;
        }
        #endregion // Properties
    }

    public enum Gender
    {
        Male,
        Female
    }
}

注意到没?原先Student的默认构造函数已经被改成private的了——原因在于我不想让外界调用它,并且我更希望外界调用那个两个static构造函数,即FromDataRow和CreateNewStudent。其中,FromDataRow我们待会儿便会用到,而CreateNewStudent无非就是强迫你填入一些参数然后生成一个新的Student而已。

下面是重点了。首先,为了简单起见,我不打算建立新的Project了,所以我就直接把数据层的代码放到MvvmTutorial.Model下了。在MvvmTutorial.Model的Project下添加一个新类,名字叫做DataRepository,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;

namespace MvvmTutorial.Model
{
    public class DataRepository
    {
        #region Fields
        private DataSet _data;
        /// <summary>
        /// The file path that we use to store XML file which holds all the app data we need
        /// </summary>
        public static readonly string PhysicalFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "mvvm.rep");
        /// <summary>
        /// Name of [Students] table
        /// </summary>
        private const string StudentTableName = "Students";
        #endregion // Fields

        #region Constructors
        public DataRepository()
        {
            this.CreateDataSource();

            if (!File.Exists(PhysicalFilePath))
            {
                this.AddDefaultStudents();
                this.Save();
            }
            else
            {
                this.Load();
            }
        }
        #endregion // Constructors

        #region Properties
        /// <summary>
        /// Gets Students table
        /// </summary>
        private DataTable Students
        {
            get
            {
                return _data.Tables[StudentTableName];
            }
        }
        #endregion // Properties

        #region Public Methods
        /// <summary>
        /// Gets all the rows from Students table, transforms them into Student and returns a list of Student intances.
        /// </summary>
        public IEnumerable<Student> GetStudents()
        {
            foreach (DataRow dataRow in Students.Rows)
            {
                yield return Student.FromDataRow(dataRow);
            }
        }

        public void SaveStudent(Student student)
        {
            // If current student is a new record, it means this student is a new tuple, so we
            // need to add it into table
            if (student.IsNew)
            {
                AddStudent(student);
            }
            else
            {
                // Otherwise, the student may have existed, we need to find it and update its values to current values
                // Here, we search the student.
                var target = Students.AsEnumerable().FirstOrDefault(p => p.Field<Guid>(Student.StudentIDPropertyName) == student.StudentID);
                if (target != null) // We indeed found our wanted student record, so update it
                {
                    target.SetField<string>(Student.NamePropertyName, student.Name);
                    target.SetField<int>(Student.AgePropertyName, student.Age);
                    target.SetField<Gender>(Student.GenderPropertyName, student.Gender);
                    target.SetField<string>(Student.DescriptionPropertyName, student.Description);
                }
            }
            _data.AcceptChanges();
            this.Save();
        }
        #endregion // Public Methods

        #region Private Methods
        /// <summary>
        /// Creates the structure of all tables
        /// </summary>
        private void CreateDataSource()
        {
            _data = new DataSet("StudentData");

            DataTable students = new DataTable(StudentTableName);
            students.Columns.Add(Student.StudentIDPropertyName, typeof(Guid));
            students.Columns.Add(Student.NamePropertyName, typeof(string));
            students.Columns.Add(Student.AgePropertyName, typeof(int));
            students.Columns.Add(Student.GenderPropertyName, typeof(Gender));
            students.Columns.Add(Student.DescriptionPropertyName, typeof(string));

            _data.Tables.Add(students);
        }

        /// <summary>
        /// Adds some default student rows to Students table if there aren't any yet.
        /// </summary>
        private void AddDefaultStudents()
        {
            int total = new Random(Guid.NewGuid().GetHashCode()).Next(10, 30);
            for (int i = 0; i < total; ++i)
            {
                string randomName = "Name " + i;
                int randomAge = new Random(Guid.NewGuid().GetHashCode()).Next(17, 30);
                Gender randomGender = i % 2 == 0 ? Gender.Male : Gender.Female;
                string randomDescription = "Description " + i;
                Student randomStudent = Student.CreateNewStudent(randomName, randomAge, randomGender, randomDescription);
                this.AddStudent(randomStudent);
            }
        }

        /// <summary>
        /// Accepts all changes to current repository and persist changes to XML file
        /// </summary>
        private void Save()
        {
            _data.AcceptChanges();
            _data.WriteXml(PhysicalFilePath);
        }

        /// <summary>
        /// Reads data from XML file
        /// </summary>
        private void Load()
        {
            _data.ReadXml(PhysicalFilePath);
        }

        /// <summary>
        /// Adds one student as a new row to Students table
        /// </summary>
        /// <param name="student"></param>
        private void AddStudent(Student student)
        {
            Students.Rows.Add(new object[]
                {
                    student.StudentID,
                    student.Name,
                    student.Age,
                    student.Gender,
                    student.Description
                });
        }
        #endregion // Private Methods
    }
}

这段代码稍微有点长,不过并不难理解。大体功能就是:从C:\ProgramData\中找到本程序的XML数据文件,如果未找到就新建一个此文件,并填入一些随机数据。至于其他函数,我都有注释,应该比较好理解,不懂的留言提问。OK,我现在只能输入1200多字符了,下一贴继续(马上回来)。 --------------------编程问答-------------------- 好了,现在我们有了DataRepository这个类,那么它真的实现我们所要求的功能么?也就是说,它是否存在Bug我们尚不得知,所以,接下来的工作自然是——测试!我们要写点Unit test来在最短时间内测试我们的代码是否存在问题。在VS下面建立测试Project是非常方便的。比如,我是这么做的:

打开DataRepository.cs,找到GetStudents方法,然后点右键,选择Create unit tests...,如果你用的是中文版,那差不多应该是选择“建立单元测试”。之后,跟着向导走,它会让你为Project取一个名字,我们就叫它MvvmTutorial.Test。

搞定了吗?当你有了MvvmTutorial.Test之后,你会得到一个DataRepositoryTest.cs文件,此文件是VS为你自动生成的,因为你当时是在DataRepository中建立的unit test project。你需要在MvvmTutorial.Test工程下添加一个文件夹,名叫Model,并把DataRepositoryTest.cs拖到这个文件夹下,然后你要把DataRepositoryTest的namespace改成MvvmTutorial.Test.Model,这是因为以后也许我们会有其他层的测试文件,所以我们用文件夹来进行分类,并用namespace来进行逻辑分类。

DataRepositoryTest里会有一些自动生成的代码,你可以不编辑它们,而你所要做的就是添加一些新的测试方法。注意,每个测试方法的头顶都有一个[TestMethod()],但凡有这个属性的方法都是要被测试(也就是会被run的方法)。

DataRepositoryTest的代码如下:

using MvvmTutorial.Model;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace MvvmTutorial.Test.Model
{
    /// <summary>
    ///This is a test class for DataRepositoryTest and is intended
    ///to contain all DataRepositoryTest Unit Tests
    ///</summary>
    [TestClass()]
    public class DataRepositoryTest
    {


        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion


        /// <summary>
        ///A test for GetStudents
        ///</summary>
        [TestMethod()]
        public void GetStudentsTest()
        {
            DataRepository target = new DataRepository();
            Debug.WriteLine(DataRepository.PhysicalFilePath);

            foreach (Student student in target.GetStudents())
            {
                Debug.WriteLine(student.Name);
            }
        }

        /// <summary>
        ///A test for persisting a new student by using SaveStudent method
        ///</summary>
        [TestMethod()]
        public void AddNewStudentTest()
        {
            DataRepository target = new DataRepository();
            var expect = Student.CreateNewStudent("Chris", 20, Gender.Male, "Lazy...");
            target.SaveStudent(expect);

            target = new DataRepository();
            var actual = target.GetStudents().FirstOrDefault(p => p.StudentID == expect.StudentID);
            if (actual != null)
            {
                Assert.AreEqual(actual.StudentID, expect.StudentID);
                Assert.AreEqual(actual.Name, actual.Name);
                Assert.AreEqual(actual.Age, expect.Age);
                Assert.AreEqual(actual.Gender, expect.Gender);
                Assert.AreEqual(actual.Description, actual.Description);
            }
            else
            {
                Assert.Fail("Didn't find the persisted student");
            }
        }

        /// <summary>
        /// A test for updating an existing student by using SaveStudent method
        /// </summary>
        [TestMethod()]
        public void UpdateStudentTest()
        {
            DataRepository target = new DataRepository();
            var expect = target.GetStudents().First();
            expect.Name = "John";
            expect.Gender = Gender.Male;
            expect.Age = 18;
            expect.Description = "Hello world!";
            target.SaveStudent(expect);

            target = new DataRepository();
            var actual = target.GetStudents().FirstOrDefault(p => p.StudentID == expect.StudentID);
            if (actual != null)
            {
                Assert.AreEqual(actual.StudentID, expect.StudentID);
                Assert.AreEqual(actual.Name, actual.Name);
                Assert.AreEqual(actual.Age, expect.Age);
                Assert.AreEqual(actual.Gender, expect.Gender);
                Assert.AreEqual(actual.Description, actual.Description);
            }
            else
            {
                Assert.Fail("Didn't find the updated student");
            }
        }
    }
}


那一大段被注释掉的代码是自动生成的,我们可能以后会用到,所以先留着它备用。好了,现在你需要做的是run一遍所有Test,然后观察程序输出以及它们是否全部通过。(我测试过,应该是全部通过,如果有问题,你得自己debug一下,或者留言问我)

今天先讲到这里,下回继续。再次感谢大家支持。 --------------------编程问答-------------------- --------------------编程问答-------------------- 能看懂一点点~~ --------------------编程问答-------------------- 仅能看懂一点啊
--------------------编程问答-------------------- 喜欢,学 --------------------编程问答-------------------- 认真看看,觉得有用 --------------------编程问答-------------------- 还可以,学习了。谢谢了
--------------------编程问答-------------------- 注释掉的代码是自动生 --------------------编程问答-------------------- study --------------------编程问答-------------------- 学习,讲的很多东西,我原来没有注意过,汗颜!努力学习 --------------------编程问答-------------------- 很好很好 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 很多朋友很牛啊。潜水多年,今天也来冒个泡。 --------------------编程问答-------------------- 想当老师不是那么好当的,没有丰富的开发经验,当老师让人笑的。
好为人师的太多了,被人指出问题又不肯接受,盲目自负。

CSDN 还是适合问些纯技术的问题。比如某个控件怎么用啊,某个语法怎么用啊。
这倒对我有很大的帮助。
--------------------编程问答-------------------- --------------------编程问答-------------------- “这里恳请各位读者不要纠结于一些细节问题,比如Student的Age怎么没有做验证之类的问题”

验证恰恰是最重要的地方,这是体现逻辑的地方,

楼主直接把最重要的给无视了,让我们情何以堪。
那些什么增加修改删除,谁都写得出来,没什么可讲的。 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 好,喜欢 --------------------编程问答-------------------- --------------------编程问答-------------------- 还有,对于多组关联如何写,
比如修改学生信息时,需要记录日志,插入日志信息,
你这个插入日志信息放在哪里?如何加进去,

--------------------编程问答-------------------- --------------------编程问答-------------------- 还有,学生有银行帐号字段(用于缴纳学费),修改时,只有有权限的人才能修改银行帐号字段,
而且这些敏感信息不是所有人都能看,你这个又怎么分出来??? --------------------编程问答-------------------- 不错,很喜欢楼主教科,mark~
话说41、43楼的有开战的趋势 --------------------编程问答-------------------- --------------------编程问答--------------------
引用 50 楼 weizisheng131083 的回复:
还有,学生有银行帐号字段(用于缴纳学费),修改时,只有有权限的人才能修改银行帐号字段,
而且这些敏感信息不是所有人都能看,你这个又怎么分出来???


Dude, like someone else, you just think too much. 

This tutorial is all about MVVM. This example program above has no commercial value and it will not be sold in market as well. As a tutorial, it shouldn't show everything as more as possible, because that will make novices confused and of course they have limit in absorbing knowledge everyday.

Plus, I said I would mention validation later, but not now. FYI, in this program, no student will have sentitive information. And, there won't be complex data relations. So please don't worry that much. My focus is not on data layer.

Once again, the tutorial is just a tutorial, which will not be used in real world. Its sole purpose is only to show people the idea and basic concept. You're definitely not a beginner, so I'm afraid that this thread couldn't be doing any favor for you. --------------------编程问答--------------------
引用 51 楼 wrqlgd 的回复:
不错,很喜欢楼主教科,mark~
话说41、43楼的有开战的趋势

Thanks for your support. I've already replied his challenge. If you think this is helpful to you, please keep following this thread. (Sorry, I can't input Chinese at this moment) --------------------编程问答-------------------- 看看,mvvm还没看,这些月都在看SL! --------------------编程问答-------------------- 53楼,这么说,
验证、逻辑的处理,不属于 MVVM 的范畴了,
那 MVVM 拿来做什么?装点门面?
MVVM绝不是你说的那样,你不理解MVVM。 --------------------编程问答-------------------- 教科的意思就是 教本宣科,呵呵。 --------------------编程问答-------------------- 如果真正理解 MVVM,我提的那些问题,那都是小Case,
还有逻辑的控制,比如编辑时控制长度、类型、控制选择啥的,,,, --------------------编程问答--------------------
引用 41 楼 weizisheng131083 的回复:
想当老师不是那么好当的,没有丰富的开发经验,当老师让人笑的。
好为人师的太多了,被人指出问题又不肯接受,盲目自负。

CSDN 还是适合问些纯技术的问题。比如某个控件怎么用啊,某个语法怎么用啊。
这倒对我有很大的帮助。


别人分享经验无可厚非,大家各取所需,你要是认为别人写的对你没用,完全可以不理会,没必要在在这里泼冷水,作为一个技术交流的社区,看看CSDN跟国外的差别有多大 --------------------编程问答-------------------- 呵呵,59楼,
如果楼主能解决我提的问题,那我跪下来拜他三拜都行。

再提个问题:某天学校领导有事通知各班班长,
现在大学学生都是很分散的,
所以领导通过系统,群发短信给该系的班长,通知去开会。

你这个怎么筛选,这么处理?给个方法就行,
假设短信模块已经有了,调用 SendSms 就行。 --------------------编程问答--------------------
引用 56 楼 weizisheng131083 的回复:
53楼,这么说,
验证、逻辑的处理,不属于 MVVM 的范畴了,
那 MVVM 拿来做什么?装点门面?
MVVM绝不是你说的那样,你不理解MVVM。


MVVM is not all about validation. I said I would mention that later. OK, fine, I don't understand MVVM and hopefully this can make you satisfied.

Back to the topic, teaching is not throw everything out. Maybe you misunderstood my point. I was just trying to make things smoother for beginners, and therefore I avoid to teach as much as possible each time.

If you think I don't understand MVVM then suit yourself.

引用 58 楼 weizisheng131083 的回复:
如果真正理解 MVVM,我提的那些问题,那都是小Case,
还有逻辑的控制,比如编辑时控制长度、类型、控制选择啥的,,,,

Believe me dude, everything in programming is not a small case. As to what you mentioned, if you desire so much to show everyone how to do it, then go ahead. Probably you can create another thread to help others, better than questioning me in a pointless way here... I'll just follow my initial idea to teach. --------------------编程问答-------------------- 所以,平时说的是条条是道,一碰到实际问题就束手无策了。
--------------------编程问答-------------------- --------------------编程问答--------------------
引用 60 楼 weizisheng131083 的回复:
呵呵,59楼,
如果楼主能解决我提的问题,那我跪下来拜他三拜都行。

再提个问题:某天学校领导有事通知各班班长,
现在大学学生都是很分散的,
所以领导通过系统,群发短信给该系的班长,通知去开会。

你这个怎么筛选,这么处理?给个方法就行,
假设短信模块已经有了,调用 SendSms 就行。


You've gone too far and I don't think I need to be polite to you anymore. Pal, the truth is: I'm working overseas, in a very big IT company, being senior software engineer, and you don't need to ask what it is. Normally I'm very busy (not today apparently) and I don't have time and I don't even bother caring about your these small shit problems.

You have so much time to waste here arguing with me. Why not spend some of your time to solve your programming problems yourself, using your intelligence and knowledge.

Additionaly, this thread is about showing MVVM to beginners, not aimed to solve your issues, and I can smell that you're absolutely a senior programmer, so, why bother keep haunting here? --------------------编程问答-------------------- 领导通过系统,群发短信给该系的各班班长,通知去开会。

短信中提到开会,看到短信请回复。
群发出去后,过一小时检查回复信息,如果没有回复,继续重发。
并重发给副班长。

检查回复用 ReadSms,ReadSms 有对象编号。 --------------------编程问答-------------------- LZ打不出胡汉字很悲剧,
但让我看你的英文我也很悲剧,
看得累啊.....

weizisheng131083 童鞋,
感觉你的建议不错,
不过希望你的话语能委婉点,
我支持LZ的帖子,希望以后会出现更多这样的帖子。
我们应该给予支持,
如果大家都像你这样,我怕以后没人会去写了。 --------------------编程问答--------------------
引用 62 楼 weizisheng131083 的回复:
所以,平时说的是条条是道,一碰到实际问题就束手无策了。


I'm just wondering what your goal is. You just want to show others that I'm not a programmer and I'm not allowed to teach others? Maybe I didn't solve any practical issues yet, or maybe I've solved thousands of them and you just don't know yet, which you don't have reason to.

Is it really making you so upset that I'm teaching in this thread? If it is, then, dude, I can't help you and you can just go home throw up everything you ate. --------------------编程问答--------------------
引用 66 楼 tommir3 的回复:
LZ打不出胡汉字很悲剧,
但让我看你的英文我也很悲剧,
看得累啊.....

weizisheng131083 童鞋,
感觉你的建议不错,
不过希望你的话语能委婉点,
我支持LZ的帖子,希望以后会出现更多这样的帖子。
我们应该给予支持,
如果大家都像你这样,我怕以后没人会去写了。


多谢提醒。 --------------------编程问答--------------------
引用 66 楼 tommir3 的回复:
LZ打不出胡汉字很悲剧,
但让我看你的英文我也很悲剧,
看得累啊.....

weizisheng131083 童鞋,
感觉你的建议不错,
不过希望你的话语能委婉点,
我支持LZ的帖子,希望以后会出现更多这样的帖子。
我们应该给予支持,
如果大家都像你这样,我怕以后没人会去写了。


I'm really sorry about the English... I don't want to use it either, but there is no way for me to input Chinese now... --------------------编程问答-------------------- 67楼,我的目的就是如何用 mvvm 去解决这些现实问题。

mvvm 的目的也是为了解决这些现实问题。 --------------------编程问答--------------------
引用 70 楼 weizisheng131083 的回复:
67楼,我的目的就是如何用 mvvm 去解决这些现实问题。

mvvm 的目的也是为了解决这些现实问题。


Alright, then, I'll say: I don't care about your problems... I'm here to teach beginners the very basic concept of MVVM. Go create another thread to ask for help. Don't linger here anymore. --------------------编程问答-------------------- 恩,71楼,
那好吧,
那就先把问题放在这里,我跟着你学,
等全部学完了,再来看这些问题如何解决。
--------------------编程问答-------------------- 这些问题解决本来是很简单的事情,
如何把这些问题放到 MVVP 的架构里去,才是大家关心的。
--------------------编程问答--------------------
引用 70 楼 weizisheng131083 的回复:
67楼,我的目的就是如何用 mvvm 去解决这些现实问题。

mvvm 的目的也是为了解决这些现实问题。


你的问题更多的是业务逻辑的问题,要说用mvvm来解决你的问题,就是领导发通知短信,通过Command绑定触发ViewModel的Command执行方法,ViewModel中跟数据库和短信模块的交互跟一般C#编程一样,最后只要将交互获得的信息显示到View层,这时就需要实现INotifyPropertyChanged接口通知View更改显示内容

mvvm是一种框架,它的核心依据是数据绑定,最主要的功能就是可以将界面开发与业务逻辑开发完全分开,对于小型项目,完全可以不用mvvm --------------------编程问答-------------------- 74楼,
是啊,就是如何将 业务逻辑与 MVVM 结合起来。
因为实际编写时,发现很难将业务逻辑与 MVVM 结合起来。

你说的这个没理解这个逻辑,

首先要筛选出班长出来,假设学生有个职务列表,
如何筛选出职务出来,然后将筛选出来的,放到表格中显示,
然后构造短信,发送。

而回复 --------------------编程问答-------------------- 算了,先跟着楼主学,
现在也很难说清楚,筛选什么,写sql语句,或者对象筛选,都不是问题,
问题不在这里。 --------------------编程问答--------------------
引用 76 楼 weizisheng131083 的回复:
算了,先跟着楼主学,
现在也很难说清楚,筛选什么,写sql语句,或者对象筛选,都不是问题,
问题不在这里。


So we shouldn't be arguing anymore? I hope we could be friends sitting together discussing programming. My ability may not be enough to solve your problem, because I myself have much to learn as well.

As to your problem, I think what #74 says is pretty close to the answer. MVVM is just a pattern, nothing more. I suppose you're not new to MVVM, so I suggest you can use some existing framework, such as MVVM Light Toolkit, which has a Messenger class that might be able to help you.

You're always welcome to discuss programming related issues here (better to show some code actually). Thank you.  --------------------编程问答-------------------- LZ让我想起了昔日的一个老师,一个好老师 --------------------编程问答-------------------- --------------------编程问答-------------------- LZ,我想问下,WPF以后会怎么发展啊,用WPF还得装framework这么大的东西,你说这个拿来做客户端实用吗? --------------------编程问答-------------------- 非常的支持LZ  也希望楼主能够多给大家上点儿课!    .NET平台上的技术就WPF我不是很了解  以为公司原因本来目前也还没有打算去学习  但是看到LZ的帖子 似乎觉得也很有学习的必要  但是我却有一个疑问 :

           WPF与WINFORM开发相比  好处有哪些? 为什么你开发一个WINDOW程序要选择WPF而不是WINFORM呢?

这点对我很重要! 我希望是以你的经验来说说!  谢谢!

--------------------编程问答--------------------
引用 80 楼 blackeye2004 的回复:
LZ,我想问下,WPF以后会怎么发展啊,用WPF还得装framework这么大的东西,你说这个拿来做客户端实用吗?


I'll answer your question tonight. --------------------编程问答--------------------
引用 81 楼 zjx198934 的回复:
非常的支持LZ  也希望楼主能够多给大家上点儿课!    .NET平台上的技术就WPF我不是很了解  以为公司原因本来目前也还没有打算去学习  但是看到LZ的帖子 似乎觉得也很有学习的必要  但是我却有一个疑问 :

           WPF与WINFORM开发相比  好处有哪些? 为什么你开发一个WINDOW程序要选择WPF而不是WINFORM呢?

这点对我很重要! 我希望是以你……


I'll talk about this tonight. --------------------编程问答-------------------- 楼主啊,赶紧回归正题吧。。。怎么说着说着又跑题了啊。。。。~~~~(>_<)~~~~  --------------------编程问答-------------------- --------------------编程问答--------------------
引用 84 楼 thlwbsgz 的回复:
楼主啊,赶紧回归正题吧。。。怎么说着说着又跑题了啊。。。。~~~~(>_<)~~~~


好的好的,我会尽快更新。 --------------------编程问答-------------------- 楼主的例子很好,但是处理学生数据的代码写得比较长,推荐使用PDF.NET数据开发框架,在数据处理上能够节省很多代码。 --------------------编程问答-------------------- 别人分享东西,你看不惯可以自己去发帖,我们也会认真地学习,不要在这说这说那的,请不要再嘴上证明自己有NB,NB的体现不在于嘴上,OK?!现在ZG怎么都是些这种人啊,只会嘴上功夫。。。。 --------------------编程问答--------------------
引用 80 楼 blackeye2004 的回复:
LZ,我想问下,WPF以后会怎么发展啊,用WPF还得装framework这么大的东西,你说这个拿来做客户端实用吗?

谢谢你支持。
WPF以后会怎么发展。。。。这个我也没法说,但是我觉得它日后会更加成熟,当用户硬件不断升级,.net framework不断升级后,它会逐渐在大部分方面取代WinForm,成为Desktop编程的主要技术。另外,客户端的问题,如果你说的网络应用程序的客户端,那WPF的framework太大了,肯定不行。不过WPF有“网络版”的,也就是Silver Light,它是轻量级的,语法跟WPF没什么差别。

不过话又说回来,WPF也可以做分布式开发,只是客户端需要装一个好多MB的WPF程序罢了,这种东西是给特定客户用的。我目前搞的就是一个WPF+Microsoft Synchronization framework的分布式系统软件,我们的客户是需要装客户端软件的。


引用 83 楼 ktei2008 的回复:
引用 81 楼 zjx198934 的回复:

非常的支持LZ  也希望楼主能够多给大家上点儿课!    .NET平台上的技术就WPF我不是很了解  以为公司原因本来目前也还没有打算去学习  但是看到LZ的帖子 似乎觉得也很有学习的必要  但是我却有一个疑问 :

WPF与WINFORM开发相比  好处有哪些? 为什么你开发一个WINDOW程序要选择WPF而不是WINFORM呢?

……

谢谢你的支持。其实这个贴子本身并不是讲WPF的,而是讲MVVM。MVVM是一种模式,所以它与WPF并无直接关系,而且MVVM也非常适合开发SilverLight和WP7。我之所以用WPF作例子是因为我自己从事WPF相关开发,所以对它更熟悉一些。

至于你说的WPF和WinForm开发相比,哪个更好些——这个问题很难讲。不过WPF能做的,理论上WinForm都能实现,但是其二者的方法和开发时间是不同的。而且WPF不能在旧的framework(比如.net 2.0)上跑,而且它本身对硬件的需求也比WinForm要高一些。作为开发人员,我觉得凡事从需求出发会比较好一点,而不是单纯从技术角度思考。不过WPF确实有一些地方比WinForm要高明,比如它的数据绑定,非常灵活,又比如它的Template,Style等,可以让你几乎在不用后台代码支持的情况下,只需要在XAML里就可以重绘出所有控件。我没法回答你太多,不如你自己试试WPF,然后自己比较一下。不过有一点我可以肯定:WPF的思想非常不错,值得借鉴。
--------------------编程问答--------------------
引用 87 楼 bluedoctor 的回复:
楼主的例子很好,但是处理学生数据的代码写得比较长,推荐使用PDF.NET数据开发框架,在数据处理上能够节省很多代码。


感谢你的建议。是这样的:我的初衷其实是用Entity Framework做数据层来讲解,但是我觉得这样一来要引入新的框架的学习,初学者消化不了。想来想去我决定用一个大家都比较熟悉的东西(ADO.NET)做数据层。其实目前这个数据层非常简陋,它也只是帮助我进行后面的讲解,我的重点还是在MVVM上。  --------------------编程问答-------------------- 顶一下!!!!!
集成电路测试仪 http://www.art18.net/dcpower/电路板故障检测仪 http://www.art18.net/labpower/ 
电能质量分析仪 http://www.art18.net/smartpower/ 
谐波分析仪 http://www.art18.net/smartload/ 
谐波测试仪 http://www.art18.net/power/ --------------------编程问答-------------------- 很感谢大家的支持。我先说点废话,发这个贴子的原因是前些日子我看了一个讲解MVVM的视频,地址在这里:http://channel9.msdn.com/Events/MIX/MIX11/OPN03 这是MSDN的Channel9上的视频,主讲人是Laurent Bugnion,他基本上一个人写了Mvvm Light Toolkit框架——一个专门用于MVVM开发的轻量级框架。如果你去看这个视频,你会发现,他在做代码示范的时候,左边窗口式Expression Blend,右边是VS2010,他用Expression Blen设计界面,可以做到几乎完全不动VS里的代码,他也可以做到改写VS里的代码,却几乎不改Blend里面的XAML——也就是说他的代码分层已经到达一定境界了。看完之后我挺感叹,我觉得有必要写点东西来介绍一点这方面的皮毛,让更多人有个了解,这样CSDN里就会有更多关于MVVM方面的讨论了,不是挺好么?好了,题外话,下面进入主题。今天要讲的是:数据的增加(Create)。

如果大家觉得程序很不完善,请不要着急,我们一开始会用一些比较“恶心”的办法来做实现一些功能,往后我们会再回来,用更好的方法重构我们的代码,为的是让各位有个对比。所以,你们就先忍着吧!

首先打开你的VS,打开我们的MvvmTutorial工程。今天要教的东西对新手来说稍微有点难理解,不过别担心,自己动手做一做就知道了。昨天的代码如果你编译一下,编译器会报错,原因在于StudentDesignDataService这个类有错误,这是由于我们修改了Student类而引起的,所以你目前可以把StudentDesignDataService直接整个注释掉——我们暂不需要它。我们首先为StudentViewModel增加一个无参的构造函数,代码如下:

        public StudentViewModel()
        {
            Student newStudent = Student.CreateNewStudent(string.Empty, 18, Gender.Male, string.Empty);
            _student = newStudent;
        }
...
注意,往后我可能不会一下贴所有代码,字数不够用。这个无参构造函数的作用是:当我们想添加一个新的Student时,我们便会调用它——往下看你就明白了。此外,我们还要在StudentViewModel中添加一个Save方法,代码如下:

public void Save()
        {
            DataRepository.GetInstance().SaveStudent(_student);
            _student.IsNew = false;
        }

注意此方法是public的,也就是说外界将调用此方法来保存当前的student实例。
接下来,我们要添加一个新类,叫MainViewModel,它是支持MainWindow的ViewModel。请在MvvmTutorial.ViewModel下添加一个MainViewModel,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MvvmTutorial.Model;

namespace MvvmTutorial.ViewModel
{
    public class MainViewModel
    {
        #region Fields
        private IList<StudentViewModel> _all;
        #endregion // Fields

        #region Properties
        public IList<StudentViewModel> All
        {
            get
            {
                if (_all == null)
                {
                    _all = new List<StudentViewModel>();
                    foreach (var student in DataRepository.GetInstance().GetStudents())
                    {
                        _all.Add(new StudentViewModel(student));
                    }
                }
                return _all;
            }
        }
        #endregion // Properties
    }
}
注意MainViewModel向外界提供了一个All属性,它包含了所有的StudentViewModel。还要注意的是,All这个属性的类型是IList<>,我们等一下将看到用这个类型会有一个问题。好了,话说到这儿,既然有了新的ViewModel,我们需要改一下MainWindow.xaml了,修改如下:

<Window x:Class="MvvmTutorial.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="480" Width="640">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Menu>
            <MenuItem Header="File">
                <MenuItem x:Name="NewMenuItem" Header="New" Click="NewMenuItem_Click" />
            </MenuItem>
        </Menu>
        <ListView ItemsSource="{Binding All, Mode=OneWay}" Grid.Row="1">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Name">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Name, Mode=OneWay}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Age">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Age, Mode=OneWay}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Gender">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Gender, Mode=OneWay}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Description">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Description, Mode=OneWay}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>
注意到:大部分代码未变,不过我们添加了一个Menu,更重要的是:我们的ItemsSource的绑定变成了ItemsSource="{Binding All, Mode=OneWay}",也就是说,我们将把它绑定到MainViewModel的All属性上,因此,我们需要改一下App.xaml.cs中的代码,修改如下:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
using MvvmTutorial.ViewModel;

namespace MvvmTutorial
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        #region Fields
        private MainViewModel _mainVm;
        #endregion // Fields

        #region Protected Methods
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            
            var mainWindow = new MainWindow();
            Application.Current.MainWindow = mainWindow;

            // Set data context
            _mainVm = new MainViewModel();
            mainWindow.DataContext = _mainVm;
            mainWindow.Show();
        }
        #endregion // Protected Methods
    }
}
这样一来MainWindow的DataContext便是MainViewModel实例了。回到MainWindow.xaml中,细心的读者会发现里面有一个事件:<MenuItem x:Name="NewMenuItem" Header="New" Click="NewMenuItem_Click" />没错,这个事件关联到MainWindow.xaml.cs,所以我们打开此文件,添加如下方法:

#region Event Handlers
        private void NewMenuItem_Click(object sender, RoutedEventArgs e)
        {
            StudentView studentView = new StudentView();

            // Set data context
            StudentViewModel newStudentVM = new StudentViewModel();
            studentView.DataContext = newStudentVM;
            studentView.ShowDialog();

            if (studentView.DialogResult == true)
            {
                (this.DataContext as MainViewModel).All.Add(newStudentVM);
            }
        }
        #endregion // Event Handlers

你又会发现,这个方法里多了个StudentView——我们下面就要在MvvmTutorial下添加一个Window项目,名叫StudentView,它的DataContext将是StudentViewModel,其代码如下:

<Window x:Class="MvvmTutorial.StudentView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="StudentView" Height="300" Width="300">
    <Window.BindingGroup>
        <BindingGroup />
    </Window.BindingGroup>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Label Content="Name:" VerticalAlignment="Center" />
        <TextBox Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=Explicit}" Grid.Column="1" Height="23" />
        <Label Content="Age:" VerticalAlignment="Center" Grid.Row="1" />
        <TextBox Text="{Binding Age, Mode=TwoWay, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="1" Height="23" />
        <Label Content="Gender:" VerticalAlignment="Center" Grid.Row="2" />
        <ComboBox ItemsSource="{Binding GenderOptions, Mode=OneTime}" SelectedValuePath="ValueMember" DisplayMemberPath="DisplayMember" SelectedValue="{Binding Gender, Mode=TwoWay}" 
                  Grid.Column="1" Grid.Row="2" Height="23" />
        <TextBox Text="{Binding Age, Mode=TwoWay, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="1" Height="23" />
        <Label Content="Description:" VerticalAlignment="Center" Grid.Row="3" />
        <TextBox Text="{Binding Description, Mode=TwoWay, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="3" VerticalAlignment="Stretch" AcceptsReturn="True" TextWrapping="Wrap"
                 HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" />
        <StackPanel Orientation="Horizontal" Grid.ColumnSpan="2" Grid.Row="4" HorizontalAlignment="Right" Margin="0,5,0,0">
            <Button x:Name="OKButton" Content="OK" IsDefault="True" Width="75" Height="23" Margin="0,0,5,0" Click="OKButton_Click" />
            <Button x:Name="CancelButton" Content="Cancel" Width="75" Height="23" Click="CancelButton_Click" />
        </StackPanel>
    </Grid>
</Window>
另外,StudentView.xaml.cs的代码如下: (未完待续)
[code=C#] --------------------编程问答-------------------- 紧接着上面:StudentView.xaml.cs里的代码如下

using System.Windows;
using MvvmTutorial.ViewModel;

namespace MvvmTutorial
{
    /// <summary>
    /// Interaction logic for StudentView.xaml
    /// </summary>
    public partial class StudentView : Window
    {
        #region Constructors
        public StudentView()
        {
            InitializeComponent();
        }
        #endregion // Constructors

        #region Properties
        private StudentViewModel StudentVM
        {
            get
            {
                return this.DataContext as StudentViewModel;
            }
        }
        #endregion // Properties

        #region Event Handlers
        private void OKButton_Click(object sender, RoutedEventArgs e)
        {
            this.BindingGroup.CommitEdit();
            StudentVM.Save();
            this.DialogResult = true;
            this.Close();
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }
        #endregion // Event Handlers
    }
}

好了,现在差不多齐活了,不过请注意在StudentView.xaml里:

<ComboBox ItemsSource="{Binding GenderOptions, Mode=OneTime}" SelectedValuePath="ValueMember" DisplayMemberPath="DisplayMember" SelectedValue="{Binding Gender, Mode=TwoWay}" 
                  Grid.Column="1" Grid.Row="2" Height="23" />

这是个有点绕的地方:意思大体是,此ComboBox的可选项目(ItemsSource)被绑定到了DataContext的GenderOptions,而每一项的显示值为该项目的DisplayMember属性,数据值为该项目的DisplayMember,此ComboBox被选的项目则被绑定到DataContext的Gender属性。在这里,整个DataContext就是StudentViewModel,而StudentViewModel似乎还没有GenderOptions属性,所以我们要加上它:

private static object[] _genderOptions; // 添加此变量
...
        public object[] GenderOptions // 然后添加这个属性
        {
            get
            {
                if (_genderOptions == null)
                {
                    _genderOptions = new object[]
                    {
                        new { DisplayMember = Gender.Male.ToString(), ValueMember = Gender.Male },
                        new { DisplayMember = Gender.Female.ToString(), ValueMember = Gender.Female }
                    };
                }
                return _genderOptions;
            }
        }


最后,编译,执行。试着用现在的程序添加一个新的Student。你会发现一个问题:怎么点了OK之后主窗口中的Student表不刷新呢?但是如果你重新运行程序,或者直接察看XML文件,你会发现这个Student明明已经被加进去了。问题来了:如何让界面和ViewModel保持同步呢?这将是一个比较大的内容,我下回讲,今天先到这里。另外,对于那些Click事件,暂时先如此——这不是很好的编程方式,以后我们会用到Command,不过现在我们先不去管它们。 --------------------编程问答-------------------- 各位,实在不好意思,忘记了一点:有人会注意到DataRepository无缘无故跑出一个GetInstance方法,其实是我忘贴这段代码了,DataRepository的构造函数修改如下:

        #region Constructors
        public static DataRepository GetInstance()
        {
            if (_instance == null)
            {
                _instance = new DataRepository();
            }
            return _instance;
        }

        private DataRepository()
        {
            this.CreateDataSource();

            if (!File.Exists(PhysicalFilePath))
            {
                this.AddDefaultStudents();
                this.Save();
            }
            else
            {
                this.Load();
            }
        }
        #endregion // Constructors

这就是一个简单的单例模式而已——我只是想确保程序中只存在一个DataRepository。 --------------------编程问答-------------------- 继续支持~~~
发现楼主真是好耐心的一个人,是不是大牛都必须先得心性比较好啊~~ --------------------编程问答--------------------
引用 95 楼 xxc3303 的回复:
继续支持~~~
发现楼主真是好耐心的一个人,是不是大牛都必须先得心性比较好啊~~

哈哈,多谢夸奖。我不是啥大牛,而且我也挺容易跟人家打口水仗——你看看我和weizisheng131083那些贴子。不过编程的时候我还算有耐性。希望这个贴子对你又帮助,谢谢支持。 --------------------编程问答-------------------- CSDN竟然不能编辑自己发的贴子,这明显是开发人员太懒了,连个if-else都懒得加。。。。我今天写的东西有个地方有笔误,原文本为:
而每一项的显示值为该项目的DisplayMember属性,数据值为该项目的DisplayMember,此ComboBox被选的项目则被绑定到DataContext的Gender属性。

应修改为:
而每一项的显示值为该项目的DisplayMember属性,数据值为该项目的ValueMember,此ComboBox被选的项目则被绑定到DataContext的Gender属性。
红色为修改部分。说实话,这论坛的硬件确实太差了…… --------------------编程问答-------------------- 另外,Student类的IsNew属性的Setter,原本是private的,但是现在已经变为public的了。也就是说,代码如下:

public IsNew
{
    get;
    set;
}

这么做的原因是:在StudentViewModel的Save函数中,执行到末尾的时候,其_stuent已经被保存了,所以IsNew不管原先是什么,都应该被设为false,否则DataRepository每次都会将其当作新的Student来添加到Students table中。而Student的IsNew的Setter必须是public的,才可以被StudentViewModel来调用,因为Student和StudentViewModel属于不同的命名空间——这么说你应该能理解了。 --------------------编程问答-------------------- 好帖,要支持啊 --------------------编程问答-------------------- lz一段一段的码那么多文字,真挺不容易的,希望像lz这样的人越多越好。
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,