Volunteer .NET Evangelist

A well oiled machine can’t run efficiently, if you grease it with water.
posts - 44, comments - 88, trackbacks - 2, articles - 0
  首页  :: 联系 :: 订阅 订阅  :: 管理

Data Binding In WPF

Posted on 2006-04-06 00:33  Sheva  阅读(...)  评论(...编辑  收藏
    Data binding is a very important feature in every development platform, for all of you who have been using the data binding mechanism of ASP.NET, you must think that data binding in ASP.NET is quite flexible, and extensible, you can declaratively bind the data to any property for any web control, when the data is changed through user interactituon with the interface, the underlying data store will be changed accordingly. fortunately WPF comes up with another quite similar data binding mechanism which can make data access super easy for developers. For demonstration purpose I plan to write a slide show application which will take advantage of data binding capability in WPF, with this application, you can select a set of images through a dialog, and the application will recursively display those images one at a time, so the slide show effects can be achieved.
    First of all, I need to write a helper class which will perform the actual slide show operations behind the scene:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace SlideShow
{
    
public class SlideShowManager: INotifyPropertyChanged
    {
        
public event PropertyChangedEventHandler PropertyChanged;
        
private String[] imageFiles;
        
private Int32 interval = 2;
        
private Int32 currentIndex = 0;
        
private DispatcherTimer timer;
        
private Image targetImage;

        
public SlideShowManager(String[] imageFiles)
        {
            
this.imageFiles = imageFiles;
            timer 
= new DispatcherTimer();
            timer.Interval 
= new TimeSpan(00, interval);
            timer.Tick 
+= TimerOnTick;
            timer.Start();
        }

        
public Image TargetImage
        {
            
get { return targetImage; }
            
set { targetImage = value; }
        }

        
public BitmapImage Current
        {
            
get
            {
                currentIndex 
= (currentIndex + 1% imageFiles.Length;
                
return new BitmapImage(new Uri(imageFiles[currentIndex], UriKind.Absolute));
            }
        }

        
public Int32 Interval
        {
            
get { return interval; }
            
set 
            {
                
if (value > 0)
                {
                    timer.Stop();
                    timer.Interval 
= new TimeSpan(00, value);
                    timer.Start();
                }
            }
        }

        
private void FadeIn()
        {
            DoubleAnimation fadeInAnimation 
= new DoubleAnimation(0.11, TimeSpan.FromSeconds(Interval));
            targetImage.BeginAnimation(Image.OpacityProperty, fadeInAnimation);
        }

        
private void TimerOnTick(Object sender, EventArgs e)
        {
            
if (imageFiles != null && imageFiles.Length > 1)
            {
                FadeIn();
                
if (PropertyChanged != null) PropertyChanged(thisnew PropertyChangedEventArgs("Current"));
            }
        }
    }
}

    Note that this class implements the INotifyPropertyChanged event, for any object which wants to act as data source object, it should  implement this  interface,  basically  this interface  provides  a notifcation  mechanism  in the data  binding scenario,  when any property  of this  class  is  changed,  it will send  a notification to the  data binding  UI elements, so the UI elements can see the change and refresh its visual display accordingly.
    Now I have the house keeping class at hand, what I need then is a UI which hosts an Image element, whose Source property is bound to the SlideShowManager's Current property:
<Window x:Class="SlideShow.MainWindow"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    Title
="SlideShow" Height="300" Width="300"
    
>
  
<Window.CommandBindings>
    
<CommandBinding Command="ApplicationCommands.Open" Executed="OpenCommandExecutedHandler" />
    
<CommandBinding Command="ApplicationCommands.Close" Executed="CloseCommandExecutedHandler" />
  
</Window.CommandBindings>
  
<DockPanel>
    
<Menu DockPanel.Dock="Top">
      
<MenuItem Header="_File">
        
<MenuItem Header="_Open" Name="menuOpen" Command="ApplicationCommands.Open" />
        
<MenuItem Header="E_xit" Name="menuExit" Command="ApplicationCommands.Close"/>
      
</MenuItem>
    
</Menu>
      
<Image x:Name="slideshowImage" Opacity="1"/>
  
</DockPanel>
</Window>

In the code beside file for this XAML file, I write some lines of code like this:
using System;
using System.Windows.Input;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;


namespace SlideShow
{
    
public partial class MainWindow : Window
    {
        
private SlideShowManager manager;
        
public MainWindow()
        {
            InitializeComponent();
        }

        
private void OpenSlideShow()
        {
            Microsoft.Win32.OpenFileDialog dlg 
= new Microsoft.Win32.OpenFileDialog();
            dlg.Filter 
= dlg.Filter = "PNG file(*.png)|*.png|BMP file(*.bmp)|*.bmp|JPEG file(*.jpg)|*.jpg|GIF file(*.gif)|*.gif|Image Files(*.*) | *.*";
            dlg.FilterIndex 
= 3;
            dlg.InitialDirectory 
= System.Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
            dlg.Multiselect 
= true;
            
if (dlg.ShowDialog() == true)
            {
                manager 
= new SlideShowManager(dlg.FileNames);
                manager.Interval 
= 2;
                manager.TargetImage 
= slideshowImage;
                Binding binding 
= new Binding("Current");
                
using (binding.DeferChanges())
                {
                    binding.Mode 
= BindingMode.OneWay;
                    binding.Source 
= manager;
                }

                BindingOperations.SetBinding(slideshowImage, Image.SourceProperty, binding);
            }
        }

        
private void OpenCommandExecutedHandler(Object sender, ExecutedRoutedEventArgs e)
        {
            OpenSlideShow();
            Binding binding = new Binding("Current");
                
using (binding.DeferChanges())
                {
                    binding.Mode 
= BindingMode.OneWay;
                    binding.Source 
= manager;
                }

                BindingOperations.SetBinding(slideshowImage, Image.SourceProperty, binding);

        }

        
private void CloseCommandExecutedHandler(Object sender, ExecutedRoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }
    }
}

    You can see from this code that in the
OpenSlideShow() method, I open up a OpenFileDialog control so user can select a set of images he or she want to show up, and then I create the data binding code like following:
Binding binding = new Binding("Current");
                
using (binding.DeferChanges())
                {
                    binding.Mode 
= BindingMode.OneWay;
                    binding.Source 
= manager;
                }

                BindingOperations.SetBinding(slideshowImage, Image.SourceProperty, binding);
    This is the actual data binding operation I want to talk about, so you must be surprised by how easy data binding can be achieved in WPF. in fact, you also can declaratively perform the data binding operation in XAML, for example:
<Window x:Class="RssReader.Window1"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
         xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    Title
="RssReader" Height="444" Width="619">
  
<Window.Resources>
    
<XmlDataProvider x:Key="RssData" Source="http://sheva.cnblogs.com/Rss.aspx" IsAsynchronous="True"/>
  
</Window.Resources>
  
<DockPanel>
    
<Label DockPanel.Dock="Top" FontSize="14" FontWeight="Bold"
             Content
="{Binding Source={StaticResource RssData}, XPath=/rss/channel/title}"/>
    
<Label DockPanel.Dock="Top"
              Content
="{Binding Source={StaticResource RssData}, XPath=/rss/channel/description}" />
    
<DockPanel DockPanel.Dock="Top"
                    DataContext
="{Binding Source={StaticResource RssData}, XPath=/rss/channel/item}">
      
<ScrollViewer DockPanel.Dock="Left" >
        
<ListBox ItemsSource="{Binding}"
                      Width
="200"
                      IsSynchronizedWithCurrentItem
="True">
          
<ListBox.ItemTemplate>
            
<DataTemplate>
              
<TextBlock Text="{Binding XPath=title}" />
            
</DataTemplate>
          
</ListBox.ItemTemplate>
        
</ListBox>
      
</ScrollViewer>
      
<Frame Source="{Binding XPath=link}" Width="Auto"/>
    
</DockPanel>
  
</DockPanel>
</Window>

This markup can build a very simple RSS reader application, quite stunning is isn't?