使用MvvmFoundation框架开发WPF应用程序

 约 27 分钟

使用MvvmFoundation框架开发WPF应用程序

本文意在通过创建MvvmFoundationDemo的演示程序,对于使用MvvmFoundation框架及WPF公用库进行WPF应用程序开发进行向导式演示

创建项目

新建WPF应用程序

新建WPF应用

添加MvvmFoundation框架到当前项目

此步非必需,只是为了方便在实际的项目中,可以根据项目的需要对MvvmFoundation框架进行一些修改,以适应项目需要,一般情况下不需要修改。且添加本框架可以加深对MvvmFoundation的底层原理了解,方便调试。
添加MvvmFoundation框架
添加MvvmFoundation框架

添加MvvmFoundation.wpf.dll引用

MVVMFoundation中有几个比较重要的概念:

  • 1、Messenger:这里主要用在各种不同的ViewModel之间通信(比如相互关联的ViewModel、主从ViewModel等),当然也可以扩展成ViewModel与View之间进行通信。

  • 2、ObservableObject:这里相当于ViewModelBase的概念,每一个ViewModel继承自该类,调用完成之后立即释放,防止内存泄露。

  • 3、PropertyObserver:主要是对INotifyPropertyChanged.PropertyChanged进行封装,可以通过其对某个对象的属性变更注册回调函数,当属性变更时便触发回调函数。

  • 4、RelayCommand接口:封装command的声明,包括execution执行逻辑,可选的can-execute逻辑等。外部只需要实例化并Binding就可以简单使用。

添加引用)

分别创建Model、View、ViewModel文件夹

  • The Model is the entity that represents the business concept; it can be anything from a simple customer entity to a complex stock trade entity .

  • The View is the graphical control or set of controls responsible for rendering the Model data on screen .A View can be a WPF window, a Silverlight page, or just an XAML data template control .

  • The ViewModel is the magic behind everything .The ViewModel contains the UI logic, the commands, the events, and a reference to the Model .

  • In MVVM, the ViewModel is not in charge of updating the data displayed in the UI—thanks to the powerful data-binding engine provided by WPF and Silverlight, the ViewModel doesn’t need to do that .This is because the View is an observer of the ViewModel, so as soon as the ViewModel changes, the UI updates itself .For that to happen, the ViewModel must implement the INotifyPropertyChangedinterface and fire the PropertyChangedevent .

添加MvvmFoundation框架

根据需要创建Lib、Converter、Resources文件夹并添加项

  • Lib:存放常用到的公共类,如DbgTrace,IniFile, ObservableCollectionEx等

  • Converter:存放转换器

  • Resources:存放项目中WPF控件及窗体的资源文件

项目开发

编写Model

 public class UserInfo
{
    private string userName;

    public string UserName
    {
        get { return userName; }
        set { userName = value; }
    }

    private uint age;

    public uint Age
    {
        get { return age; }
        set { age = value; }
    }

    private Sex sex;

    public Sex Sex
    {
        get { return sex; }
        set { sex = value; }
    }

    private string userImage;

    public string UserImage
    {
        get { return userImage; }
        set { userImage = value; }
    }
}

编写View

View显示

<UserControl x:Class="MvvmFoundationDemo.View.UserInfoView"
         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:converter="clr-namespace:MvvmFoundationDemo.Converter"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Control.Resources>
    <converter:SexToMaleConverter x:Key="SexToMaleConverter"/>
    <converter:SexToFemaleConverter x:Key="SexToFemaleConverter"/>
    <converter:BoolVisiableConverter x:Key="BoolVisiableConverter"/>
</Control.Resources>
    <Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="2*"/>
    </Grid.RowDefinitions>
    <Button Content="浏览..." Style="{StaticResource ResourceKey= StepButtonStyle}" Command="{Binding BrowseImageCommand}" Width="80" Height="30" HorizontalAlignment="Right" Margin="10" Visibility="{Binding IsReadOnly,Converter={StaticResource ResourceKey=BoolVisiableConverter}}"/>
    <Image Source="{Binding UserImage}" HorizontalAlignment="Center" Grid.ColumnSpan="4" Width="100" Height="100" Style="{StaticResource ResourceKey=GeneratingImageStyle}" />
    <Grid Grid.Row="1" Grid.RowSpan="1" Grid.ColumnSpan="4">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="225"/>
        </Grid.ColumnDefinitions>
        <TextBlock Text="姓名" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" Style="{StaticResource ResourceKey=TextBlockStyle}"/>
        <TextBlock Text="性别" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Style="{StaticResource ResourceKey=TextBlockStyle}"/>
        <TextBlock Text="年龄" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" Style="{StaticResource ResourceKey=TextBlockStyle}"/>
        <TextBlock Text="姓名" Grid.Row="4" HorizontalAlignment="Center" VerticalAlignment="Center" Style="{StaticResource ResourceKey=TextBlockStyle}"/>
        <TextBox Text="{Binding UserName}" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center"  Margin="25,0" Width="80" IsReadOnly="{Binding IsReadOnly}" Style="{StaticResource ResourceKey=TextBoxStyle}"/>
        <StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal">
            <RadioButton HorizontalAlignment="Left" VerticalAlignment="Center" Content="男" IsChecked="{Binding Sex,Converter={StaticResource ResourceKey=SexToMaleConverter}}" Margin="25,0" Foreground="{StaticResource ResourceKey=ForegroundStyle}" Style="{StaticResource ResourceKey=TimeTypeRadioButton}"/>
            <RadioButton HorizontalAlignment="Left" VerticalAlignment="Center" Content="女" IsChecked="{Binding Sex,Converter={StaticResource ResourceKey=SexToFemaleConverter}}" Margin="25,0" Foreground="{StaticResource ResourceKey=ForegroundStyle}"/>
        </StackPanel>
        <TextBox Text="{Binding Age}" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="25,0" Width="35" IsReadOnly="{Binding IsReadOnly}" Style="{StaticResource ResourceKey=TextBoxStyle}"/>
        <TextBox Text="{Binding UserName}" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="25,0" Width="80" Style="{StaticResource ResourceKey=TextBoxStyle}"/>
    </Grid>
</Grid>
</UserControl>

编写ViewModel

需要继承MvvmFoundation框架的ObservableObject类。

 public class UserInfoViewModel:ObservableObject
{
    private UserInfo user;

    private bool isReadOnly;

    public bool IsReadOnly
    {
        get { return isReadOnly; }
        set
        {
            isReadOnly = value;
            OnPropertyChanged("IsReadOnly");
        }
    }

    public UserInfoViewModel()
    {
        this.user = new UserInfo();
        this.UserImage = @"..\Pictures\DefaultUser.png";
    }

    public string UserName
    {
        get { return this.user.UserName; }
        set
        {
            this.user.UserName = value;
            OnPropertyChanged("UserName");
        }
    }

    public uint Age
    {
        get { return this.user.Age; }
        set
        {
            this.user.Age = value;
            OnPropertyChanged("Age");
        }
    }

    public Sex Sex
    {
        get { return this.user.Sex; }
        set
        {
            this.user.Sex = value;
            OnPropertyChanged("Sex");
        }
    }

    public string UserImage
    {
        get { return this.user.UserImage; }
        set 
        {
            this.user.UserImage = value;
            OnPropertyChanged("UserImage");
        }
    }

    public ICommand BrowseImageCommand
    {
        get { return new RelayCommand(BrowseImage); }
    }

    private void BrowseImage()
    {
        OpenFileDialog open = new OpenFileDialog();
        open.Filter = "Image(*.jpg;*.bmp;*.png)|*.jpg;*.bmp;*.png";
        bool openResult = (bool)open.ShowDialog();
        if (openResult)
            this.UserImage = open.FileName;
    }
}

App.xaml设置

  1. 引用WPF样式库中的样式

    • 首先,需要确定要引用的资源是App级别,还是Window级别,还是UserControl级别:

      App级别就需要在App.xaml中引用资源;Window及UserControl级别就需要在对应的.xaml文件中引用对应样式的Generic.xaml资源即可。

          <Application.Resources>
             <ResourceDictionary>
                 <ResourceDictionary.MergedDictionaries>
                     <ResourceDictionary Source="pack://application:,,,/WpfControlsAndStyles;component/ControlStyles/Solid/Generic.xaml" />
                 </ResourceDictionary.MergedDictionaries>
             </ResourceDictionary>
         </Application.Resources>
      
    • 其次,引入资源后,便可通过设置控件的Style来实现对控件样式的更改

         <Button Command="{Binding UpdateCommand}" Content="更新->" Width="80" Height="30" Grid.Row="2" Style="{StaticResource ResourceKey=StepButtonStyle}"/>
         
      
  2. Xaml中注释默认启动窗体

       <Application x:Class="MvvmFoundationDemo.App"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            >
            <!--StartupUri="MainWindow.xaml">-->
    
  3. 设置程序启动窗体

    在App的构造函数中实现对主窗体的创建及显示

       public partial class App : Application
       {
           public static MainWindow MainWindow;
    
           protected override void OnStartup(StartupEventArgs e)
           {
               base.OnStartup(e);
               MainWindow = new MainWindow();
               var MainWindowViewModel = new MainWindowViewModel();
               MainWindow.DataContext = MainWindowViewModel;
               MainWindow.Show();
           }
       }
    
  4. 设置主窗体DataContext

       MainWindow = new MainWindow();
       var MainWindowViewModel = new MainWindowViewModel();
       MainWindow.DataContext = MainWindowViewModel;
    

Mvvm框架应用介绍

OnPropertyChange

用于对ViewModel中的属性改变通知到绑定的View对应的控件。

  1. OnPropertyChange属性通知定义

            
        public string UserName
        {
            get { return this.user.UserName; }
            set
            {
                this.user.UserName = value;
                OnPropertyChanged("UserName");
            }
        }
    
  2. 属性在View界面的绑定

         <TextBox Text="{Binding UserName}" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center"  Margin="25,0" Width="80" IsReadOnly="{Binding IsReadOnly}" Style="{StaticResource ResourceKey=TextBoxStyle}"/>
    

RelayCommand

用于在ViewModel中定义View中绑定的事件。

  1. 在ViewModel中定义RelayCommand

           public ICommand BrowseImageCommand
        {
            get { return new RelayCommand(BrowseImage); }
        }
    
        private void BrowseImage()
        {
            OpenFileDialog open = new OpenFileDialog();
            open.Filter = "Image(*.jpg;*.bmp;*.png)|*.jpg;*.bmp;*.png";
            bool openResult = (bool)open.ShowDialog();
            if (openResult)
                this.UserImage = open.FileName;
        }
    
  2. 在View中的按钮管理该Command

        <Button Content="浏览..." Style="{StaticResource ResourceKey= StepButtonStyle}" Command="{Binding BrowseImageCommand}" Width="80" Height="30" HorizontalAlignment="Right" Margin="10" Visibility="{Binding IsReadOnly,Converter={StaticResource ResourceKey=BoolVisiableConverter}}"/>
    

Messenger

可用于ViewModel之间的信息传递,可以用于ViewModel和View之间的信息传递。

  1. 定义信息传输类,非必需,建议

        public class ViewModelCommunication
        {
            static ViewModelCommunication()
            {
                Messaging = new Messenger();
            }
     
            public static Messenger Messaging { get; set; }
     
            public static string DataIDInChanged { get { return "DataIDInChanged"; } }
        }
    
  2. 在需要通知的类中注册要通知的信息

           ViewModelCommunication.Messaging.Register(ViewModelCommunication.DataIDInChanged,(Action<string>)(param => SetLastSelectedDataID(param))); 
    
  3. 当对应的消息出现时,通知已经注册的类

        ViewModelCommunication.Messaging.NotifyColleagues(ViewModelCommunication.DataIDInChanged, en.Datalog.DataID.ToString()); 
    

PropertyOberver

主要用于对对象的属性监听,属性变更后可触发已注册的回调函数。

  1. 注册要监听对象的属性及回调函数

        PropertyObserver<UserInfoViewModel> userInfoAfterObserver;
        public MainWindowViewModel()
        {
            UserInfoBefore = new UserInfoViewModel();
            UserInfoBefore.IsReadOnly = false;
            UserInfoAfter = new UserInfoViewModel();
            UserInfoAfter.IsReadOnly = true;
            userInfoAfterObserver = new PropertyObserver<UserInfoViewModel>(UserInfoAfter)
                .RegisterHandler(UserInfo => UserInfo.Age, this.AgeChangedCallback);
        }
    
  2. 实现回调函数

        private void AgeChangedCallback(UserInfoViewModel userInfo)
        {
            MessageBox.Show("Property Age changed"); 
        } 
    

项目演示

效果图

效果图

阅读 2.8k

推荐阅读
天地一码农
用户专栏

分享个人平时工作、学习、生活心得体会;记录人情冷暖,事事非非

0 人关注
4 篇文章
专栏主页
目录