wpf入门demo(二)

MVVM模式

MVVM全称为Model、View、ViewModel。

  • View代表窗体、控件等可视化资源

  • ViewModel:代表View的业务处理类,将获取到的数据处理好与View进行关联绑定。

  • Model:通常代表数据模型,它将支持ViewModel中所使用的到的。还有一种用法就是在Model里完成业务逻辑的编写ViewModel只需要写出关联逻辑代码,具体的使用方式视情况而定。毕竟MVVM只是一个规范我们尽量遵守即可。

新建项目

新建项目wpf_basic2,并且加载在demo一中写好的wpf_common模块,并将该模块添加为wpf_basic2的一个子模块。

Models

新建Models文件夹,并在该文件夹下新建LoginModel.cs, MainModel.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace wpf_basic2.Models
{
    class LoginModel
    {
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Juster.Music.Models
{
    public class MainModel
    {
    }
}

Models主要试为ViewModels提供数据,这里我们没有数据处理,就建两个空类。

ViewModels

  • 新建ViewModels文件夹并在这两个文件夹下新建LoginViewModel.cs 和 MainViewModel.cs。
  • ViewModels主要是接收Models数据并与Views界面交互。

LoginViewModel

主要把demo(一)中LoginModel.cs的内容拷贝过来。

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Input;
using wpf_common;
namespace wpf_basic2.ViewModels
{
    class LoginViewModel
    {
        private string _account;

        public string Account
        {
            get { return _account; }
            set
            {
                _account = value;
            }
        }

        private string _passwrold;

        public string PassWrold
        {
            get { return _passwrold; }
            set
            {
                _passwrold = value;
            }
        }

        private List<string> _loginTypes;

        public List<string> LoginTypes
        {
            get { return _loginTypes; }
            set { _loginTypes = value; }
        }


        public ICommand LoingCommand { get; set; }
        public ICommand CancelRemberCommand { get; set; }




        public LoginViewModel()
        {
            LoingCommand = new MyCommand(LoginAction);
            CancelRemberCommand = new MyCommand<bool>(CancelAction);
            Account = "root";
            LoginTypes = new List<string>();
            LoginTypes.Add("手机号登录");
            LoginTypes.Add("账号密码登录");
            LoginTypes.Add("二维码登录");
        }

        private void CancelAction(bool obj)
        {
            MessageBox.Show($"check status { obj }");
        }

        private void LoginAction()
        {
            if (Account == "root" && PassWrold == "123")
            {
                MessageBox.Show("登录成功!");
            }
            else
            {
                MessageBox.Show("登录失败!");
            }
        }
    }
}

MainViewModel

这页不需要做数据交互,所以只建一个空的类

using System;
using System.Collections.Generic;
using System.Text;

namespace wpf_basic2.ViewModels
{
    class MainViewModel
    {
    }
}

Views

新建Views文件夹并在该文件夹下新建LoginView页包含LoginView.xaml 和 LoginView.xaml.cs

LoginView.xaml将demo1的MainWindow.xaml的内容拷贝过来,但x:Class,xmlns:local等属性要改

<Window
    x:Class="wpf_basic2.Views.LoginView"
    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:local="clr-namespace:wpf_basic2.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="LoginView"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
            <TextBlock
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Text="账号:" />
            <TextBox
                Name="TxtA"
                Width="300"
                Height="30"
                Text="{Binding Account, UpdateSourceTrigger=PropertyChanged}" />
        </StackPanel>
        <StackPanel
            Grid.Row="1"
            HorizontalAlignment="Center"
            Orientation="Horizontal">
            <TextBlock
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Text="密码:" />
            <TextBox
                Name="TxtP"
                Width="300"
                Height="30"
                Text="{Binding PassWrold, Mode=TwoWay}" />
        </StackPanel>
        <StackPanel
            Grid.Row="2"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Orientation="Horizontal">
            <ComboBox
                Width="200"
                Height="25"
                ItemsSource="{Binding LoginTypes}" />
            <CheckBox
                x:Name="ChkboxPwd"
                Margin="5"
                Content="是否记住密码?" />
        </StackPanel>
        <StackPanel
            Grid.Row="3"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Orientation="Horizontal">
            <Button
                Name="BtnLogin"
                Width="200"
                Height="30"
                Margin="3"
                Command="{Binding LoingCommand}"
                Content="Login" />
            <Button
                Width="200"
                Height="30"
                Margin="3"
                Command="{Binding CancelRemberCommand}"
                CommandParameter="{Binding ElementName=ChkboxPwd, Path=IsChecked}"
                Content="check" />
        </StackPanel>
    </Grid>
</Window>

LoginView.xaml.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using wpf_basic2.ViewModels;
namespace wpf_basic2.Views
{
    /// <summary>
    /// LoginView.xaml 的交互逻辑
    /// </summary>
    public partial class LoginView : Window
    {
        public LoginView()
        {
           
            InitializeComponent();
            
            DataContext = new LoginViewModel();
        }
    }
}

修改MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using wpf_basic2.ViewModels;

namespace wpf_basic2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainViewModel();
        }
    }
}

修改启动页

修改文件App.xaml中的StartupUri

<Application
    x:Class="wpf_basic2.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:wpf_basic2"
    StartupUri="/Views/LoginView.xaml">
    <Application.Resources />
</Application>

测试

运行项目界面和demo1是一致的。

资源 样式

资源语法

Window写法

<Window>
      <Window.Resources>
       </Window.Resources>
</Window>

Control写法

<Control>
      <Control.Resources>
       </Control.Resources>
</Control>

样试(style)

style 类属性:

  • Setterts:设置属性值以及自动关联事件处理程序的Setter对象或EventSetter对象的集合是Style类中最重要的属性,但并非唯一属性。
  • Triggers: 继承自TriggerBase类能自动改变样式设置的对象集合。例如,当另一个属性改变时,或者当发生某个时间时,可以修改样式。
  • Resources: 希望用于样式的资源集合。
  • BasedOn:通过该属性可创建继承自其它样式设置的更具体的样式
  • TargetType: 该属性标识应用样式的元素类型。通过该属性可创建只影响特定类型元素的设置器,还可以创建能够为恰当的元素类型自动起作用的设置器。

LoginView.xaml

添加资源

<Window
    x:Class="wpf_basic2.Views.LoginView"
    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:local="clr-namespace:wpf_basic2.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="LoginView"
    Width="800"
    Height="450"
    mc:Ignorable="d">

    <Window.Resources>
        <Style x:Key="BtnBkgdStyle" TargetType="Button">
            <Setter Property="Foreground" Value="White" />
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="False">
                    <Setter Property="Foreground" Value="BlueViolet" />
                </Trigger>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Foreground" Value="Red" />
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style
            x:Key="BtnStyle"
            BasedOn="{StaticResource BtnBkgdStyle}"
            TargetType="Button">
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="BorderThickness" Value="5" />
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style x:Key="BackgroundStyle1">
            <Setter Property="Control.Background" Value="Beige" />
        </Style>
    </Window.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
            <TextBlock
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Text="账号:" />
            <TextBox
                Name="TxtA"
                Width="300"
                Height="30"
                Text="{Binding Account, UpdateSourceTrigger=PropertyChanged}" />
        </StackPanel>
        <StackPanel
            Grid.Row="1"
            HorizontalAlignment="Center"
            Orientation="Horizontal">
            <TextBlock
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Text="密码:" />
            <TextBox
                Name="TxtP"
                Width="300"
                Height="30"
                Text="{Binding PassWrold, Mode=TwoWay}" />
        </StackPanel>
        <StackPanel
            Grid.Row="2"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Orientation="Horizontal">
            <ComboBox
                Width="200"
                Height="25"
                ItemsSource="{Binding LoginTypes}" />
            <CheckBox
                x:Name="ChkboxPwd"
                Margin="5"
                Content="是否记住密码?" />
        </StackPanel>
        <StackPanel
            Grid.Row="3"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Orientation="Horizontal">
            <Button
                Name="BtnLogin"
                Width="200"
                Height="30"
                Margin="3"
                Command="{Binding LoingCommand}"
                Content="Login"
                Style="{StaticResource BtnBkgdStyle}" />
            <Button
                Width="200"
                Height="30"
                Margin="3"
                Command="{Binding CancelRemberCommand}"
                CommandParameter="{Binding ElementName=ChkboxPwd, Path=IsChecked}"
                Content="check"
                Style="{StaticResource BtnStyle}" />
        </StackPanel>
        <Grid Style="{StaticResource BackgroundStyle1}" />
    </Grid>
</Window>
  • x:Key 是指定资源的唯一标识符
  • Setter Property="Foreground" Value="White" />前景色为白色
  • <Trigger Property="IsMouseOver" Value="True"> 触发方式是鼠标放在上面时触发
  • Style="{StaticResource BtnStyle}"指定元素的样式

效果

145

Convert

WPF中Converter的用于对数据的转换。

实现IValueConverter接口

在wpf_common中添加ColorConvert.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Windows.Data;

namespace wpf_common
{
    public class ColorConvert : IValueConverter
    {
        /// <summary>
        /// 源属性传给目标属性时,调用此方法Convert
        /// </summary>
        /// <param name="value">数据源</param>
        /// <param name="targetType">目标类型</param>
        /// <param name="parameter">不常用</param>
        /// <param name="culture">不常用</param>
        /// <returns>目标值</returns>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null) return null;

            //TODO:转换逻辑
            int index = System.Convert.ToInt32(value);
            if (index == 0)
                return "Blue";
            else if (index == 1)
                return "Green";
            else
                return "Red";
        }

        /// <summary>
        /// 目标属性传给源属性时,调用此方法ConvertBack
        /// </summary>
        /// <param name="value">目标值</param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns>数据源</returns>
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
}

  • Convert是从绑定源到绑定目标,ConvertBack是方法Convert的反向实现从目标到源

新增属性更改通知

  • INotifyPropertyChanged 接口:向客户端发出某一属性值已更改的通知。
  • 实现INotifyPropertyChanged 接口
  • 在wpf_common中新增BindableBase.cs
using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;

namespace wpf_common
{
    public abstract class BindableBase : INotifyPropertyChanged
    {

            /// <summary>
            /// Occurs when a property value changes.
            /// </summary>
            public event PropertyChangedEventHandler PropertyChanged;


            /// <summary>
            /// Checks if a property already matches a desired value. Sets the property and
            /// notifies listeners only when necessary.
            /// </summary>
            /// <typeparam name="T">Type of the property.</typeparam>
            /// <param name="storage">Reference to a property with both getter and setter.</param>
            /// <param name="value">Desired value for the property.</param>
            /// <param name="propertyName">Name of the property used to notify listeners. This
            /// value is optional and can be provided automatically when invoked from compilers that
            /// support CallerMemberName.</param>
            /// <returns>True if the value was changed, false if the existing value matched the
            /// desired value.</returns>
            protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
            {
                if (object.Equals(storage, value)) return false;

                storage = value;
                this.OnPropertyChanged(propertyName);

                return true;
            }

            /// <summary>
            /// Notifies listeners that a property value has changed.
            /// </summary>
            /// <param name="propertyName">Name of the property used to notify listeners. This
            /// value is optional and can be provided automatically when invoked from compilers
            /// that support <see cref="CallerMemberNameAttribute"/>.</param>
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
}

新增颜色改变动作

修改LoginViewModel.cs

  • 继承BindableBase
  • 添加ChangeAction
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Input;
using wpf_common;


namespace wpf_basic2.ViewModels
{
    class LoginViewModel:BindableBase
    {
        private string _account;

        public string Account
        {
            get { return _account; }
            set
            {
                _account = value;
            }
        }

        private string _passwrold;

        public string PassWrold
        {
            get { return _passwrold; }
            set
            {
                _passwrold = value;
            }
        }

        private List<string> _loginTypes;

        public List<string> LoginTypes
        {
            get { return _loginTypes; }
            set { _loginTypes = value; }
        }


        public ICommand LoingCommand { get; set; }
        public ICommand CancelRemberCommand { get; set; }

        public ICommand ChangeCommand { get; set; }


        private int _color;

        public int Color
        {
            get { return _color; }
            set
            {
                _color = value;
                OnPropertyChanged("Color");
            }
        }

        private void ChangeAction()
        {
            if (Color > 2)
            {
                Color = -1;
            }
            Color++;
        }


        public LoginViewModel()
        {
            LoingCommand = new MyCommand(LoginAction);
            CancelRemberCommand = new MyCommand<bool>(CancelAction);
            ChangeCommand = new MyCommand(ChangeAction);
            Account = "root";
            LoginTypes = new List<string>();
            LoginTypes.Add("手机号登录");
            LoginTypes.Add("账号密码登录");
            LoginTypes.Add("二维码登录");
        }

        private void CancelAction(bool obj)
        {
            MessageBox.Show($"check status { obj }");
        }

        private void LoginAction()
        {
            if (Account == "root" && PassWrold == "123")
            {
                MessageBox.Show("登录成功!");
            }
            else
            {
                MessageBox.Show("登录失败!");
            }
        }
    }
}

  • 属性Color再 -1,0,1之间切换

修改LoginView.xaml

<Window
    x:Class="wpf_basic2.Views.LoginView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:cvt="clr-namespace:wpf_common;assembly=wpf_common"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:wpf_basic2.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="LoginView"
    Width="800"
    Height="450"
    mc:Ignorable="d">

    <Window.Resources>
        <cvt:ColorConvert x:Key="MyColorConvert" />
        <Style x:Key="BtnBkgdStyle" TargetType="Button">
            <Setter Property="Foreground" Value="White" />
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="False">
                    <Setter Property="Foreground" Value="BlueViolet" />
                </Trigger>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Foreground" Value="Red" />
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style
            x:Key="BtnStyle"
            BasedOn="{StaticResource BtnBkgdStyle}"
            TargetType="Button">
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="BorderThickness" Value="5" />
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style x:Key="BackgroundStyle1">
            <Setter Property="Control.Background" Value="Beige" />
        </Style>
    </Window.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
            <TextBlock
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Text="账号:" />
            <TextBox
                Name="TxtA"
                Width="300"
                Height="30"
                Text="{Binding Account, UpdateSourceTrigger=PropertyChanged}" />
        </StackPanel>
        <StackPanel
            Grid.Row="1"
            HorizontalAlignment="Center"
            Orientation="Horizontal">
            <TextBlock
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Text="密码:" />
            <TextBox
                Name="TxtP"
                Width="300"
                Height="30"
                Text="{Binding PassWrold, Mode=TwoWay}" />
        </StackPanel>
        <StackPanel
            Grid.Row="2"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Orientation="Horizontal">
            <ComboBox
                Width="200"
                Height="25"
                ItemsSource="{Binding LoginTypes}" />
            <CheckBox
                x:Name="ChkboxPwd"
                Margin="5"
                Content="是否记住密码?"
                Foreground="{Binding Color, Converter={StaticResource MyColorConvert}}" />
        </StackPanel>
        <StackPanel
            Grid.Row="3"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Orientation="Horizontal">
            <Button
                Name="BtnLogin"
                Width="200"
                Height="30"
                Margin="3"
                Command="{Binding LoingCommand}"
                Content="Login"
                Style="{StaticResource BtnBkgdStyle}" />
            <Button
                Width="200"
                Height="30"
                Margin="3"
                Command="{Binding CancelRemberCommand}"
                CommandParameter="{Binding ElementName=ChkboxPwd, Path=IsChecked}"
                Content="check"
                Style="{StaticResource BtnStyle}" />
            <Button
                Width="200"
                Height="30"
                Margin="3"
                Command="{Binding ChangeCommand}"
                Content="Change Color"
                Style="{StaticResource BtnStyle}" />
        </StackPanel>
        <Grid Style="{StaticResource BackgroundStyle1}" />
    </Grid>
</Window>
  • xmlns:cvt : 在xmal文件引用DateConverter类所在命名空间。

  • <cvt:ColorConvert x:Key="MyColorConvert" />: 在xaml文件添加convert 资源。

  • 是否记住密码绑定Color

  • 新增Change Color按钮

效果

146

每点击一次Change Color,是否记住密码颜色都会改变一次。

模板

  • ControlTemplate : 它决定了控件“长成什么样子”,并让开发者有机会在控件原有的内部逻辑基础上扩展自己的逻辑。
  • DataTemplate : 是数据内容的展示方式,一条数据显示成什么样子,是简单的文本还是直观的图形就由它来决定了。

在MainWindow.xaml中使用模板

<Window
    x:Class="wpf_basic2.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:local="clr-namespace:wpf_basic2"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Window.Resources>
        <DataTemplate x:Key="songSheetDataTemplate">
            <StackPanel Orientation="Horizontal">
                <Image
                    Width="30"
                    Height="30"
                    Source="{Binding Path=Icon}" />
                <TextBlock
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    FontSize="14"
                    FontWeight="Bold"
                    Text="{Binding Path=Name}" />
            </StackPanel>
        </DataTemplate>
        <ControlTemplate x:Key="ButtonTemplate" TargetType="Button">
            <!--  定义视觉树  -->
            <Grid>
                <Ellipse
                    Name="faceEllipse"
                    Width="{TemplateBinding Button.Width}"
                    Height="{TemplateBinding Control.Height}"
                    Fill="{TemplateBinding Button.Background}" />
                <TextBlock
                    Name="txtBlock"
                    Margin="{TemplateBinding Button.Padding}"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Text="{TemplateBinding Button.Content}" />
            </Grid>
            <!--  定义视觉树_end  -->
            <!--  定义触发器  -->
            <ControlTemplate.Triggers>
                <Trigger Property="Button.IsMouseOver" Value="True">
                    <Setter Property="Button.Foreground" Value="Red" />
                </Trigger>
            </ControlTemplate.Triggers>
            <!--  定义触发器_End  -->
        </ControlTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="2*" />
            <RowDefinition Height="6*" />
            <RowDefinition Height="2*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*" />
            <ColumnDefinition Width="8*" />
        </Grid.ColumnDefinitions>
        <ListBox
            x:Name="LstSongSheets"
            Grid.Row="1"
            ItemTemplate="{StaticResource songSheetDataTemplate}"
            ItemsSource="{Binding Path=SongSheets}" />
        <DataGrid
            Grid.Row="1"
            Grid.Column="1"
            AutoGenerateColumns="False"
            ItemsSource="{Binding ElementName=LstSongSheets, Path=SelectedItem.Songs}"
            RowHeaderWidth="0">
            <DataGrid.Columns>
                <DataGridTextColumn
                    Width="150"
                    Binding="{Binding Path=Name}"
                    Header="歌名" />
                <DataGridTextColumn
                    Width="150"
                    Binding="{Binding Path=Singer}"
                    Header="歌手" />
                <DataGridTextColumn
                    Width="150"
                    Binding="{Binding Path=AlbumTitle}"
                    Header="专辑" />
                <DataGridTextColumn
                    Width="150"
                    Binding="{Binding Path=Lenght}"
                    Header="时长" />
            </DataGrid.Columns>
        </DataGrid>
        <StackPanel
            Grid.Row="2"
            Grid.Column="1"
            Width="195"
            Height="40"
            Orientation="Horizontal">
            <Button
                Width="55"
                Height="25"
                Margin="5"
                Content="上一曲"
                Template="{StaticResource ButtonTemplate}" />
            <Button
                Grid.Row="2"
                Width="55"
                Height="25"
                Margin="5"
                Content="播放"
                Template="{StaticResource ButtonTemplate}" />
            <Button
                Grid.Row="2"
                Width="55"
                Height="25"
                Margin="5"
                Content="下一曲"
                Template="{StaticResource ButtonTemplate}" />
        </StackPanel>
    </Grid>
</Window>

  • 定义了数据模板: songSheetDataTemplate,这个模板是一个图像,加一个文本框
  • 定义了按钮模板: ButtonTemplate,并在按钮模板中定义了触发器
  • LstSongSheets下拉列表绑定了songSheetDataTemplate模板
  • 按钮绑定ButtonTemplate模板

新增数据服务

上面我们有了模板需要一些展示的数据

在wpf_basic2中新建一个Dal目录,并在该目录下新建DataSercice.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace wpf_basic2.Dal
{
    class DataService
    {
        public static List<SongSheetDTO> GetSongSheet()
        {
            List<SongSheetDTO> songSheets = new List<SongSheetDTO>();

            var songSheetJay = new SongSheetDTO();
            songSheetJay.Name = "周杰伦的歌单";
            songSheetJay.Icon = "pack://application:,,,/wpf_common;component/imgs/music.png"; ;
            songSheetJay.Songs = new List<SongDTO>()
            {
                new SongDTO{ Name = "七里香" , AlbumTitle = "叶惠美" , Lenght = 800 , Singer = "周杰伦" , Url = "七里香.mp3" },
                new SongDTO{ Name = "外婆" , AlbumTitle = "叶惠美" , Lenght = 800 , Singer = "周杰伦" , Url = "外婆.mp3" },
                new SongDTO{ Name = "将军" , AlbumTitle = "叶惠美" , Lenght = 800 , Singer = "周杰伦" , Url = "将军.mp3" },
                new SongDTO{ Name = "搁浅" , AlbumTitle = "叶惠美" , Lenght = 800 , Singer = "周杰伦" , Url = "搁浅.mp3" }
            };
            songSheets.Add(songSheetJay);

            var songSheetJolin = new SongSheetDTO();
            songSheetJolin.Name = "蔡依林的歌单";
            songSheetJolin.Icon = "/wpf_common;component/imgs/music.png";
            songSheetJolin.Songs = new List<SongDTO>()
            {
                new SongDTO{ Name = "倒带" , AlbumTitle = "城堡" , Lenght = 800 , Singer = "蔡依林" , Url = "倒带.mp3" },
                new SongDTO{ Name = "爱情36计" , AlbumTitle = "城堡" , Lenght = 800 , Singer = "蔡依林" , Url = "爱情36计.mp3" },
                new SongDTO{ Name = "海盗" , AlbumTitle = "城堡" , Lenght = 800 , Singer = "蔡依林" , Url = "海盗.mp3" },
                new SongDTO{ Name = "柠檬草的味道" , AlbumTitle = "城堡" , Lenght = 800 , Singer = "蔡依林" , Url = "柠檬草的味道.mp3" }
            };
            songSheets.Add(songSheetJolin);

            return songSheets;
        }
    }

    public class SongDTO
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 歌曲存放地址
        /// </summary>
        public string Url { get; set; }

        /// <summary>
        /// 歌手
        /// </summary>
        public string Singer { get; set; }

        /// <summary>
        /// 歌曲时长(s)
        /// </summary>
        public int Lenght { get; set; }

        /// <summary>
        /// 专辑名称
        /// </summary>
        public string AlbumTitle { get; set; }
    }

    /// <summary>
    /// 歌单
    /// </summary>
    public class SongSheetDTO
    {
        /// <summary>
        /// 歌单名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 歌单图标
        /// </summary>
        public string Icon { get; set; }

        /// <summary>
        /// 歌单里所包含的歌曲
        /// </summary>
        public List<SongDTO> Songs { get; set; }
    }
}


添加图片资源

在wpf_common文件夹下新建imgs文件夹,并在该文件夹下新增muics.png图片

右键选中图片-> 属性-> 在面板中选择“生成操作”->将内容修改为“Resource”。

一定要生成resource

新增Model

  • 在Models中新增歌曲模型SongModel.cs。
  • 主要定义歌曲中用到的变量。
using System;
using System.Collections.Generic;
using System.Text;

namespace wpf_basic2.ViewModels
{
    class SongModel
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 歌曲存放地址
        /// </summary>
        public string Url { get; set; }

        /// <summary>
        /// 歌手
        /// </summary>
        public string Singer { get; set; }

        /// <summary>
        /// 歌曲时长(s)
        /// </summary>
        public int Lenght { get; set; }

        /// <summary>
        /// 专辑名称
        /// </summary>
        public string AlbumTitle { get; set; }
    }
}

  • 新增歌单模型SongSheetModel.cs。
using System;
using System.Collections.Generic;
using System.Text;

namespace wpf_basic2.ViewModels
{
    class SongSheetModel
    {
        /// <summary>
        /// 歌单名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 歌单图标
        /// </summary>
        public string Icon { get; set; }

        /// <summary>
        /// 歌单里所包含的歌曲
        /// </summary>
        public List<SongModel> Songs { get; set; }

        public override string ToString()
        {
            return Name;
        }
    }
}

修改启动页

修改App.xaml中的StartupUri

 StartupUri="MainWindow.xaml"

效果

147

music.png图片不显示还要定位

线程

线程不更新ui

修改MainWIndow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using wpf_basic2.ViewModels;
using System.Threading;

namespace wpf_basic2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainViewModel();
            Thread subthread = new Thread(SubthreadAction);
            subthread.IsBackground = true;
            subthread.Start();
        }
        private void SubthreadAction(object obj)
        {
            while (true) {
                Thread.Sleep(1000 * 10);
            }

        }
    }
}

启动了线程SubthreadAction

线程更新ui

如果在后台线程中直接修改ui会报错,调用线程无法访问此对象,因为另一个线程拥有该对象。

修改MainWindow.xaml

在播放按钮中添加x:Name="btn",C#可以通过他访问播放按钮

            <Button
                x:Name="btn"
                Grid.Row="2"
                Width="55"
                Height="25"
                Margin="5"
                Content="播放"
                Template="{StaticResource ButtonTemplate}" />

Dispatcher

ui多线程更新有两种方法:Dispatcher(大部分人使用),TaskScheduler(任务调度器)。

在MainWindow.xaml.cs中添加线程UithreadAction,这个线程会把btn按钮上面的字从播放改为夏中伟。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using wpf_basic2.ViewModels;
using System.Threading;

namespace wpf_basic2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainViewModel();
            Thread subthread = new Thread(SubthreadAction);
            subthread.IsBackground = true;
            subthread.Start();
            Thread uithread = new Thread(UithreadAction);
            uithread.IsBackground = true;
            uithread.Start();
        }
        private void SubthreadAction(object obj)
        {
            while (true) {
                Thread.Sleep(1000 * 10);
                //调用线程无法访问此对象,因为另一个线程拥有该对象。
                //btn.Content = "ddd";
            }

        }
        private void UithreadAction(object obj)
        {
            this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, (ThreadStart)delegate ()
                {
                    btn.Content = "夏中伟";
                });
        }
    }
}

效果

运行发现播放按钮被线程改成了夏中伟按钮

148

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×