EssentialWPF_chapter6_Data

 

Resource:

Static Resources is static assignment, just can be used once.

Dynamic resource

The lookup path for resources is

1. Element hierarchy

2. Application.Resources

3. Type theme

4. System theme

 

A resource needs to be used more than once: FrameworkElementFactory. For elements

in control templates, we create a factory instead of creating the elements

directly.

Most visual objects (brushes, pens, meshes, etc.) dont require a

factory, because they support multiuse by virtue of deriving from

Freezable.3

(Freezable objects support sharing by having a frozen mode, in which they cannot be

changed. The frozen mode allows for a single instance to be used by multiple objects, without

consumers having to watch for changes in the object)

 

<Button Background='{DynamicResource toShare}' Click='Clicked'>

this.Resources["toShare"] = newBrush;

 

button1.SetResourceReference(

Button.BackgroundProperty,

"toShare");

 

 

We can explicitly set the

DataContext property on any element and that data source will be used for

any bindings on that element (or its children).

 

Panel1.DataContext = new Name();

 

When any resource is changed, the entire tree is updated. Therefore,

static and dynamic resource references can be used in many places without

a cost per reference being incurred. What this means is that we should not

make frequent changes of resources to update the UI. It also means that we

should not worry about using lots of resource references.

 

resources are optimized for coarse-grained changes.

 

 

Basic Binding:

 

Binding bind = new Binding();

bind.Source = textBox1;

bind.Path = new PropertyPath("Text");

 

contentControl1.SetBinding(ContentControl.Content, bind);

 

<ContentControl Margin='5' x:Name='contentControl1'

Content='{Binding ElementName=textBox1,Path=Text}' />

 

 

ValueConverter

 

public class HumanConverter : IValueConverter {

public object Convert(object value, Type targetType,

object parameter, CultureInfo culture) {

Human h = new Human();

h.Name = (string)value;

return h;

}

public object ConvertBack(object value, Type targetType,

object parameter, CultureInfo culture) {

return ((Human)value).Name;

}

}

 

 

  <ContentControl  Margin="5" BorderThickness="3">

            <ContentControl.Content >

                <Binding  ElementName="textbox1" Path="Text">

                    <Binding.Converter>

                        <l:HumanConverter></l:HumanConverter>

                    </Binding.Converter>

                </Binding>

            </ContentControl.Content>

            <ContentControl.ContentTemplate >

                <DataTemplate DataType="{x:Type l:Human}">

                    <Border Margin='5' Padding='5' BorderBrush='Black' BorderThickness='3' CornerRadius='5'>

                           <TextBlock Text='{Binding Path=Name}' />

                    </Border>

                </DataTemplate>

            </ContentControl.ContentTemplate>

        </ContentControl>

 

Property Change:

 

To enable our Person object model to support change notification,

we have three options: (1) implement INotifyPropertyChanged,

(2) Add events that report changes to properties, or (3) create Dependency-

Property-based properties.

 

Generally, when creating data models we should implement INotify-

PropertyChanged. Using DependencyProperty-based properties adds the

requirement of deriving from DependencyObject, which in turn ties the

data object to an STA thread.

 

public class Name : INotifyPropertyChanged

    {

        private string _first;

 

        public string First

        {

            get { return _first; }

            set { _first = value; NotifyChanged("First"); }

        } private void NotifyChanged(string property)

        {

            if (PropertyChanged != null)

            {

                PropertyChanged(this,new PropertyChangedEventArgs(property));

            }

       

        }

 

 

        #region INotifyPropertyChanged Members

 

        public event PropertyChangedEventHandler PropertyChanged;

 

        #endregion

}

 

<StackPanel.DataContext >

         <l:Name  x:Name="myName" First="daf" Last="ddfdf"></l:Name>

</StackPanel.DataContext>

<Label Grid.Row='0' Grid.Column='0'>First</Label>

                    <TextBox Grid.Row='0' Grid.Column='1'

                    Text='{Binding Path=First}' />

                    <Label Grid.Row='1' Grid.Column='0'>Last</Label>

<TextBox Grid.Row='1' Grid.Column='1'

                    Text='{Binding  UpdateSourceTrigger=PropertyChanged,Path=Last}' />

 

bind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

 

 

Lists are more complicated than just property changes:

 

INotifyCollectionChanged

 

public class NotifyCollectionChangedEventArgs : EventArgs {

public NotifyCollectionChangedAction Action { get; }

public IList NewItems { get; }

public int NewStartingIndex { get; }

public IList OldItems { get; }

public int OldStartingIndex { get; }

}

public enum NotifyCollectionChangedAction {

Add,

Remove,

Replace,

Move,

Reset,

}

 

The ObservableCollection<T> has inherited INotifyCollectionChanged, so we can use it directly.

   public class Persion

    {

        private IList<Address> addresses = new ObservableCollection<Address>();

 

        public IList<Address> Addresses

        {

            get { return addresses; }

            set { addresses = value; }

        }

 

 

}

public class Address:INotifyPropertyChanged

 

<StackPanel.Resources >

                <DataTemplate x:Key='addressTemplate'>

                    <StackPanel Orientation='Horizontal'>

                        <TextBlock Text='{Binding Path=Street1}' />

                        <TextBlock Text=',' />

                        <TextBlock Text='{Binding Path=City}' />

                        <TextBlock Text=',' />

                        <TextBlock Text='{Binding Path=State}' />

                        <TextBlock Text=',' />

                        <TextBlock Text='{Binding Path=Zip}' />

                        </StackPanel>

                </DataTemplate>

            </StackPanel.Resources>

<StackPanel.DataContext >

                <l:Persion></l:Persion>

            </StackPanel.DataContext>

            <ListBox ItemsSource="{Binding Path =Addresses}" ItemTemplate="{DynamicResource addressTemplate}"> //这里我还在想用IValueConverter,template

 

            </ListBox>

            <Button Click="AddButton">Add</Button>

 

private void AddButton(object sender, RoutedEventArgs e)

        {

            Address a = new Address();

            a.Street1 = "street1";

            a.City = "city";

            a.State = "state";

            a.Zip = "zip";

            ((Persion)(CollectionStackPanel.DataContext)).Addresses.Add(a);

        }

 

 

XML Binding:

public class Window1 : Window {

public Window1() {

XmlDocument doc = new XmlDocument();

doc.LoadXml(...);

XmlDataProvider dataSource = new XmlDataProvider();

dataSource.Document = doc;

Binding bind = new Binding();

bind.Source = dataSource;

bind.XPath = "/Media/Book/@Title";

ListBox list = new ListBox();

list.SetBinding(ListBox.ItemsSourceProperty, bind);

Title = "XML Binding";

Content = list;

}

 

}

<Window x:Class='BookScratch.Window1'

xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'

xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'

Text='XML Binding'

DataContext='{DynamicResource dataSource}'

<Window.Resources>

<XmlDataProvider x:Key='dataSource'>

...

</XmlDataProvider>

</Window.Resources>

<ListBox

ItemsSource= '{Binding XPath=/Media/Book/@Title}' />

</Window>

 

 

DataTemplate:

 

data templates allow us to define how a piece of

data will appear.

 

The DataTemplate type resembles ControlTemplate quite a bit. They

both leverage FrameworkElementFactory to define the display tree. Control-

Template defines a display tree for a control, within which we use template

binding to wire up display properties to control properties. DataTemplate,

however, defines a display tree for a data item, within which we use data

binding to wire up display properties to data properties. DataTemplate also

automatically sets the data context of the display tree to be the template

data item.

 

 

public class Window1 : Window {

public Window1() {

XmlDocument doc = new XmlDocument();

doc.LoadXml(...);

XmlDataProvider dataSource = new XmlDataProvider();

dataSource.Document = doc;

Binding bind = new Binding();

bind.Source = dataSource;

bind.XPath = "/Media/Book";

ListBox list = new ListBox();

list.SetBinding(ListBox.ItemsSourceProperty, bind);

Title = "XML Binding";

Content = list;

}

}

 

///////////create our template

DataTemplate template = new DataTemplate();

template.DataType = typeof(XmlElement);

 

FrameworkElementFactory textFactory =

new FrameworkElementFactory(typeof(TextBlock));

 

Binding bind = new Binding();

bind.XPath="@Title";

textFactory.SetBinding(TextBlock.TextProperty, bind);

 

template.VisualTree = textFactory;

<Window ...

xmlns:sx='clr-namespace:System.Xml;assembly=System.Xml'

Title='XML Binding'

DataContext='{DynamicResource dataSource}'

<Window.Resources>

<XmlDataProvider x:Key='dataSource'>

...

</XmlDataProvider>

<DataTemplate x:Key='template' DataType='{x:Type sx:XmlElement}'>

<TextBlock Text='{Binding XPath=@Title}' />

</DataTemplate>

</Window.Resources>

<ListBox

ItemsSource= '{Binding XPath=/Media/Book }'

ItemTemplate='{DynamicResource template}' />

</Window>

 

 

DataTemplate Selector:

 

DataTemplateSelector provides a single method, SelectTemplate,

that allows us to perform any logic we want to determine which template

to return. We can find the template in the contained element (i.e., ListBox)

and return some hard-coded templates, or even dynamically create a template

for each item.

 

 

<Window ... Title='XML Binding'>

<Window.Resources>

<XmlDataProvider x:Key='dataSource'>

<x:XData>

<Media xmlns=''>

<Book Author='John' Title='Fish are my friends' />

<Book Author='Dave' Title='Fish are my enemies' />

<Book Author='Jane' Title='Fish are my food' />

<CD Artist='Jane' Title='Fish sing well' />

<DVD Director='John' Title='Fish: The Movie'>

<Actor>Jane</Actor>

<Actor>Dave</Actor>

</DVD>

</Media>

</x:XData>

</XmlDataProvider>

</Window.Resources>

<ListBox

ItemsSource =

'{Binding XPath=/Media/Book/@Title,Source={StaticResource dataSource}}'

/>

</Window>

 

 

public class LocalNameTemplateSelector : DataTemplateSelector {

public override DataTemplate SelectTemplate(object item,

DependencyObject container) {

XmlElement data = item as XmlElement;

if (data != null) {

return ((FrameworkElement)container).FindResource(data.LocalName)

as DataTemplate;

}

return null;

}

}

<Window.Resources>

<XmlDataProvider x:Key='dataSource'>

...

</XmlDataProvider>

<DataTemplate x:Key='Book' DataType='{x:Type sx:XmlElement}'>

...

</DataTemplate>

<DataTemplate x:Key='CD' DataType='{x:Type sx:XmlElement}'>

...

</DataTemplate>

<DataTemplate x:Key='DVD' DataType='{x:Type sx:XmlElement}'>

...

</DataTemplate>

</Window.Resources>

<ListBox

ItemsSource= '{Binding XPath=/Media/*}'>

<ListBox.ItemTemplateSelector>

<l:LocalNameTemplateSelector

xmlns:l='clr-namespace:EssentialWPF' />

</ListBox.ItemTemplateSelector>

</ListBox>

 

 

 Hierarchical Binding:

<ListBox ItemsSource='{Binding}'> // a period (.) path can be used to bind to the current source. For example, Text=”{Binding}” is equivalent to Text=”{Binding Path=.}”.

<ListBox

                ItemsSource='{Binding}' Width ="300">

                <ListBox.ItemTemplate>

                    <DataTemplate>

                        <StackPanel>

                            <TextBlock Text='{Binding Path=Name}' />

                            <ListBox Height='75' Width="250">

                            <!-- ItemsSource for the inner ListBox

                            is going to be the collection of

                            child items. -->

                            <ListBox.ItemsSource>

                            <Binding Path='.'>

                            <Binding.Converter>

                            <l:GetFileSystemInfosConverter />

                            </Binding.Converter>

                            </Binding>

                            </ListBox.ItemsSource>

                            <!-- The items inside of the inner ListBox

                            get to be just TextBlocks. -->

                           </ListBox>

                           </StackPanel>

                    </DataTemplate>

                </ListBox.ItemTemplate>

            </ListBox>

"{Binding RelativeSource={x:Static RelativeSource.Self},

                        Path=(Validation.Errors)[0].ErrorContent}"/

 

Use Resource:

<StackPanel Name="filesStackPanel" xmlns:io='clr-namespace:System.IO;assembly=mscorlib'>

            <StackPanel.Resources >

                <DataTemplate DataType='{x:Type io:DirectoryInfo}'>

                    <StackPanel>

                        <TextBlock Text='{Binding Path=Name}' />

                        <ListBox>

                        <ListBox.ItemsSource>

                        <Binding Path='.'>

                        <Binding.Converter>

                        <l:GetFileSystemInfosConverter />

                        </Binding.Converter>

                        </Binding>

                        </ListBox.ItemsSource>

                        </ListBox>

                        </StackPanel>

                </DataTemplate>

            </StackPanel.Resources>        

Wait! Where is the recursion? Data templates can be discovered

by the DataType property. This means that when ListBox sees an

item of type DirectoryInfo, it will automatically find the template that we

just defined. Within that template is a nested list box that will do the same

thing.

 

 

For a control, like TreeView or Menu, that natively supports hierarchy,

 

public class HierarchicalDataTemplate : DataTemplate {

public BindingBase ItemsSource { get; set; }

public DataTemplate ItemTemplate { get; set; }

public DataTemplateSelector ItemTemplateSelector { get; set; }

}

 

Datacontext 是一个directoryInfo[], 但是datatemplate用的时候好像直接作为DirectoryInfo,itemssource 接受collection value.是因为itemsSource 在一个一个调用item时,用directoryInfo template 来处理。

<HierarchicalDataTemplate DataType='{x:Type io:DirectoryInfo}'>

                    <HierarchicalDataTemplate.ItemsSource>

                        <Binding Path='.'>

                            <Binding.Converter>

                                <l:GetFileSystemInfosConverter />

                            </Binding.Converter>

                        </Binding>

                    </HierarchicalDataTemplate.ItemsSource>

                    <TextBlock Text='{Binding Path=Name}' />

                </HierarchicalDataTemplate>

            </StackPanel.Resources>

 

            <TreeView ItemsSource='{Binding}'/>

 

Collection Views:

Up to now we have talked about three objects in play to perform binding:

the data source, the binding, and the target element. With list binding

there is actually a fourth player: the collection view.

We can think of the collection view as a lightweight wrapper on the underlying data that allows us to have multiple views on top of the same data.

 

with larger data sets it

becomes increasingly important not to load the data into memory more

than once.

同一组数据,不同的view

 

The most important service that a collection view supplies is currency

management, tracking the current item in a list. IsSynchronizedWithCurrentItem

This property synchronizes the list selection with the current item in the view.

 

 

posted on 2008-09-22 19:54  oyl  阅读(233)  评论(0编辑  收藏  举报

导航