显示标签为“数据绑定”的博文。显示所有博文
显示标签为“数据绑定”的博文。显示所有博文

星期二, 一月 09, 2007

Master-detail情形

Master-detail情形

原文出处:http://www.beacosta.com/Archive/2005_11_01_bcosta_archive.html

最简单的Master-detail情形就是,点击ItemsControl特定,使该项的细节在另一个控件中显示出来.例如,一个程序在ListBox 中显示了一个顾客名字列表,当点击了一个特定的顾客时,TextBlock就更新这个顾客的地址,电话号码和出生日期.

在这个帖子中我将使用太阳系的行星作为数据源:点击ListBox中的行星名字,使该行星的图片与信息显示在
被模板化的ContentControl上.ListBox扮演master的角色,ContentControl呈现细节.

在Window的资源段落,有一个提供行星数据的XmlDataProvider和一个CollectionViewSource,该 CollectionViewSource绑定Source属性到这个provider(要获得更多关于CollectionViewSource的信息,可以参考我前面的帖子.)这里是ListBox绑定到CollectionViewSource的markup:

    <!-- master -->
    <ListBox ItemsSource="{Binding Source={StaticResource cvs}}" DisplayMemberPath="@Name" Padding="5" Margin="0,0,5,0"/>

我还需要一个ContentControl来显示选择项的细节.下面的markup看起来有点奇怪:我们绑定ContentControl(只能显示单项)到很多项的集合?(注意看它的绑定和ListBox的itemsSource的绑定很像).这个markup工作的很好,因为数据绑定引擎足够聪明,它能分辨出这两个目标.当绑定ItemsControl到数据集合,我们能得到整个集合, 当我们绑定ContentControl到数据集合,我们得到的是集合的当前项.这就是在wpf中实现Marster-detail如此简单的缘故了.

    <!-- detail -->
    <ContentControl ContentTemplate="{StaticResource detailTemplate}" Content="{Binding Source={StaticResource cvs}}"/>

为了详细说明如何在ContentControl中显示行星的细节数据,我们使用了DataTemplate.下面的markup显示了 DataTemplate的数据绑定部分.注意,因为我们是绑定到XML,因此绑定使用XPath而不是Path.

    <DataTemplate x:Key="detailTemplate">
        (...)
        <Image Source="{Binding XPath=Image, Converter={StaticResource stringToImageSource}}" />
        (...)
        <StackPanel Orientation="Horizontal" Margin="5,5,5,0">
            <TextBlock Text="Orbit: " FontWeight="Bold" />
            <TextBlock Text="{Binding XPath=Orbit}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="5,0,5,0">
            <TextBlock Text="Diameter: " FontWeight="Bold"/>
            <TextBlock Text="{Binding XPath=Diameter}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="5,0,5,5">
            <TextBlock Text="Mass: " FontWeight="Bold"/>
            <TextBlock Text="{Binding XPath=Mass}" />
        </StackPanel>
        (...)
    </DataTemplate>

这里就是该示例的截图:


星期五, 一月 05, 2007

如何同步两个绑定数据的ListBox的选择项?

如何同步两个绑定数据的ListBox的选择项?


原文出处:http://www.beacosta.com/Archive/2005_11_01_bcosta_archive.html 我将会向你展示两种方法来同步绑定数据的ListBox的选择项。

在第一种解决方案中,我将会创建数据集合的定制view,然后把两个ListBox都绑定到它上面。view能够跟踪它背后数据集合的当前项,允许我们排序,组合,过滤它的数据项。CollectionViewSource是九月CTP发布的一个新的类,能够用标识来创建一个定制的view。因为在这个例子中,创建的定制的view所跟踪数据集合的当前项,currency,选择项都是同步的,因此绑定两个ListBox到相同的view能够让他们的选择项同步。

    <Window.Resources>
        <local:GreekGods x:Key="source" />
        <CollectionViewSource Source="{StaticResource source}" x:Key="cvs"/>
    </Window.Resources>



    <ListBox ItemsSource="{Binding Source={StaticResource cvs}}" DisplayMemberPath="Name"/>
    <ListBox ItemsSource="{Binding Source={StaticResource cvs}}" DisplayMemberPath="Name"/>

我将会在以后的帖子中介绍如何使用CollectionViewSource来排序,分组,和过滤数据项。

另一个方法也能达到同样的效果,就是设置ItemsSource属性为数据源,同时设置IsSynchronizedWithCurrentItem属性为true:

    <ListBox ItemsSource="{StaticResource source}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"/>
    <ListBox ItemsSource="{StaticResource source}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"/>

这个markup有此功能是因为当绑定到一个集合时总是会创建一个view,如果你没有指定一个view,系统就会创建一个默认的view。虽然这个 view和定制的view用同样的方式跟踪数据,但如果你有默认的view,currency和选择项是默认不同步的。改变这个默认的行为的方法就是设置 IsSynchroizedWithCurrentItem属性为true

这些数据组通过定制的回馈来区分定制views和默认views之间的默认同步方式的不同之处。这种方法能让玩家了解到view的概念,且能够根据计自己的需要精确的指定哪些能够同步,哪些不能。

下面的图片中,第一和第二个ListBox是绑定到CollectionViewSource,第三第四个设置 InSynchronizedWithCurrentItem为true.

星期三, 十二月 27, 2006

如何实现条形图的数据绑定

如何实现条形图的数据绑定


原文出处:http://www.beacosta.com/Archive/2005_10_01_bcosta_archive.html
Avalon的风格和模板特征与绑定数据的ItemsControl结合可以创建一个简单的条形图.ItemsControl是一个用来显示一系列项的控件.这些项可以是你想要的任何东西:人,数字,控件等等.如果一个ItemsControl的每一项的都可以用矩形来表示,且矩形的高度绑定到数字的数据,你就可以得到一个绑定数据的条形图.

这个例子的数据源是一个带ValueCollection属性的类,ValueCollection的类型为
ObservableCollection<int>.ObservableCollection<T>继承自 INotifyCollectionChanged,INotifyCollectionChanged表示当从集合增加/移除/覆盖项时, 数据绑定引擎将会通知这些变化,界面将会得到更新.

这里是ItemsControl的表示法:

    <ItemsControl ItemsSource="{Binding Source={StaticResource source}, Path=ValueCollection}"
        ItemTemplate="{StaticResource template}" Height="130">
        <ItemsControl.ItemsPanel>
            <StackPanel Orientation="Horizontal" />
        </ItemsControl.ItemsPanel>
    </ItemsControl>

ItemsControl的默认Panel是垂直方向的,但是我们想要各项按照水平方向显示-每一个条形都在前一个的右边.为了改变Panel,我们修改了ItemsControl的ItemsPanel属性(关于如何改变ItemsControl的Panel的细节请察看我的上一片帖子).< br/>
每项的模板包含一个高度绑定到相应整数数据源的矩形,和一个没有填充的矩形来增加条形之间的间隔:

    <DataTemplate x:Key="template">
        <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom">
            <Rectangle Height="{Binding}" Width="20" Fill="Red" />
            <Rectangle Width="5" />
        </StackPanel>
    </DataTemplate>

这是一个很简单的例子,但是它将带给你一些思路,是处理更复杂数据的基础.结果表现如下:

如何改变ItemsControl的布局

如何改变ItemsControl的布局


原文出处:http://www.beacosta.com/Archive/2005_10_01_bcosta_archive.html
在这个例子中,我将会介绍两个方法来改变ItemsControl的布局.为了回答我前面帖子的评论,这个例子使用XmlDataProvider,它可以绑定到XML数据.

改变ItemsControl布局的最简单的方法就是简单的设置ItemsPanel属性为你想要保存条目的Panel

    <ListBox ItemsSource="{Binding Source={StaticResource xmlData}}" (...) >
        <ListBox.ItemsPanel>
            <StackPanel Orientation="Horizontal" />
        </ListBox.ItemsPanel>
    </ListBox>

另外就是创建一个ControlTemplate,它允许被更广泛的定制.ControlTemplate允许你用替换掉整个VisualTree,包括选择一个新的Panel来保存条目.例如,下面的标记就显示了一个ControlTemplate,它增加一个了Border且改变了 ItemsControl的Panel.

    <ControlTemplate x:Key="listBoxTemplate">
        <Border BorderBrush="Orange" BorderThickness="2" Margin="10,0,10,10">
            <StackPanel Orientation="Horizontal" IsItemsHost="True" />
        </Border>
    </ControlTemplate>
    
    <ListBox ItemsSource="{Binding Source={StaticResource xmlData}}" Template="{StaticResource listBoxTemplate}" (...) />

Most people get this far in this scenario,但是忘记了设置Panel的IsItemsHost的属性.IsItemHost指明"使用这个Panel来放置 ItemsControl中的条目".注意,选择还是可以照常使用的.

如果你想让条目能够自动换行,你可以使用WrapPanel代替StackPanel.在这种情形下,要牢记ListBox的默认template包含一个ScrollViewer,因此你的条目是不会自动换行的.为了让它们换行你可以设置你自己的ControlTemplate,如果你不需要选择功能, 可以使用ItemsControl替代ListBox.

我前面提过,我使用XmlDataProvider绑定XML数据.这是我如何转化前面例子中使用的GreekGods运行时数据源:< br/>

    <Window.Resources>
        <XmlDataProvider XPath="/GreekGods/GreekGod" x:Key="xmlData">
            <GreekGods xmlns="">
                <GreekGod>
                    <Name>Aphrodite</Name>
                    <Description>Goddess of love, beauty and fertility</Description>
                    <RomanName>Venus</RomanName>
                </GreekGod>
                (...)
            </GreekGods>
        </XmlDataProvider>
    </Window.Resources>

你只需要记住在绑定对象中使用XPath属性代替Path属性.使用DisplayMemberPath格式时,Path和XPath都可以使用.

SelectedValue和SelectedItem有什么不同?

SelectedValue和SelectedItem有什么不同?


http://www.beacosta.com/Archive/2005_10_01_bcosta_archive.html
当他们被分别使用时,这两个属性是很相象的.但当同时使用且设置了SelectedValuePath时,不同之处就显示出来了.

例如,考虑到我们熟悉的GreekGods数据源,我通过代码设置StackPanel的DataContext为这个集合:

    GreekGods items;
    items = new GreekGods();
    mainStackPanel.DataContext = items;

且使用空的Binding绑定到ListBox的集合上.我想通过"Messenger of the Gods"描述来选择GreekGod(即使我只显示每个God的名字).这时SelectedValuePath就很有用了.每个ListBox的项都是一个GreekGod物件. 通过设置SelectedValuePath为"Description",我能够深入到每个GreekGod的Description属性.这样我只要设置SelectedValue为我所想要的描述就可以选择该项了.
    
        
ItemsSource="{Binding}" DisplayMemberPath="Name"
SelectedValue="Messenger of the Gods" SelectedValuePath="Description"
Name="listBox1" (...) />
    

SelectedValue和SelectedItem的不同就显而易见了.SelectedValue返回它所设置的字符串("Messenger of the Gods"),但是SelectedItem返回那个描述所指定的GreekGod对象.

    string messengerOfGods = (string)(listBox1.SelectedValue);
    GreekGod hermes = (GreekGod)(listBox1.SelectedItem);

当只需要绑定模型上的部分数据时,SelectedValue就很有用了.你可以绑定SelectedValue属性到你模型的部分信息上,而ListBox却能显示更多的信息.

如果你知道如何结合这两个属性为一个,请告诉我们.
If you have ideas of how to combine these two properties in one, we would love to hear it.

星期二, 十二月 26, 2006

九月PDC新的特性:DisplayAMemberPath

九月PDC新的特性:DisplayAMemberPath


原文出处:http://www.beacosta.com/Archive/2005_10_01_bcosta_archive.html 就像我以前的帖子所表现的,绑定一个ItemsControl到一个IEnumerable数据源确实是很容易(记住ListBox和ComboBox继承自ItemsControl).当你想以文字形式显示每个数据项的单个属性时,利用九月PDC介绍的DisplayMemberPath,就更容易了.在DisplayMemberPath出现之前,这种情形需要使用到DataTemplate来指定我们所兴趣的属性.就像这样的xaml:

    <Window.Resources>
        <DataTemplate x:Key="itemTemplate">
            <TextBlock Text="{Binding Path=Name}" />
        </DataTemplate>
    </Window.Resources>

    <ItemsControl ItemsSource="{StaticResource greekGods}" ItemTemplate="{StaticResource itemTemplate}" />

数据绑定团队意识到这是一个非常常见的情形,需要简单化.这就是产生ItemsControl的DisplayMemberPath属性的缘故了.上面的这种情形现在可以用一行的xaml做到:

    <ItemsControl ItemsSource="{StaticResource greekGods}" DisplayMemberPath="Name" />

很容易吧:)

下面的图片显示了两种版本的ItemsControl,左边的使用DataTemplate,右边的使用DisplayMemberPath.


以下不是翻译内容:
奇怪的是作者的例子显示出来的并不是这样图片.修改以下.:)

share your files at box.net

星期一, 十二月 25, 2006

如何绑定ComboBox的Items?如何从绑定的ComboBox中获取ComboBoxItem?

如何绑定ComboBox的Items?如何从绑定的ComboBox中获取ComboBoxItem?


原文出处:http://www.beacosta.com/Archive/2005_09_01_bcosta_archive.html
绑定ComboBox的Items和绑定ListBox的Items是很象的:

<Window.Resources>
    <local:GreekGods x:Key="greekGods"/>

    <DataTemplate x:Key="itemTemplate">
        <TextBlock Text="{Binding Path=Name}" />
    </DataTemplate>
</Window.Resources>

<ComboBox ItemsSource="{StaticResource greekGods}" ItemTemplate="{StaticResource itemTemplate}" Width="200" Name="comboBox"/>

相似的原因是因为ComboBox和ListBox都是从ItemsControl继承来的,且ItemsSource和ItemTemplate都是ItemsControl的属性.

如果你读过前面关于如果从绑定数据的ListBox中获取一个ListBoxItem的帖子,你或许会认为没有必要继续阅读如何为ComboBox做同样的事.但是那里还是有一些小窍门你必须知道的. 

如果你使用与前面帖子相似的代码去解决问题,那就会发现ComboBoxItems是null:

GreekGod greekGod = (GreekGod)(comboBox.Items[0]);
ComboBoxItem cbi1 = (ComboBoxItem)(comboBox.ItemContainerGenerator.ContainerFromIndex(0));
ComboBoxItem cbi2 = (ComboBoxItem)(comboBox.ItemContainerGenerator.ContainerFromItem(comboBox.Items.CurrentItem));

这是因为ComboBox只会在你打开它时才会有产生的items.所以这个窍门就是当你调用ContainerFromIndex或ContainerFromItem前打开ComboBox:

GreekGod greekGod = (GreekGod)(comboBox.Items[0]);
comboBox.IsDropDownOpen = true;
ComboBoxItem cbi1 = (ComboBoxItem)(comboBox.ItemContainerGenerator.ContainerFromIndex(0));
ComboBoxItem cbi2 = (ComboBoxItem)(comboBox.ItemContainerGenerator.ContainerFromItem(comboBox.Items.CurrentItem));
comboBox.IsDropDownOpen = false;


由于原来作者的代码比较早期,vs更新后无法编译了.自己小小修改以下:

share your files at box.net

星期六, 十二月 23, 2006

如何从数据绑定的ListBox中获取ListBoxItem?

如何从数据绑定的ListBox中获取ListBoxItem?

原文出处:http://www.beacosta.com/Archive/2005_09_01_bcosta_archive.html

在WPF中数据绑定一个ListBox到一个列举物品并不是那么容易.

<Window.Resources>
    <local:GreekGods x:Key="greekGods"/>

    <DataTemplate x:Key="itemTemplate">
        <TextBlock Text="{Binding Path=Name}" />
    </DataTemplate>
</Window.Resources>

<ListBox ItemsSource="{StaticResource greekGods}" ItemTemplate="{StaticResource itemTemplate}" Name="listBox"/>

ListBox的ItemSource属性有一个IEnumerable,就是你想要显示的物体列表.在这里GreekGods数据源是ObservableCollection(继承自IEnumerable)类型.ItemTemplate属性指定DataTemplate,DataTemplate是用来控制如何显示数据的.在这个例子中,每一项都有一个TextBlock来显示GreekGod的名字.

一些人可能会很惊讶,因为运行代码中的ListBox.Items[i]会返回我们所绑定的数据而不是TextBlock或者ListBoxItem.我觉得,能够 很容易的从ListBox中获取到特定位置的数据是很酷的,因为这是我们通常想要的.

GreekGod greekGod = (GreekGod)(listBox.Items[0]);

但是当你确实想获取产生的ListBoxItem时该如何做呢?这个有一些小窍门去发掘,但是利用下面的代码也很容易做到:

ListBoxItem lbi1 = (ListBoxItem)(listBox.ItemContainerGenerator.ContainerFromIndex(0));

另外ListBox.ItemContainerGenerator.ContainerFromItem(object item)通过指定item也能返回ListBoxItem.这个方法很经常使用到,例如,从CurrentItem获取ListBoxItem:

ListBoxItem lbi2 = (ListBoxItem)(listBox.ItemContainerGenerator.ContainerFromIndex(listBox.Items.CurrentItem));

我将会在以后的帖子中详细的介绍selection和current item.但在这个例子中,这个还是很有必要知道的,为了保持selection和current item的同步性,我设置了ListBox的IsSynchronizedWithCurrentItem="true".



由于原来作者的代码比较早期,vs更新后无法编译了.自己小小修改以下:

share your files at box.net

星期五, 十二月 22, 2006

"{Binding}"是什么意思?

"{Binding}"是什么意思?

原文出处:http://www.beacosta.com/Archive/2005_09_01_bcosta_archive.html

你在例子中看到的绑定大都有设置Source和Path属性.Source指定你所绑定的物体,Path指定该物体中你所兴趣的属性.

我看到过一些人第一次看到"{Binding}"时很困惑.它乍看起来就像我们没有为绑定给出足够的信息似的,这是不正确,我将会解释为什么.

如果你读过我以前的帖子,你就会知道并不是一定要为绑定设置Source的,只要在元素树的上层设置过DataContext就可以了.

至于Path,如果你想绑定整个物体,而只是物体的一个属性,就可以不要Path.一种情形就是当你只是简单的想绑定一个String(而不是它的Length属性)

<Window.Resources>
    <system:String x:Key="helloString">Hello</system:String>
</Window.Resources>

<Border DataContext="{StaticResource helloString}">
    <TextBlock TextContent="{Binding}"/>
</Border>

另一种常见的情形就是你想绑定一个元件到一个物体的多个属性.

<Window.Resources>
    <local:GreekGod Name="Zeus" Description="Supreme God of the Olympians" RomanName="Jupiter" x:Key="zeus"/>
</Window.Resources>

<Border DataContext="{StaticResource zeus}">
    <ContentControl Content="{Binding}"/>
</Border>

在这种情况下ContentControl不知道如何显示GreekGod数据.因此你只能看到ToString()的结果,当然这通常不是你想要的.但你可以使用DataTemplate,它允许你指定你数据的外观.

<Window.Resources>
    <local:GreekGod Name="Zeus" Description="Supreme God of the Olympians" RomanName="Jupiter" x:Key="zeus"/>

    <DataTemplate x:Key="contentTemplate">
        <DockPanel>
            <TextBlock Foreground="RoyalBlue" TextContent="{Binding Path=Name}" />
            <TextBlock TextContent=":" Margin="0,0,5,0" />
            <TextBlock Foreground="Silver" TextContent="{Binding Path=Description}" />
        </DockPanel>
    </DataTemplate>
</Window.Resources>

<Border DataContext="{StaticResource zeus}">
    <ContentControl Content="{Binding}" ContentTemplate="{StaticResource contentTemplate}"/>
</Border>

注意,任何DataTemplate内部的绑定声明都没有Source.那是因为它有一个DataContext被自动设置为被模板化的数据物件了.


由于原来作者的代码比较早期,vs更新后无法编译了.自己小小修改以下:

share your files at box.net

星期四, 十二月 21, 2006

[翻译]如何决定使用DataContext还是Source?

刚好最近在学习wpf的数据绑定,也想学点英语,所以试着翻译一下有关数据绑定的文章.
这是第一次翻译的文章,还好这篇不是很长:)

如何决定使用DataContext还是Source?

原文出处:http://www.beacosta.com/Archive/2005_09_01_bcosta_archive.html

DataContext是数据绑定的最根本的概念之一
绑定的物件需要从其它地方获取数据,有几种方法能够指定数据的来源.在这篇中我将讲解两种方法,一种是直接在绑定中设定Source属性,另一种是从元素树中最近的上层继承DataContext.另外还有两种替代方法,就是在绑定物件中分别设置ElementName和RelativeSource属性,这个我会留到以后的帖子中说.

例如,假设我们有下面的这些数据源(GreekGod是一个类,定义在在下面的代码中)

<Window.Resources>
   
<local:GreekGod Name="Zeus" Description="Supreme God of the Olympians" RomanName="Jupiter" x:Key="zeus"/>
   
<local:GreekGod Name="Poseidon" Description="God of the sea, earthquakes and horses" RomanName="Neptune" x:Key="poseidon"/>
</Window.Resources>

<StackPanel DataContext="{StaticResource poseidon}">
   
<TextBlock TextContent="{Binding Source={StaticResource zeus}, Path=Name}"/>
   
<TextBlock TextContent="{Binding Path=Description}"/>
   
<TextBlock TextContent="{Binding Path=RomanName}"/>
</StackPanel>

第一个TextBlock继承了继承了StackPanel的DataContext而且绑定的物件也设置了Source.在这种情况下,Source具有优先权,因此该TextBlock绑定到key为"zeus"的资源的Name属性上,显示"Zeus"

第二个TextBlock没有直接设置绑定物件的Source,因此它继承了StackPanel的DataContext.就像你想的这样,它绑定到Key为"poseidon"的资源的Description属性上,显示
"God of the sea, earthquakes and horses".

第三个TextBlock就很明了了,显示"Neptune"

我所看到的数据相关程序,大部分都趋向与使用DataContext而非Source.我的建议是:DataContext只使用在需要绑定数据源的多个属性时.当只要绑定数据源的一个属性时,我通常使用Source.这样方便调试,我只需要在一个地方就能了解到所有的绑定信息,而无需寻找最近的DataContext.在一个小例子中,就像这个例子,看不出很大的好处,但在复杂的程序中就可以节约你不少时间.




由于原来作者的代码比较早期,vs更新后无法编译了.自己小小修改以下:


share your files at box.net