zoukankan      html  css  js  c++  java
  • 从零开始搭建Wpf初学篇3-界面模块化

    前言:将控件模块化,本章先不使用prism实现

    第一步:在Views里面新建用户控件IntroduceView,将MainWindow里面的内容拷贝过去;在ViewModels里面新建类IntroduceViewModel,将MainWindowViewModel里面的内容拷贝过去,并将IntroduceViewModel关联到IntroduceView上。

    <Grid>
        <TextBlock HorizontalAlignment="Center" Margin="0,160,0,0" Text="{Binding NoticeText}" TextWrapping="Wrap" VerticalAlignment="Top"/>
        <Button Content="点击我" HorizontalAlignment="Left" Margin="355,236,0,0" VerticalAlignment="Top" Command="{Binding ClickCommand}"/>
    </Grid>
    class IntroduceViewModel : BindableBase
    {
        private string _noticeText = "欢迎来到AIStudio.Wpf.Client,让我们一起从0开始学Wpf框架搭建吧!";
        public string NoticeText
        {
            get { return _noticeText; }
            set
            {
                SetProperty(ref _noticeText, value);
            }
        }

        private ICommand _clickCommand;
        public ICommand ClickCommand
        {
            get
            {
                return this._clickCommand ?? (this._clickCommand = new DelegateCommand(() => this.Click()));
            }
        }

        private void Click()
        {
            MessageBox.Show("HelloWorld, 您点击了一下Button按钮");
        }
    }
        
    this.DataContext = new IntroduceViewModel();

    第二步:MainWindow内容替换成如下:

    <mah:MetroWindow  x:Class="AIStudio.Wpf.Client.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:local="clr-namespace:AIStudio.Wpf.Client"
            xmlns:view="clr-namespace:AIStudio.Wpf.Client.Views"
            xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
            mc:Ignorable="d"
            Title="AIStudio.Wpf.Client" Height="450" Width="800">
      
        <Grid>
            <view:IntroduceView/>
        </Grid>
    </mah:MetroWindow>

    运行,将和之前的效果一样。

    第三步:添加登录窗口,启动的时候显示登录窗口,点击登录后切换到IntroduceView。

    在Views里添加用户控件LoginView,在ViewModels里添加LoginViewModel

    <Grid x:Name="LoginGrid" HorizontalAlignment="Center" VerticalAlignment="Center">
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
            <ColumnDefinition Width="150"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        
        <TextBlock Grid.Row="0" Grid.Column="0" Text="用户名" VerticalAlignment="Center"/>

        <TextBox Grid.Row="0" Grid.Column="1" Margin="2" Text="{Binding UserName}"/>
        
        <TextBlock Grid.Row="1" Grid.Column="0" Text="密码" VerticalAlignment="Center"/>
        <PasswordBox Grid.Row="1" Grid.Column="1" Margin="2"  core:PasswordBoxHelper.Password="{Binding Password,Mode=TwoWay}" />
        <CheckBox Grid.Row="2" Grid.ColumnSpan="2" Content="记住密码" VerticalAlignment="Center" IsChecked="{Binding IsRmembered}" />
        <Button Grid.Row="3" Grid.ColumnSpan="2" Content="登录" Command="{Binding LoginCommand}" IsDefault="True" />
    </Grid>

    注:PasswordBox的绑定需要使用附加属性来辅助,附加属性的实现请看我的源码,或者百度一下。

    public class LoginViewModel : BindableBase
    {
        private string _userName;
        public string UserName
        {
            get { return _userName; }
            set
            {
                SetProperty(ref _userName, value);
            }
        }

        private string _password;
        public string Password
        {
            get { return _password; }
            set
            {
                SetProperty(ref _password, value);

            }
        }

        private bool _isRmembered = true;
        public bool IsRmembered
        {
            get { return _isRmembered; }
            set
            {
                SetProperty(ref _isRmembered, value);
            }
        }

        private ICommand _loginCommand;
        public ICommand LoginCommand
        {
            get
            {
                return this._loginCommand ?? (this._loginCommand = new DelegateCommand(() => this.Login()));
            }
        }

        public Action LoginSuccess;

        private void Login()
        {
            if (!string.IsNullOrEmpty(UserName)  && !string.IsNullOrEmpty(Password))
            {
                MessageBox.Show("登录成功!");
                if (LoginSuccess != null)
                {
                    LoginSuccess();                    
                }
            }
            else
            {
                MessageBox.Show("请输入用户名或密码!");
            }
        }

    }

    可以看到里面还添加了一个LoginSuccess,用来通知MainWindowViewModel改变系统状态。

    第四步:主窗口MainWindow的控件改成ContentControl,以便可以加载不同的控件。

    <Window.Resources>
        <ControlTemplate x:Key="LoginStyle" TargetType="{x:Type ContentControl}">
            <view:LoginView DataContext="{Binding LoginViewModel}"/>
        </ControlTemplate>

        <ControlTemplate x:Key="IntroduceStyle" TargetType="{x:Type ContentControl}">
            <view:IntroduceView/>
        </ControlTemplate>

        <Style x:Key="MainContentStyle" TargetType="{x:Type ContentControl}">
            <Setter Property="Template" Value="{StaticResource LoginStyle}" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding Status}" Value="Introduce">
                    <Setter Property="Template" Value="{StaticResource IntroduceStyle}" />
                </DataTrigger>               
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>        
        <ContentControl  Style="{StaticResource MainContentStyle}"/>
    </Grid>

    注意1:可以看到Template中用DataTrigger触发器,根据系统状态Status,使用不同的内容模板。 注意2: <view:LoginView DataContext="{Binding LoginViewModel}"/>这里DataContext没有使用之前的绑定方法,而是在MainWindowViewModel中new一个LoginViewModel,然后在界面上把LoginView与LoginViewModel关联上。

    第五步:MainViewModel的代码改成如下:

    class MainWindowViewModel: BindableBase
    {
        private string _status = "Login";
        public string Status
        {
            get { return _status; }
            set
            {
                SetProperty(ref _status, value);
            }
        }      

        public LoginViewModel LoginViewModel { get; set; }

        public MainWindowViewModel()
        {
            LoginViewModel = new LoginViewModel();
            LoginViewModel.LoginSuccess += LoginSuccess;
        }

        private void LoginSuccess()
        {
            Status = "Introduce";
        }
    }

    可以看到MainWindowViewModel的子类LoginViewModel之间通信是使用LoginSuccess这个Action方法。(缺点就是不同ViewModel之间通信还很麻烦,Prism的EventAggregator很方便,后续将介绍)

    源码地址:https://gitee.com/akwkevin/aistudio.-wpf.-client.-stepby-step

    另外推荐一下我的Wpf客户端框架:https://gitee.com/akwkevin/aistudio.-wpf.-aclient

    作者:竹天笑
    互相学习,提高自己。
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    Java中数组遍历
    数组常见异常
    数组的访问
    Java中数组的定义方式
    Java中数组的概念与特点
    node爬虫技术初探
    node相关内容
    vs code快捷键
    vue 入门开发
    es6 常见用法
  • 原文地址:https://www.cnblogs.com/akwkevin/p/15112250.html
Copyright © 2011-2022 走看看