zoukankan      html  css  js  c++  java
  • WPF之MVVM模式讲解

    WPF技术的主要特点是数据驱动UI,所以在使用WPF技术开发的过程中是以数据为核心的,WPF提供了数据绑定机制,当数据发生变化时,WPF会自动发出通知去更新UI。

    恰当的模式可以让我们轻松达到“高内聚低耦合”,MVVM就是为WPF量身定做的,该模式充分利用了WPF的数据绑定机制,最大限度地降低了XAML和CS文件的耦合度,即UI显示和逻辑代码的耦合度,如需更换界面时,逻辑代码修改很少,甚至不用修改。与WinForm开发相比,我们一般在后置代码中会使用控件的名字来操作控件的属性来更新UI,而在WPF中通常是数据绑定来更新UI。在响应用户操作上,WinForm是通过控件的事件来处理,而WPF可以使用命令绑定的方式来处理,耦合度将降低。

    我们可以通过下图来理解MVVM模式:

      View,UI界面,即XAML实现的页面,负责与用户交互,接收用户输入,把数据展现给用户。

      ViewModel,一个C# 类,是View的抽象,负责收集需要绑定的数据和命令,帮助View和Model之间的信息转换,将View的Command传送到Model,聚合Model对象,通过View类的DataContent属性绑定到View,同时也可以处理一些UI逻辑。

      Model,数据访问层,就是系统中的对象,可包含属性和行为。

      一般,View对应一个ViewModel,ViewModel可以聚合N个Model,ViewModel可以对应多个View,Model不知道View和ViewModel的存在。

      View与ViewModel连接可通过下面的方式

      (1)Binding Data:实现数据的传递;

      (2)Command:实现操作的调用;

      (3)AttachBehavior:实现控件加载过程中的操作;

    示例讲解

    一、Model

        class ButtonInfo
        {
            public string Content { get; set; }
        }
    ButtonInfo
        class DownLoadFileInfo
        {
            public string url = "";
            public string fileName = "";
    
            public DownLoadFileInfo(string _url, string _fileName)
            {
                url = _url;
                fileName = _fileName;
            }
        }
    DownLoadFileInfo
        class ProgressBarInfo
        {
            public long pbCurrentMaxLength { get; set; }
            public long pbCurrentLength { get; set; }
            public long pbTotalMaxLength { get; set; }
            public long pbTotalLength { get; set; }
            public ProgressBarInfo()
            { }
            public ProgressBarInfo(long pbCurrentMaxLength, long pbCurrentLength, long pbTotalMaxLength, long pbTotalLength)
            {
                this.pbCurrentMaxLength = pbCurrentMaxLength;
                this.pbCurrentLength = pbCurrentLength;
                this.pbTotalLength = pbTotalLength;
                this.pbTotalMaxLength = pbTotalMaxLength;
            }
        }
    ProgressBarInfo

    二、View

    <Window x:Class="AutoUpdate_MVVM.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="201" Width="505">
        <Window.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="GESBrushes.xaml"/>
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Window.Resources>
        <Grid Background="{StaticResource SolidBrushBackground}">
            <Button Content="{Binding Button.Content,Mode=TwoWay}" Command="{Binding Pause}" Name="btnPause" HorizontalAlignment="Left" Margin="108,113,0,0" VerticalAlignment="Top" Width="75"/>
            <Button Content="关闭" Command="{Binding Close}" Name="btnClose" HorizontalAlignment="Left" Margin="251,113,0,0" VerticalAlignment="Top" Width="75" />
            <ProgressBar Value="{Binding ProgressBar.pbCurrentLength,Mode=TwoWay}" Maximum="{Binding ProgressBar.pbCurrentMaxLength}" Name="pbCurrent" HorizontalAlignment="Left" Height="16" Margin="90,32,0,0" VerticalAlignment="Top" Width="355"/>
            <ProgressBar Value="{Binding ProgressBar.pbTotalLength,Mode=TwoWay}" Maximum="{Binding ProgressBar.pbTotalMaxLength}" Name="pbTotal" HorizontalAlignment="Left" Height="16" Margin="90,65,0,0" VerticalAlignment="Top" Width="355"/>
            <Label Content="当前进度:" Foreground="{StaticResource SolidBrushForeground}" Height="28" HorizontalAlignment="Left" Margin="25,25,0,0" Name="label1" VerticalAlignment="Top" FontWeight="Normal" FontStyle="Normal" FontStretch="{Binding}" />
            <Label Content="总 进 度:" Foreground="{StaticResource SolidBrushForeground}"  Height="28" HorizontalAlignment="Left" Margin="25,59,0,0" Name="label2" VerticalAlignment="Top" />
        </Grid>
    </Window>
    MainWindow.xaml
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                this.DataContext = new DownLoadFile();
            }
        }
    MainWindow.xaml.cs
    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <SolidColorBrush x:Key="SolidBrushBackground" Color="#FF3A9692"/>
    </ResourceDictionary>
    GESBrushes.xaml

    三、ViewModel

    (1)DownLoadFile类,一个实现INotifyPropertyChanged接口的类,目的是绑定数据属性。WPF中实现这个接口的类的属性成员才具有通知UI的能力

        class DownLoadFile : INotifyPropertyChanged,IDisposable 
        {
            public event PropertyChangedEventHandler PropertyChanged;
            
            #region  [Object]
            
            ManualResetEvent _pauseEvent = new ManualResetEvent(true);
            List<DownLoadFileInfo> _listFileInfo = new List<DownLoadFileInfo>();
            List<string> _listUrl = new List<string>();
    
            readonly int MAX_BUFFER_SIZE = 512;
    
            long totalCurrentLength = 0;
            long totalLength = 0;
    
            ProgressBarInfo _ProgressBarInfo;
            ButtonInfo _ButtonInfo;
            HttpWebRequest myrq;
            HttpWebResponse myrp; 
            
            #endregion
    
            #region [Property]
    
            public bool IsFinish { get; set; }
    
            private ProgressBarInfo _ProgressBar;
            public ProgressBarInfo ProgressBar
            {
                get
                {
                    return _ProgressBar;
                }
                set
                {
                    _ProgressBar = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("ProgressBar"));
                        ;
                    }
                }
            }
    
            private ButtonInfo _Button;
            public ButtonInfo Button
            {
                get { return _Button; }
                set
                {
                    _Button = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("Button"));
                    }
                }
            }
    
            public ICommand Pause
            {
                get
                {
                    return new PauseCommand(this);
                }
            }
    
            public ICommand Close
            {
                get
                {
                    return new CloseCommand(this);
                }
            }
    
            #endregion
            
            #region DownLoadFile
            public DownLoadFile()
            {
                _ProgressBarInfo = new ProgressBarInfo();
                _ButtonInfo = new ButtonInfo();
                _ButtonInfo.Content = "暂停";
                Button = _ButtonInfo; //注意此次的赋值
    
                _listUrl.Add(@"http://127.0.0.1/孙晓林周报(2014-12-11)1.txt");
                _listUrl.Add(@"http://127.0.0.1/孙晓林周报(2014-12-11)2.txt");
    
                for (int i = 0; i < _listUrl.Count; i++)
                {
                    string url = _listUrl[i];
                    string[] fileNames = url.Split('/');
                    if (fileNames.Length > 0)
                    {
                        string fileName = fileNames[fileNames.Length - 1];
                        string fileFullName = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\" + fileName;//文件保存路径
    
                        DownLoadFileInfo fileInfo = new DownLoadFileInfo(_listUrl[i], fileFullName);
                        _listFileInfo.Add(fileInfo);
                    }
                }
    
                for (int i = 0; i < _listFileInfo.Count; i++)
                {
                    HttpWebRequest myrq = (HttpWebRequest)HttpWebRequest.Create(_listFileInfo[i].url);
                    HttpWebResponse myrp = (HttpWebResponse)myrq.GetResponse();
                    totalLength += myrp.ContentLength;
                }
                //下载文件
                Thread newThread = new Thread(new ThreadStart(PerformDownloading));
                newThread.Start();
    
            } 
            #endregion
    
            #region PerformDownloading
            /// <summary>
            /// 下载文件
            /// </summary>
            private void PerformDownloading()
            {
                try
                {
                    if (_listFileInfo != null)
                    {
                        for (int i = 0; i < _listFileInfo.Count; i++)
                        {
                            string url = _listFileInfo[i].url;
                            string fileName = _listFileInfo[i].fileName;
                            _ProgressBarInfo.pbCurrentLength = 0;
                            //System.Net.ServicePointManager.DefaultConnectionLimit = 50;
                            System.GC.Collect();
                            myrq = (HttpWebRequest)HttpWebRequest.Create(url);
                            myrq.KeepAlive = false;
                            myrp = (HttpWebResponse)myrq.GetResponse();
    
                            _ProgressBarInfo.pbCurrentMaxLength = myrp.ContentLength;
                            _ProgressBarInfo.pbTotalMaxLength = totalLength;
    
                            System.IO.Stream st = myrp.GetResponseStream();
                            System.IO.Stream so = new System.IO.FileStream(fileName, System.IO.FileMode.Create);
                            long totalDownloadedByte = 0;
    
                            byte[] buffer = new byte[MAX_BUFFER_SIZE];
                            int osize = 0;
    
                            while (true)
                            {
                                _pauseEvent.WaitOne();//阻止当前线程,直到当前 WaitHandle 收到信号。
                                osize = st.Read(buffer, 0, MAX_BUFFER_SIZE);
                                totalDownloadedByte += osize;
                                totalCurrentLength += osize;
                                _ProgressBarInfo.pbCurrentLength = totalDownloadedByte;
                                _ProgressBarInfo.pbTotalLength = totalCurrentLength;
                                ProgressBar = _ProgressBarInfo;
                                Thread.Sleep(1);
                                if (osize == 0)
                                {
                                    break;
                                }
                                so.Write(buffer, 0, osize);
                            }
                            so.Close();
                            st.Close();
    
                            CloseHttpWebObject();
                        }
                        IsFinish = true;
                    }
                }
                catch (System.Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            } 
            #endregion
    
            #region PauseDownLoad
            public void PauseDownLoad()
            {
    
                if (Button.Content == "暂停")
                {
                    _ButtonInfo.Content = "继续";
                    Button = _ButtonInfo;
                    _pauseEvent.Reset();
                }
                else
                {
                    _ButtonInfo.Content = "暂停";
                    Button = _ButtonInfo;
                    _pauseEvent.Set();
                }
            } 
            #endregion
    
            #region IDisposable 成员
    
            public void Dispose()
            {  
                _pauseEvent.Close();//释放由当前 WaitHandle 持有的所有资源。
    
                for (int i = 0; i < _listFileInfo.Count; i++)
                {
                    _listFileInfo[i] = null;
                }
                CloseHttpWebObject();
                Application.Current.Shutdown();
            }
    
            private void CloseHttpWebObject()
            {
                if (myrq != null)
                {
                    myrq.Abort();
                }
                if (myrp != null)
                {
                    myrp.Close();
                }
            }
    
            #endregion
        }

    (2)CloseCommand类和PauseCommand类,实现ICommand接口的类,目的是绑定命令属性。WPF中实现ICommand接口的类才能作为命令绑定到UI

     class CloseCommand:ICommand
        {
             private DownLoadFile _DownLoadFile;
             public CloseCommand(DownLoadFile downLoadFile)
            {
                _DownLoadFile = downLoadFile;
            }
             #region Achieve Items
             public bool CanExecute(object parameter)//定义用于确定此命令是否可以在当前状态下执行的方法,如果可以执行此命令,返回true,否则返回false。
             {
                 return true;
             }
    
             public event EventHandler CanExecuteChanged;//当出现影响是否执行该命令的更改时发生
    
             public void Execute(object parameter)//定义在调用此命令时调用的方法
             {
                 _DownLoadFile.Dispose();
             } 
             #endregion
        }
    CloseCommand
     class PauseCommand : ICommand
        {
            private DownLoadFile _DownLoadFile;
            public PauseCommand(DownLoadFile downLoadFile)
            {
                _DownLoadFile = downLoadFile;
            }
            public bool CanExecute(object parameter)
            {
                if (_DownLoadFile.IsFinish)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
    
            public event EventHandler CanExecuteChanged;
    
            public void Execute(object parameter)
            {
                _DownLoadFile.PauseDownLoad();
            }
        }
    PauseCommand

    二、

  • 相关阅读:
    涡轮增压中冷器
    TortoiseSVN文件及文件夹图标不显示解决方法
    无线路由器无法登录管理界面,怎么办?
    TP-LINK-TL-WVR450G路由器经常断网
    阿里云DNS地址
    农村社保和职工社保能同时交吗?
    电视机顶盒遥控器可以同时遥控电视和机顶盒
    各种汽车的高端品牌
    金蝶云星空系统打印提示:对象不支持“SetGraphicsMode”属性或方法
    怎么去掉视频的背景音乐?
  • 原文地址:https://www.cnblogs.com/SunXiaoLin/p/4221117.html
Copyright © 2011-2022 走看看