• MVVM体验记之DataGrid绑定


        知道MVVM这个词已经很久,但是一直没有去认识一下它,恰好这几天正在做点Silverlight的东西玩,顺便就也见识一下MVVM。

        准备工作:

    1、Silverlight 4包括SDK 及 Silverlight 4 Toolkit April 2010

    2、创建实体类(将在后面的DEMO中使用)People.cs:

        public class People
        {
            public string Name { get; set; }
    
            public int Age { get; set; }
        }

    3、创建实体类相应View视图PeopleView.xaml:

        <Grid x:Name="LayoutRoot" Background="White">
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Row="0" Grid.Column="0" Text="姓名:" />
            <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}" />
            <TextBlock Grid.Row="1" Grid.Column="0" Text="年龄:" />
            <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Age}" />
        </Grid>

    4、创建ViewModel(PropertyChangedBase来自这里),使它实现INotifyPropertyChanged接口:

        public class PeopleViewModel : PropertyChangedBase
        {
            public PeopleViewModel()
            {
                Peoples = new ObservableCollection<People>();
                for (int i = 10; i < 30; i++)
                {
                    Peoples.Add(new People { Name = "test" + i.ToString(), Age = i });
                }
            }
    
            private People selectedPeople;
    
            public People SelectedPeople
            {
                get { return selectedPeople; }
                set
                {
                    selectedPeople = value;
                    this.NotifyPropertyChanged(p => p.SelectedPeople);
                }
            }
    
            public ObservableCollection<People> Peoples { get; set; }
    
        }

    接下来开始DEMO

    一、绑定DataGrid当前选中的行SelectedItem

    创建DEMO1页面,Xaml代码为:

    <UserControl x:Class="LWME.MVVMExperience.Views.Demo1"
        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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
        xmlns:v="clr-namespace:LWME.MVVMExperience.Views"
        xmlns:vm="clr-namespace:LWME.MVVMExperience.ViewModel"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="400">
        <UserControl.Resources>
            <vm:PeopleViewModel x:Key="vm1" />
        </UserControl.Resources>
        <Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource vm1}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="400" />
            </Grid.ColumnDefinitions>
            <sdk:DataGrid Grid.Column="0" ItemsSource="{Binding Peoples}" SelectedItem="{Binding SelectedPeople, Mode=TwoWay}" />
            <StackPanel Grid.Column="1" HorizontalAlignment="Center">
                <TextBlock Text="当前选中:" />
                <v:PeopleView DataContext="{Binding SelectedPeople}" />
            </StackPanel>
        </Grid>
    </UserControl>

    运行它,可以看到选择DataGrid某一行的时候,右边相应的详细信息也跟着变了,但这里并没有写任何代码,是不是很神奇呢?

    二、绑定选择的多行SelectedItems

        上面由于SelectedItem同时有get、set访问器,所以支持直接绑定。但是SelectedItems是不支持set访问器的,所以无法直接绑定。有人提到使用Command来解决这一问题,现在便用一下他的方法吧:

        首先,实现ICommand接口:

        public class RelayCommand<T> : ICommand
        {
            /// <summary>
            /// Occurs when changes occur that affect whether the command should execute.
            /// </summary>
            public event EventHandler CanExecuteChanged;
    
            Predicate<T> canExecute;
            Action<T> executeAction;
            bool canExecuteCache;
    
            /// <summary>
            /// Initializes a new instance of the <see cref="RelayCommand"/> class.
            /// </summary>
            /// <param name="executeAction">The execute action.</param>
            public RelayCommand(Action<T> executeAction) : this(executeAction, null) { }
    
            /// <summary>
            /// Initializes a new instance of the <see cref="RelayCommand"/> class.
            /// </summary>
            /// <param name="executeAction">The execute action.</param>
            /// <param name="canExecute">The can execute.</param>
            public RelayCommand(Action<T> executeAction,
                                   Predicate<T> canExecute)
            {
                this.executeAction = executeAction;
                this.canExecute = canExecute;
            }
    
            #region ICommand Members
            /// <summary>
            /// Defines the method that determines whether the command 
            /// can execute in its current state.
            /// </summary>
            /// <param name="parameter">
            /// Data used by the command. 
            /// If the command does not require data to be passed,
            /// this object can be set to null.
            /// </param>
            /// <returns>
            /// true if this command can be executed; otherwise, false.
            /// </returns>
            public bool CanExecute(object parameter)
            {
                if (canExecute == null)
                    return true;
    
                bool tempCanExecute = canExecute((T)parameter);
    
                if (canExecuteCache != tempCanExecute)
                {
                    canExecuteCache = tempCanExecute;
                    if (CanExecuteChanged != null)
                    {
                        CanExecuteChanged(this, EventArgs.Empty);
                    }
                }
    
                return canExecuteCache;
            }
    
            /// <summary>
            /// Defines the method to be called when the command is invoked.
            /// </summary>
            /// <param name="parameter">
            /// Data used by the command. 
            /// If the command does not require data to be passed, 
            /// this object can be set to null.
            /// </param>
            public void Execute(object parameter)
            {
                executeAction((T)parameter);
            }
            #endregion
        }

      接下来,修改PeopleViewModel,定义一个Command属性以绑定到DataGrid的SelectionChange事件,同时添加其他需要用于多选的属性:

        public class PeopleViewModel : PropertyChangedBase
        {
            public PeopleViewModel()
            {
                Peoples = new ObservableCollection<People>();
                for (int i = 10; i < 30; i++)
                {
                    Peoples.Add(new People { Name = "test" + i.ToString(), Age = i });
                }
    
                SelectionChangedCommand = new RelayCommand<IList>(SelectionChanged);
            }
    
            private People selectedPeople;
    
            public People SelectedPeople
            {
                get { return selectedPeople; }
                set
                {
                    selectedPeople = value;
                    this.NotifyPropertyChanged(p => p.SelectedPeople);
                }
            }
    
            public ObservableCollection<People> Peoples { get; set; }
    
    
            #region 多选
    
            private IEnumerable<People> selectedPeoples;
            public IEnumerable<People> SelectedPeoples
            {
                get { return selectedPeoples; }
                set
                {
                    selectedPeoples = value;
                    this.NotifyPropertyChanged(p => p.SelectedPeoples);
                }
            }
    
            private int numberOfItemsSelected;
            public int NumberOfItemsSelected
            {
                get { return numberOfItemsSelected; }
                set
                {
                    numberOfItemsSelected = value;
                    this.NotifyPropertyChanged(p => p.NumberOfItemsSelected);
                }
            }
    
            public RelayCommand<IList> SelectionChangedCommand { get; set; }
    
            private void SelectionChanged(IList items)
            {
                if (items != null)
                {
                    SelectedPeoples = items.Cast<People>();
                    NumberOfItemsSelected = items.Count;
                }
                else
                {
                    SelectedPeoples = null;
                    NumberOfItemsSelected = 0;
                }
            }
            #endregion
        }

        再定义一个Converter用于显示多个选择的People到textblock上:

        public class SelectedPeoplesConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                IEnumerable<People> SelectedPeoples = value as IEnumerable<People>;
                if (SelectedPeoples != null)
                {
                    return string.Join(
                        "|",
                        SelectedPeoples.Select(p => string.Format("姓名:{0},年龄:{1}   ", p.Name, p.Age))
                        );
                }
    
                return string.Empty;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }

        再添加我们的Demo2.xaml,这里Command的绑定用到了System.Windows.Interactivity.dll:

    <UserControl x:Class="LWME.MVVMExperience.Views.Demo2"
        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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:v="clr-namespace:LWME.MVVMExperience.Views"
        xmlns:vm="clr-namespace:LWME.MVVMExperience.ViewModel"
        xmlns:cv="clr-namespace:LWME.MVVMExperience.Common"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="400">
        <UserControl.Resources>
            <vm:PeopleViewModel x:Key="vm1" />
            <cv:SelectedPeoplesConverter x:Key="pc1" />
        </UserControl.Resources>
        <Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource vm1}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="400" />
            </Grid.ColumnDefinitions>
            <sdk:DataGrid x:Name="peoplesDataGrid" Grid.Column="0" ItemsSource="{Binding Peoples}" SelectedItem="{Binding SelectedPeople, Mode=TwoWay}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <i:InvokeCommandAction
                            Command="{Binding SelectionChangedCommand, Mode=OneWay}"
                            CommandParameter="{Binding SelectedItems, ElementName=peoplesDataGrid}" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </sdk:DataGrid>
            <StackPanel Grid.Column="1" HorizontalAlignment="Center">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text=" 总共选中了:" />
                    <TextBlock Text="{Binding NumberOfItemsSelected}" />
                    <TextBlock Text="条记录,分别为:" />
                </StackPanel>
                <TextBlock Text="{Binding SelectedPeoples, Converter={StaticResource pc1}}" TextWrapping="Wrap" />
                <TextBlock Text=" 当前选中:" />
                <v:PeopleView DataContext="{Binding SelectedPeople}" />
            </StackPanel>
        </Grid>
    </UserControl>

    运行一下,选中多个列的时候,右边同时显示出了多个被选中的People数据以及选中的数目。

    总结一下,良好的分离使MVVM看起来蛮优雅的,而且熟悉了以后,有利于简化代码以及提高开发效率,over。

    DEMO在这里。。。

  • 相关阅读:
    伪元素 first-letter
    html语义化 -------<fieldset>和<legend>
    《ASP.NET MVC4 WEB编程》学习笔记------ViewBag、ViewData和TempData的使用和区别
    《ASP.NET MVC4 WEB编程》学习笔记------.net mvc实现原理ActionResult/View
    《ASP.NET MVC4 WEB编程》学习笔记------RenderBody,RenderPage,RenderSection
    《转》Visual Studio 2010 终极定制安装精简方法
    《转》IIS中配置通配符应用程序映射
    IIS安装时,添加/编辑应用程序扩展名映射 确定按钮不可用。
    异常:操作可能会破坏运行时稳定性
    petri网学习心得
  • 原文地址:https://www.cnblogs.com/lwme/p/1785550.html
Copyright © 2020-2023  润新知