WPF中MVVM结构以及Command绑定到事件(WPF写七牛云空间管理工具)

项目简介

本项目想写一个七牛云空间管理的工具,省去每次都要登录网页管理的繁琐,也是为了学习一下WPF开发的MVVM架构和思想,降低代码耦合度(降低UI层和BLL层的代码耦合度)。前后端只要约定好命令名和参数即可,让搞UI的专心搞UI,搞后台的专心写后台逻辑。

项目主要架构

考虑到对XP系统的友好支持,使用了.Net 4

  • MahApps.Metro
  • PropertyChanged.Fody
  • Microsoft.Bcl
  • Qiniu

新建项目QiniuDiskManager

在这里插入图片描述

NuGet安装

Install-Package MahApps.Metro
Install-Package MahApps.Metro.SimpleChildWindow
Install-Package MahApps.Metro.IconPacks -Version 2.3.0
Install-Package Microsoft.Bcl -Version 1.1.8
Install-Package Microsoft.Bcl.Async -Version 1.0.168
Install-Package Microsoft.Bcl.Build -Version 1.0.14
Install-Package Newtonsoft.Json -Version 11.0.1
Install-Package ControlzEx -Version 3.0.2.4
Install-Package Qiniu
Install-Package PropertyChanged.Fody

新建项目结构文件夹

  • Bases
  • Commands
  • Models
  • Pages
  • ViewModels

新建必要文件

  • ViewModelBase.cs
  • DelegateCommand.cs
using System.ComponentModel;

namespace QiniuDiskManager.Bases
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;

namespace QiniuDiskManager.Commands
{
    internal class DelegateCommand : ICommand
    {
        private readonly Action _execute;
        private readonly Func<bool> _canExecute;

        public DelegateCommand(Action execute) : this(execute, null) { }
        public DelegateCommand(Action execute, Func<bool> canExecute)
        {
            _execute = execute ?? throw new ArgumentNullException(nameof(execute));
            _canExecute = canExecute;
        }

        public void Execute(object parameter)
        {
            _execute();
        }
        public bool CanExecute(object parameter)
        {
            if (_canExecute == null) return true;
            return _canExecute();
        }


        public event EventHandler CanExecuteChanged
        {
            add => CommandManager.RequerySuggested += value;
            remove => CommandManager.RequerySuggested -= value;
        }
    }

    internal class DelegateCommand<T> : ICommand
    {
        private readonly Action<T> _execute;
        private readonly Func<bool> _canExecute;

        public DelegateCommand(Action<T> execute) : this(execute, null) { }
        public DelegateCommand(Action<T> execute, Func<bool> canExecute)
        {
            _execute = execute ?? throw new ArgumentNullException(nameof(execute));
            _canExecute = canExecute;
        }

        public void Execute(object parameter)
        {
            _execute((T)parameter);
        }
        public bool CanExecute(object parameter)
        {
            if (_canExecute == null) return true;
            return _canExecute();
        }


        public event EventHandler CanExecuteChanged
        {
            add => CommandManager.RequerySuggested += value;
            remove => CommandManager.RequerySuggested -= value;
        }
    }
}

应用MahApps.Metro框架

添加资源字典

添加资源字典到项目中,修改App.xaml,在<Application.Resources>中添加对应的资源字典

<Application x:Class="QiniuDiskManager.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:QiniuDiskManager"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

修改MainWindow界面

xaml文件中引用命名空间

xmlns:controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"

MainWindow的根节点由Window修改为controls:MetroWindow

<controls:MetroWindow x:Class="QiniuDiskManager.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
        mc:Ignorable="d"
        Title="Qiniu Disk Manager" Height="450" Width="800" controls:TitleCaps="False" WindowStartupLocation="CenterScreen">
    <!-- 右上角菜单按钮 -->
    <controls:MetroWindow.RightWindowCommands>
        <controls:WindowCommands x:Name="window_cmds">
            <Button x:Name="btn_login_out" Content="退出账号" Padding="0 0 0 0" Cursor="Hand" Margin="5 0 5 0"/>
            <TextBlock x:Name="txt_name" Text="用户:NULL" Margin="5 0 5 0" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock>
        </controls:WindowCommands>
    </controls:MetroWindow.RightWindowCommands>
    <Grid>
        <controls:MetroAnimatedSingleRowTabControl x:Name="tab_shouye" Margin="0 0 0 0" TabStripPlacement="Top">
            <controls:MetroTabItem Header="Files" Tag="-1" Padding="0" controls:ControlsHelper.HeaderFontSize="16">
                <controls:MetroTabItem.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}" Margin="0 0 0 0" Width="{Binding}" Padding="10 5 5 5"></TextBlock>
                    </DataTemplate>
                </controls:MetroTabItem.HeaderTemplate>
                <Frame x:Name="frame_files" Source="./Pages/FilesPage.xaml" Padding="0" Margin="0" NavigationUIVisibility="Hidden" BorderThickness="0 1 0 0" BorderBrush="#1DADD7"></Frame>
            </controls:MetroTabItem>

            <controls:MetroTabItem Header="Downloads" Tag="-1" Padding="0" controls:ControlsHelper.HeaderFontSize="16">
                <controls:MetroTabItem.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}" Margin="0 0 0 0" Width="{Binding}" Padding="5 5 5 5"></TextBlock>
                    </DataTemplate>
                </controls:MetroTabItem.HeaderTemplate>
                <Frame x:Name="frame_downloads" Source="./Pages/DownloadsPage.xaml" Padding="0" Margin="0" NavigationUIVisibility="Hidden" BorderThickness="0 1 0 0" BorderBrush="#1DADD7"></Frame>
            </controls:MetroTabItem>

            <controls:MetroTabItem Header="Config" Tag="-1" Padding="0" controls:ControlsHelper.HeaderFontSize="16">
                <controls:MetroTabItem.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}" Margin="0 0 0 0" Width="{Binding}" Padding="5 5 10 5"></TextBlock>
                    </DataTemplate>
                </controls:MetroTabItem.HeaderTemplate>
                <Frame x:Name="frame_config" Source="./Pages/ConfigPage.xaml" Padding="0" Margin="0" NavigationUIVisibility="Hidden" BorderThickness="0 1 0 0" BorderBrush="#1DADD7"></Frame>
            </controls:MetroTabItem>
        </controls:MetroAnimatedSingleRowTabControl>
    </Grid>
</controls:MetroWindow>

在Pages文件夹下新建页面

  • ConfigPage.xaml
  • DownloadsPage.xaml
  • FilesPage.xaml
<Page x:Class="QiniuDiskManager.Pages.ConfigPage"
      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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
      xmlns:vm="clr-namespace:QiniuDiskManager.ViewModels"
      mc:Ignorable="d" 
      d:DesignHeight="450" d:DesignWidth="800"
      Title="Config">
    <Page.DataContext>
        <vm:ConfigPageModel />
    </Page.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"></RowDefinition>
            <RowDefinition Height="30"></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" x:Name="txt_hello" Text="{Binding ContentText, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Margin="5 0 0 0"></TextBlock>
        <Button Grid.Row="1" Height="26" Margin="5 0 5 0" Content="修改">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <i:InvokeCommandAction Command="{Binding SayHello}" CommandParameter="{Binding ElementName=txt_hello}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
    </Grid>
</Page>
<Page x:Class="QiniuDiskManager.Pages.DownloadsPage"
      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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
      xmlns:vm="clr-namespace:QiniuDiskManager.ViewModels"
      mc:Ignorable="d" 
      d:DesignHeight="450" d:DesignWidth="800"
      Title="DownloadsPage">
    <Page.DataContext>
        <vm:DownloadsPageModel />
    </Page.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"></RowDefinition>
            <RowDefinition Height="30"></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" x:Name="txt_hello" Text="{Binding ContentText, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Margin="5 0 0 0"></TextBlock>
        <Button Grid.Row="1" Height="26" Margin="5 0 5 0" Content="修改">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <i:InvokeCommandAction Command="{Binding SayHello}" CommandParameter="{Binding ElementName=txt_hello}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
    </Grid>
</Page>
<Page x:Class="QiniuDiskManager.Pages.FilesPage"
      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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
      xmlns:vm="clr-namespace:QiniuDiskManager.ViewModels"
      mc:Ignorable="d" 
      d:DesignHeight="450" d:DesignWidth="800"
      Title="FilesPage">
    <Page.DataContext>
        <vm:FilesPageModel />
    </Page.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"></RowDefinition>
            <RowDefinition Height="30"></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" x:Name="txt_hello" Text="{Binding ContentText, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Margin="5 0 0 0"></TextBlock>
        <Button Grid.Row="1" Height="26" Margin="5 0 5 0" Content="修改">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <i:InvokeCommandAction Command="{Binding SayHello}" CommandParameter="{Binding ElementName=txt_hello}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
    </Grid>
</Page>

在ViewModels文件夹下新建页面模型

  • ConfigPageModel.cs
  • DownloadsPageModel.cs
  • FilesPageModel.cs
using QiniuDiskManager.Bases;
using QiniuDiskManager.Commands;
using System.Windows.Controls;
using System.Windows.Input;

namespace QiniuDiskManager.ViewModels
{
    public class ConfigPageModel : ViewModelBase
    {
        public string ContentText { get; set; }

        public ConfigPageModel()
        {
            ContentText = "Config Page";
        }

        public ICommand SayHello
        {
            get
            {
                return new DelegateCommand<TextBlock>((txt) =>
                {
                    ContentText = "Hello " + ContentText;
                });
            }
        }
    }
}
using QiniuDiskManager.Bases;
using QiniuDiskManager.Commands;
using System.Windows.Controls;
using System.Windows.Input;

namespace QiniuDiskManager.ViewModels
{
    public class DownloadsPageModel : ViewModelBase
    {
        public string ContentText { get; set; }

        public DownloadsPageModel()
        {
            ContentText = "Downloads Page";
        }

        public ICommand SayHello
        {
            get
            {
                return new DelegateCommand<TextBlock>((txt) =>
                {
                    ContentText = "Hello " + ContentText;
                });
            }
        }
    }
}
using QiniuDiskManager.Bases;
using QiniuDiskManager.Commands;
using System.Windows.Controls;
using System.Windows.Input;

namespace QiniuDiskManager.ViewModels
{
    public class FilesPageModel : ViewModelBase
    {
        public string ContentText { get; set; }

        public FilesPageModel()
        {
            ContentText = "Files Page";
        }

        public ICommand SayHello
        {
            get
            {
                return new DelegateCommand<TextBlock>((txt) =>
                {
                    ContentText = "Hello " + ContentText;
                });
            }
        }
    }
}

运行效果

单击每个页面的“修改”按钮,实现在原有字符串变量前添加“Hello ”,通过Binding绑定实现对应TextBlock控件的Text内容的通知更新,实现效果如下:
在这里插入图片描述

总结

现在前后台只需要约定好控件的绑定属性名称、命令名称及参数,然后各自写各自的代码,前端只需要修改xaml文件里的内容,后端只需要修改对应ViewModel的cs文件,最后通过版本控制器(SVN或Git)提交自己修改的代码文件,便可以直接运行。比如A提交了xaml文件,B提交了cs文件,然后C从SVN拉下A和B提交的代码后可以直接运行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0564丶Kang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值