下面这个例子本身来自微软官网,由于没有找到官网源码,我在这里自己实现了一下。还是向官网致敬。
什么是Master-Detail?直接上个图给大家看,就知道了:
这看起来真复杂,点中ListBox里出现的人名,下面要出现这个人的具体信息:姓、名、籍贯。这种应用场景还真不少。如果能够掌握岂不是爽,客官不急,下面听我慢慢道来。
类似像上面这样子在某个列表中选中一项信息概要,旁边某处就能够显示选中元素相应的详细信息的,微软就称之为Master-Detail应用。
好了,像上面这个例子怎么实现呢?我们一步一步来,首先我们想到的肯定是要实现一个Person类,这个类包含姓、名、籍贯。这个类的代码相信大部分人都会写,如下所示:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string HomeTown { get; set; }
public Person(string f, string l,string h)
{
FirstName = f;
LastName = l;
HomeTown = h;
}
public override string ToString()
{
return FirstName;
}
}
看,这个类里面看不到一丁点与界面交互的意思,全是赤裸裸的纯C#代码。不过,这里隐隐的有个ToString()需要稍微解释下:
Person类实现了ToString()方法,这样子ListBox绑定包含Person的集合后,不显示设置DisplayMemberPath,那么就会默认调用Person的ToString()来显示每个项的内容到ListBox控件当中。
下面是定义People类,这个类是Person的集合:
public class People : ObservableCollection<Person>
{
public People()
{
Add(new Person("Yun", "Ma", "HangZhou"));
Add(new Person("Qiangdong", "Liu", "NanJing"));
Add(new Person("Yanhong", "Li", "BeiJing"));
}
}
很清晰,上面代码所示,添加了三个人的信息。注意没有一丁点和界面交互的意思。
上面的两段代码添加到你的MainWindows类所在的c#源文件当中。
XMAL中的代码如下所示:
<Window.Resources>
<local:People x:Key="MyFriends"/>
<DataTemplate x:Key="DetailTemplate">
<Border Width="300" Height="100" Margin="20"
BorderBrush="Aqua" BorderThickness="1" Padding="8">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="First Name:"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=FirstName}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Last Name:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=LastName}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Home Town:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=HomeTown}"/>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
<StackPanel>
<TextBlock FontFamily="Verdana" FontSize="11" Margin="5,15,0,10" FontWeight="Bold">My Friends:</TextBlock>
<ListBox Width="200" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Source={StaticResource MyFriends}}"/>
<TextBlock FontFamily="Verdana" FontSize="11" Margin="5,15,0,5" FontWeight="Bold">Information:</TextBlock>
<ContentControl Content="{Binding Source={StaticResource MyFriends}}" ContentTemplate="{StaticResource DetailTemplate}"/>
</StackPanel>
如上所示,软件界面的ListBox和ContentControl两个最主要的控件都绑定到了MyFriends这个key所代表的资源当中。ListBox的绑定如下:
ItemsSource="{Binding Source={StaticResource MyFriends}}"
这个很好理解,表示绑定到了Person集合People中,这样子ListBox的每个项就会显示集合中的每个内容,因为ListBox中省却了DisplayMemberPath属性的设置,所以显示的具体内容是由集合中的每个项(也就是Person)的ToString()方法的返回值来决定的。
ContentControl控件的绑定如下所示:
Content="{Binding Source={StaticResource MyFriends}}"
可能觉得比较奇怪,为什么明明是内容(Content)控件,为何要绑定到一个集合当中呢?这里解释给大家,WPF在这个地方比较智能,它内部框架默认是绑定到这个绑定集合所指向的当前项(CurrentItem)。
那好了,新的问题来了,ListBox当中选中集合中的某一项,怎么让ContentCotrol控件中显示相应选中项的详细信息呢? 大家可能想到的一种办法是自己在C#后台写一堆操作代码:ListBox选中事件SelectionChanged中添加相应的事件来改变ContentControl控件的具体内容。这是一个思路,不过要指出的是WPF其实在这点上已经帮我们前期做好了框架工作了。你只要在XMAL文件的ListBox控件当中添加
IsSynchronizedWithCurrentItem="True"
一切就搞定了,你不需要再额外添加代码,WPF就是这么任性,你在后台几乎都没有加任何和界面操作相关的哪怕一句代码,神奇所在,界面与逻辑完全隔离!!!跟界面相关的交互代码几乎全部在XMAL中搞定。
好,那么我们就介绍下IsSynchronizeWithCurrentItem是怎么工作的?那你可就可以参考我的另外一篇博文: