다음을 통해 공유


Where does a Binding find its data?

If you’ve look at much WPF Xaml you’ve probably seen bindings like this:

 

<TextBlock Text="{Binding Name" />

 

… which binds the Text property of the TextBlock to the Name property of some data object.

 

The question that begets is: where does the data come from? The rest of this post looks at the answer.

 

 

Properties on Binding

 

The Binding class has a few properties that provide ways to let you set the source of the data onto the Binding, depending on what works best for your situation. Binding looks like this:

 

public class Binding : BindingBase

{

    ...

    public object Source { get; set; }

    public string ElementName { get; set; }

    public RelativeSource RelativeSource { get; set; }

    ...

}

 

 

The Source Property

 

The most straightforward way to set the source of the Binding is to use the Source property. One of the more common ways to do that is to reference the data out of a resource dictionary:

 

<Grid>

  <Grid.Resources>

    <Int32Collection x:Key='DataSource1'>1,2,3</Int32Collection>

  </Grid.Resources>

  <ListBox ItemsSource='{Binding Source={StaticResource DataSource1}}' />

</Grid>

 

(This example creates a ListBox with 3 entries in it, labeled “1”, “2”, and “3”.)

 

You can be even more explicit by setting the Source directly, specifying the Binding in full Xml syntax (rather than the above markup extension syntax):

 

<Grid>

  <ListBox>

    <ListBox.ItemsSource>

      <Binding>

        <Binding.Source>

          <Int32Collection>1,2,3</Int32Collection>

        </Binding.Source>

      </Binding>

    </ListBox.ItemsSource>

  </ListBox>

</Grid>

 

You can also set the Binding.Source by referencing a static member:

 

namespace MyNamespace

{

    class MyClass

    {

        public static List<int> MyData;

        static MyClass()

        {

            MyData = new List<int>();

            MyData.Add(1);

            MyData.Add(2);

            MyData.Add(3);

        }

    }

}

 

 

<Page

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

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

    xmlns:my="clr-namespace:MyNamespace" >

  <ListBox ItemsSource='{Binding Source={x:Static my:MyClass.MyData}}' />

</Page>

 

 

The ElementName Property

 

Aside from the Binding.Source property, you can also reference your data by name, using Binding.ElementName, such as:

 

<StackPanel>

  <TextBox Name='TextBox1' />

  <Button Content='{Binding Text, ElementName=TextBox1}' />

</StackPanel>

 

(This example causes the button label to be whatever you type into the text box.)

 

 

The RelativeSource Property

 

The RelativeSource property provides a way to tell the Binding to look around where it’s used to find its source. For example, the ‘Self’ RelativeSource binds to the object on which the Binding is placed, such as in the following (which creates a TextBlock that says “Hi”):

 

<TextBlock Tag='Hi' Text='{Binding Tag, RelativeSource={RelativeSource Self}}' />

 

As another example, the following binds the TextBlock.Text property to the Grid in its ancestry (again displaying “Hi” in this case):

 

<Grid Tag='Hi'>

  <Border Background='LightGray'>

    <TextBlock

         Text='{Binding Tag, RelativeSource={RelativeSource FindAncestor, AncestorType=Grid}}' />

  </Border>

</Grid>

 

The other two modes of a RelativeSource are TemplatedParent and PreviousData.

 

Explicit DataContext Property

 

A common way the Binding can get its source is via a DataContext property set on an element (on the element itself, not on the Binding). For (a rather boring) example, this creates a Button that says “Click”:

 

<Button Content='{Binding}'>

  <Button.DataContext>

    <sys:String>Click</sys:String>

  </Button.DataContext>

</Button>

 

This gets more interesting and typical if you set the DataContext somewhere else, usually on the root of the page/window. That works because the DataContext property inherits. The following example shows that by setting the DataContext onto the root. In this case, it is set to be an XmlDataProvider which is querying a Yahoo web service for the weather in Barrow, Alaska. Since the DataContext inherits, Bindings throughout the page have access to it automatically. Note also in this case that since we’re binding to XML, the Bindings are using XPath syntax.

 

<Page   

    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

    >

  <Page.DataContext>

    <XmlDataProvider

            Source="https://xml.weather.yahoo.com/forecastrss?p=99723"

            XPath="/rss/channel"/>

  </Page.DataContext>

  <Border BorderBrush="Black"

          BorderThickness="1" Width="370" Height="170" CornerRadius="6">

    <StackPanel>

      <!-- Image for "Yahoo! News" -->

      <Image Margin="15,15,0,0"

                   Stretch="None"

                   HorizontalAlignment="Left"

                   Source="{Binding XPath=image/url}" />

      <!-- Text to say e.g. "Yahoo! Weather - Bellevue, WA", with a hyperlink to a

           detailed weather page -->

      <TextBlock Margin="15,15,0,0">

        <Hyperlink NavigateUri="{Binding XPath=item[1]/link}">

          <TextBlock Text="{Binding XPath=title}"/>

        </Hyperlink>

      </TextBlock>

      <!-- Text to say e.g. "Conditions for Belleveue, WA at 9:53am ..." -->

      <TextBlock FontWeight="Bold" Margin="15,15,0,0"

                 Text="{Binding XPath=item[1]/title}"/>

      <!-- Weather details -->

      <TextBlock Margin="15,0,0,0">

        <!-- Text to say current condition and temp -->

        <TextBlock>

          <TextBlock.Text>

            <Binding >

              <Binding.XPath>

                item[1]/*[local-name()="condition" and

                namespace-uri()="https://xml.weather.yahoo.com/ns/rss/1.0"]/@text

              </Binding.XPath>

            </Binding>

          </TextBlock.Text>

        </TextBlock>,

        <TextBlock>

          <TextBlock.Text>

            <Binding >

              <Binding.XPath>

                item[1]/*[local-name()="condition" and

                namespace-uri()="https://xml.weather.yahoo.com/ns/rss/1.0"]/@temp

      </Binding.XPath>

            </Binding>

          </TextBlock.Text>

        </TextBlock>°

        <TextBlock>

          <TextBlock.Text>

            <Binding >

              <Binding.XPath>

                *[local-name()="units" and

                namespace-uri()="https://xml.weather.yahoo.com/ns/rss/1.0"]/@temperature

              </Binding.XPath>

            </Binding>

          </TextBlock.Text>

        </TextBlock>

        <LineBreak/>

        <!-- Text to say sunrise/sunset times -->

        Sunrise:

        <TextBlock>

          <TextBlock.Text>

            <Binding >

              <Binding.XPath>

                *[local-name()="astronomy" and

                namespace-uri()="https://xml.weather.yahoo.com/ns/rss/1.0"]/@sunrise

              </Binding.XPath>

            </Binding>

          </TextBlock.Text>

        </TextBlock>, sunset:

        <TextBlock>

          <TextBlock.Text>

            <Binding >

              <Binding.XPath>

                *[local-name()="astronomy" and

                namespace-uri()="https://xml.weather.yahoo.com/ns/rss/1.0"]/@sunset

              </Binding.XPath>

            </Binding>

          </TextBlock.Text>

        </TextBlock>

      </TextBlock>

    </StackPanel>

  </Border>

</Page>

 

… Sunrise at midnight, sunset at noon:

 

[ImageAttachment]

 

 

Implicit DataContext

 

Finally, there are a couple of places where the DataContext gets set automatically for you. That usually happens with ContentControl (e.g. Button) and ItemsControl (e.g. ListBox).

 

Here’s a ContentControl example. In this markup we have a DataTemplate set up in a ResourceDictionary so that it will be used for any ContentControl that has a String as its content. So it’s automatically picked up in the subsequent Button. In that DataTemplate, we have a binding to the data item, which in this case is the ContentControl.Content. Therefore what we get here is a Button that says “Click” in bold italic.

 

<Page   

    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:sys="clr-namespace:System;assembly=mscorlib" >

  <Page.Resources>

    <DataTemplate DataType="{x:Type sys:String}">

      <!-- The Text property is bound to the Content property of

           whatever ContentControl with which this DataTemplate is used -->

      <TextBlock Text='{Binding}' FontStyle='Italic' FontWeight='Bold' />

    </DataTemplate>

  </Page.Resources>

  <Button>Click</Button>

</Page>

 

 

And here’s an ItemsControl example. In this markup, we have a ListBox bound to an integer collection again (using the inherited DataContext as the source), but this time we have an item template, which is a template to use when displaying the items in the ListBox. And within that item template, the DataContext is again automatically set to be the item

 

<Page   

    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x=https://schemas.microsoft.com/winfx/2006/xaml >

  <Page.DataContext>

    <Int32Collection>1,2,3</Int32Collection>

  </Page.DataContext>

  <!-- ItemsSource is bound to the DataContext (Int32Collection) -->

  <ListBox ItemsSource='{Binding}' >

    <ListBox.ItemTemplate>

      <DataTemplate>

        <DataTemplate.Triggers>

          <!-- Within the DataTemplate, the DataContext is set to be the data item.

               So this DataTrigger.Binding property's DataContext is going to be

               the item (1, 2, or 3) -->

          <DataTrigger Binding='{Binding}' Value='2'>

            <Setter Property='TextBlock.FontStyle' Value='Italic' />

            <Setter Property='TextBlock.FontWeight' Value='Bold' />

          </DataTrigger>

        </DataTemplate.Triggers>

        <Border Padding='10'>

          <!-- Similarly this Text property is bound to the item (1, 2, or 3) -->

          <TextBlock Text='{Binding}' />

        </Border>

      </DataTemplate>

    </ListBox.ItemTemplate>

  </ListBox>

</Page>

 

 

 

barrow.jpg

Comments

  • Anonymous
    May 30, 2007
    Microsoft Surface: What WPF Can Do For You [Via: Ashish Shetty ] Where does a Binding find its data?...

  • Anonymous
    May 14, 2008
    Hey Mike, this is a really nice writeup on XAML binding... maybe one of the better ones out there... thanks. =D

  • Anonymous
    September 24, 2008
    Great write-up.  A good resource to point ppl to.

  • Anonymous
    December 14, 2009
    Thank you very much for this helpful and informative article! There's more information on this PDF... http://docs.google.com/viewer?url=http://www.nbdtech.com/Free/WpfBinding.pdf&pli=1

  • Anonymous
    September 27, 2012
    Good one Mike.