WPF模板數據綁定及事件模板綁定

        變更通知是WPF的一個精髓,它使得MVVM成爲WPF的標準架構!在數據綁定中,除了正常的數據模版綁定,還會涉及到模板內控件的事件綁定,以及對parent內容的綁定!接下來的示例將會展示大部分常用的綁定場景。

      示例實現的是一個便籤軟件,主要功能有新增、刪除、修改、切換日期、讀取便籤列表、設置是否完成。

      下面先說下幾種綁定方式:

       繼承於ICommand和INotifyPropertyChanged的事件委託和通知變更類這裏就不寫了,網上很多的!或者你可以直接使用mvvmlight的框架。

       在App.xaml中加入資源NoteViewModel,方便在blend中綁定

<Application x:Class="ManageNote.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:ManageNote.ViewModel"
             Startup="ApplicationStartup" >
    <Application.Resources>
        <local:NoteViewModel x:Key="NoteViewModel"></local:NoteViewModel>
    </Application.Resources>
</Application>
打開blend,如果不習慣用blend,可以參考後面的xaml代碼自己編寫!

1、模板數據綁定,這是最基礎的綁定,通過blend可以實現

1.1 綁定window的datacontext


1.2綁定listbox的itemsource


1.3、右鍵Listbox > 編輯其他模版 > 編輯生成的項 > 創建新項,拖入一個checkbox和textblock,並綁定相對應的字段。如果需要字段轉換,最常用的是bool和visible等,可以自己寫convert轉換器,具體寫法自行百度。



1.4 通過“行爲”來控制,文本框是否可編輯!原本是隻讀模式,在雙擊後可以進行編輯,只展示一個示例,其他的在代碼中找!


2、模版父控件的綁定

下面需要綁定checkbox的點擊事件來設置該項任務是否完成。在blend中可以看到,在模板的數據綁定中只能顯示notemodel中的字段,這是因爲item的datacontext類型爲NoteModel。我們想綁定NoteViewModel中的CompleteCommand事件,就需要自行指定綁定的數據源。這需要通過代碼實現:

<CheckBox x:Name="checkBox" d:LayoutOverrides="Width, Height" HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding IsCompleted}" Command="{Binding DataContext.CompleteCommand, RelativeSource={RelativeSource AncestorType=Window}}"  CommandParameter="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}" >

這裏的Binding使用到了Path和RelativeSource。

msdn的RelativeSource:http://msdn.microsoft.com/zh-cn/library/system.windows.data.binding.source(v=vs.90).aspx

RelativeSource成員:http://msdn.microsoft.com/zh-SG/library/system.windows.data.relativesource_members(v=vs.85)

RelativeSource的AncestorType:http://msdn.microsoft.com/zh-SG/library/system.windows.data.relativesource.ancestortype(v=vs.85)

這幾篇文章讀完就能掌握常用的幾種綁定方式了。

3、模板綁定某一元素的屬性(通過RelativeSource查找)

 <ContextMenu x:Key="ContextMenuKey">
            <MenuItem Header="刪除" Command="{Binding DataContext.DelPlan, RelativeSource={RelativeSource AncestorType=Window}}" CommandParameter="{Binding Path=DataContext, RelativeSource={RelativeSource  Mode=FindAncestor, AncestorLevel=1,AncestorType=ItemsPresenter}}" />
        </ContextMenu>
AncestorLevel來確定向上查找的級別,AncestorType確定要查找的元素!這點很類似於JQuery的Selector。

以上就是幾種常用的綁定方式。

下面貼上部分源碼:

NoteWindow.xaml

<Window 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:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" xmlns:ManageNote_ViewModel="clr-namespace:ManageNote.ViewModel" x:Class="ManageNote.NoteWindow"
        Title="便籤" RenderOptions.BitmapScalingMode="NearestNeighbor"  mc:Ignorable="d"  ShowInTaskbar="False" WindowStyle="None" AllowsTransparency="True" ResizeMode="NoResize" Background="{x:Null}" VerticalContentAlignment="Stretch" Width="250" Height="200" DataContext="{DynamicResource NoteViewModel}" >
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
        <ContextMenu x:Key="ContextMenuKey">
            <MenuItem Header="刪除" Command="{Binding DataContext.DelPlan, RelativeSource={RelativeSource AncestorType=Window}}" CommandParameter="{Binding Path=DataContext, RelativeSource={RelativeSource  Mode=FindAncestor, AncestorLevel=1,AncestorType=ItemsPresenter}}" />
        </ContextMenu>
        <Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Grid>
                            <Path Data="M16,16 L32,8.0002261 32,24.000226 z" Fill="#FF888864" Stretch="Fill" Stroke="Black" StrokeThickness="0"/>
                            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Content=""/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsFocused" Value="True"/>
                            <Trigger Property="IsDefaulted" Value="True"/>
                            <Trigger Property="IsMouseOver" Value="True"/>
                            <Trigger Property="IsPressed" Value="True"/>
                            <Trigger Property="IsEnabled" Value="False"/>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <DataTemplate x:Key="DataTemplate1">
            <Grid d:DesignWidth="238" Margin="0,2" Background="{x:Null}">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="25"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <CheckBox x:Name="checkBox" d:LayoutOverrides="Width, Height" HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding IsCompleted}" Command="{Binding DataContext.CompleteCommand, RelativeSource={RelativeSource AncestorType=Window}}"  CommandParameter="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}" >

                </CheckBox>
                <TextBox x:Name="textBox" Grid.Column="1" Height="Auto" TextWrapping="Wrap" Text="{Binding Content}" IsReadOnly="True" Background="{x:Null}" BorderBrush="{x:Null}" SelectionBrush="#FF0D69FF" BorderThickness="1" Style="{DynamicResource TextBoxStyle1}">
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="MouseDoubleClick">
                            <ei:ChangePropertyAction TargetObject="{Binding ElementName=textBox}" PropertyName="IsReadOnly" Value="False"/>
                        </i:EventTrigger>
                        <i:EventTrigger EventName="LostFocus">
                            <ei:ChangePropertyAction TargetObject="{Binding ElementName=textBox}" PropertyName="IsReadOnly" Value="True"/>
                            <i:InvokeCommandAction Command="{Binding DataContext.UpdateTaskCommand,RelativeSource={RelativeSource AncestorType=Window}}" CommandParameter="{Binding Path=Content,RelativeSource={ RelativeSource Mode=TemplatedParent}}"/>
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                </TextBox>
            </Grid>
        </DataTemplate>
        <Style x:Key="ListBoxItemStyle1" TargetType="{x:Type ListBoxItem}">
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
            <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
            <Setter Property="Padding" Value="2,0,0,0"/>
            <Setter Property="ContextMenu"  Value="{StaticResource ContextMenuKey}" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <Border x:Name="Bd" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true" Background="{x:Null}">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal"/>
                                    <VisualState x:Name="MouseOver"/>
                                    <VisualState x:Name="Disabled"/>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <LinearGradientBrush x:Key="TextBoxBorder" EndPoint="0,20" MappingMode="Absolute" StartPoint="0,0">
            <GradientStop Color="#ABADB3" Offset="0.05"/>
            <GradientStop Color="#E2E3EA" Offset="0.07"/>
            <GradientStop Color="#E3E9EF" Offset="1"/>
        </LinearGradientBrush>
        <Style x:Key="TextBoxStyle1" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
            <Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Padding" Value="1"/>
            <Setter Property="AllowDrop" Value="true"/>
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
            <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TextBox}">
                        <Microsoft_Windows_Themes:ListBoxChrome x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" SnapsToDevicePixels="true">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal"/>
                                    <VisualState x:Name="Disabled"/>
                                    <VisualState x:Name="ReadOnly"/>
                                    <VisualState x:Name="MouseOver"/>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" BorderThickness="1"/>
                        </Microsoft_Windows_Themes:ListBoxChrome>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid  Margin="3">
        <Border CornerRadius="5" Background="Black" BorderThickness="1" BorderBrush="#FFCDCDCD" >
            <Border.Effect>
                <DropShadowEffect BlurRadius="9" Color="#FF4B4747" Opacity="0.845" Direction="285" ShadowDepth="1"/>
            </Border.Effect>
        </Border>
        <Grid>
            <Grid.Background>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="#FFFDFDCB" Offset="0"/>
                    <GradientStop Color="#FFFCF9A1" Offset="1"/>
                </LinearGradientBrush>
            </Grid.Background>
            <Grid.RowDefinitions>
                <RowDefinition Height="30" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Border BorderBrush="Black" BorderThickness="0" Margin="0" Background="#FFF8F7B6"/>
            <Image x:Name="image"
            Height="20" Source="5_content_new.png" Stretch="Fill" Width="20" HorizontalAlignment="Right" Margin="0,0,30,0" VerticalAlignment="Center" >
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="MouseLeftButtonDown" SourceObject="{Binding ElementName=image}">
                        <ei:CallMethodAction TargetObject="{Binding Mode=OneWay}" MethodName="AddAgenda" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </Image>
            <TextBlock  HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="labDate" Foreground="#FF646363" FontWeight="Bold" FontSize="13.333" Text="{Binding Title}" />
            <Button  x:Name="btnPre" Content="Button" HorizontalAlignment="Left" Margin="5,0,0,0" Style="{DynamicResource ButtonStyle1}" Width="16" Height="16" VerticalAlignment="Center" Command="{Binding ChangeDateCommand, Mode=OneWay}" CommandParameter="-1"/>
            <Button  x:Name="btnNext"  Content="Button" Margin="0,0,5,0" Style="{DynamicResource ButtonStyle1}" RenderTransformOrigin="0.5,0.5" HorizontalAlignment="Right" Width="16" Height="16" VerticalAlignment="Center" Background="{x:Null}" Command="{Binding ChangeDateCommand, Mode=OneWay}" CommandParameter="1">
                <Button.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform/>
                        <SkewTransform/>
                        <RotateTransform Angle="180"/>
                        <TranslateTransform/>
                    </TransformGroup>
                </Button.RenderTransform>
            </Button>
            <ListBox Margin="5" Grid.Row="1" Background="{x:Null}" BorderThickness="0" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemContainerStyle="{DynamicResource ListBoxItemStyle1}" ScrollViewer.VerticalScrollBarVisibility="Hidden" ItemTemplate="{DynamicResource DataTemplate1}" ItemsSource="{Binding Tasks}" HorizontalContentAlignment="Stretch">
            </ListBox>
        </Grid>
    </Grid>
</Window>

NoteViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ManageNote.Model;
using System.Data;
using MySql.Data.MySqlClient;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;

namespace ManageNote.ViewModel
{
    public class NoteViewModel : NotificationObject
    {
        DateTime currentDate;

        private DelegateCommand<NoteModel> completeCommand;

        /// <summary>
        /// 設置完成狀態事件
        /// </summary>
        public DelegateCommand<NoteModel> CompleteCommand
        {
            get
            {
                if (this.completeCommand == null)
                {
                    this.completeCommand = new DelegateCommand<NoteModel>(this.SetComplete);
                }

                return this.completeCommand;
            }
        }

        private DelegateCommand<NoteModel> delPlan;

        /// <summary>
        /// 刪除計劃事件
        /// </summary>
        public DelegateCommand<NoteModel> DelPlan
        {
            get
            {
                if (this.delPlan == null)
                {
                    this.delPlan = new DelegateCommand<NoteModel>(this.Delete);
                }

                return this.delPlan;
            }
        }

        private DelegateCommand<object> changeDateCommand;

        /// <summary>
        /// 修改日期事件
        /// </summary>
        public DelegateCommand<object> ChangeDateCommand
        {
            get
            {
                if (this.changeDateCommand == null)
                {
                    this.changeDateCommand = new DelegateCommand<object>(this.ChangeDate);
                }

                return this.changeDateCommand;
            }
        }

        private DelegateCommand<NoteModel> updateTaskCommand;

        /// <summary>
        /// 修改事件
        /// </summary>
        public DelegateCommand<NoteModel> UpdateTaskCommand
        {
            get
            {
                if (this.updateTaskCommand == null)
                {
                    this.updateTaskCommand = new DelegateCommand<NoteModel>(this.UpdateAgenda);
                }

                return this.updateTaskCommand;
            }
        }

        public NoteViewModel()
        {
            this.BindDate(DateTime.Now.Date);
        }

        private string title;

        /// <summary>
        /// 標題
        /// </summary>
        public string Title
        {
            get
            {
                return this.title;
            }

            set
            {
                if (this.title != value)
                {
                    this.title = value;
                    this.RaisePropertyChanged("Title");
                }
            }
        }

        private ObservableCollection<NoteModel> tasks = null;

        /// <summary>
        /// 任務計劃集合
        /// </summary>
        public ObservableCollection<NoteModel> Tasks
        {
            get
            {
                return this.tasks;
            }

            set
            {
                if (this.tasks != value)
                {
                    this.tasks = value;
                    this.RaisePropertyChanged("Tasks");
                }
            }
        }

        /// <summary>
        /// 設置完成狀態
        /// </summary>
        /// <param name="iComplete"></param>
        /// <returns></returns>
        private void SetComplete(NoteModel iModel)
        {
            StringBuilder strSql = new StringBuilder();
            strSql.Append("UPDATE m_agenda SET IsComplete=");
            strSql.Append(iModel.IsCompleted ? "1" : "0");
            strSql.Append(" WHERE Id=");
            strSql.Append(iModel.Id);
            EJiang.Common.DirectDbHelperMysql.ExecuteSql(strSql.ToString());
        }

        private void ChangeDate(object days)
        {
            this.BindDate(this.currentDate.AddDays(Convert.ToInt32(days)));
        }

        /// <summary>
        /// 用於外部調用,刷新信息
        /// </summary>
        public void RefrushInfo()
        {
            this.Tasks = this.GetNoteByDate(SystemConfig.userId, this.currentDate);
        }

        /// <summary>
        /// 綁定信息
        /// </summary>
        /// <param name="date"></param>
        private void BindDate(DateTime date)
        {
            this.currentDate = date;
            this.Title = this.currentDate.ToString("yyyy年MM月dd");
            this.Tasks = this.GetNoteByDate(SystemConfig.userId, this.currentDate);
        }

        /// <summary>
        /// 刪除
        /// </summary>
        /// <param name="id"></param>
        private void Delete(NoteModel model)
        {
            if (MessageBox.Show("確定要刪除該條任務?", "刪除確認", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
            {
                string strSql = "DELETE FROM m_agenda WHERE Id=" + model.Id;
                if (EJiang.Common.DirectDbHelperMysql.ExecuteSql(strSql) > 0)
                {
                    this.Tasks.Remove(model);
                }
            }
        }

        /// <summary>
        /// 獲取日程
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="date"></param>
        /// <returns></returns>
        public ObservableCollection<NoteModel> GetNoteByDate(int userId, DateTime date)
        {
            StringBuilder strSql = new StringBuilder();
            strSql.Append("SELECT ");
            strSql.Append("Id, ");
            strSql.Append("AgendaContent, ");
            strSql.Append("StartDate, ");
            strSql.Append("IsComplete ");
            strSql.Append("FROM m_agenda ");
            strSql.Append("WHERE StartDate = @startDate ");
            strSql.Append("AND UserId = @userId ");
            MySqlParameter[] para = 
            { 
                new MySqlParameter("@userId", userId),
                new MySqlParameter("@startDate", date)
            };

            ObservableCollection<NoteModel> models = new ObservableCollection<NoteModel>();
            DataTable dt = EJiang.Common.DirectDbHelperMysql.Query(strSql.ToString(), para).Tables[0];
            foreach (DataRow dr in dt.Rows)
            {
                models.Add(new NoteModel
                {
                    Id = Convert.ToInt32(dr["Id"]),
                    Content = dr["AgendaContent"].ToString(),
                    IsCompleted = dr["IsComplete"].ToString() == "1" ? true : false,
                    StartDate = Convert.ToDateTime(dr["StartDate"])
                });
            }

            return models;
        }

        /// <summary>
        /// 新增日程
        /// </summary>
        /// <param name="noteModel"></param>
        public void AddAgenda()
        {
            StringBuilder strSql = new StringBuilder();
            strSql.Append("INSERT INTO m_agenda  ");
            strSql.Append("(UserId,");
            strSql.Append("AgendaContent,  ");
            strSql.Append("StartDate,  ");
            strSql.Append("LimitDate) ");
            strSql.Append("VALUES ");
            strSql.Append("(@UserId,  ");
            strSql.Append("@AgendaContent,  ");
            strSql.Append("@StartDate,  ");
            strSql.Append("@LimitDate)");
            MySqlParameter[] para = 
            { 
                new MySqlParameter("@UserId", SystemConfig.userId),
                new MySqlParameter("@AgendaContent", "請輸入您的任務!"),
                new MySqlParameter("@StartDate", this.currentDate),
                new MySqlParameter("@LimitDate",  this.currentDate)
            };

            int id = Convert.ToInt32(EJiang.Common.DirectDbHelperMysql.ExecuteInsert(strSql.ToString(), para));
            if (id > 0)
            {
                this.Tasks.Add(new NoteModel
                {
                    Id = id,
                    Content = "請輸入您的任務!",
                    IsCompleted = false,
                    StartDate = this.currentDate
                });
            }
        }

        /// <summary>
        /// 更新日程
        /// </summary>
        /// <param name="noteModel"></param>
        private void UpdateAgenda(NoteModel noteModel)
        {
            StringBuilder strSql = new StringBuilder();
            strSql.Append("UPDATE m_agenda   ");
            strSql.Append("SET  ");
            strSql.Append("AgendaContent = @AgendaContent    ");
            strSql.Append("WHERE  ");
            strSql.Append("Id = @Id  ");
            MySqlParameter[] para = 
            { 
                new MySqlParameter("@Id", noteModel.Id),
                new MySqlParameter("@AgendaContent", noteModel.Content)
            };

            EJiang.Common.DirectDbHelperMysql.ExecuteSql(strSql.ToString(), para);
        }
    }
}

源碼地址:http://download.csdn.net/detail/wuwo333/6316627


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章