有些事情看似简单,在进行的过程中往往遇到大的麻烦;有些问题小之又小,但躲在角落里窃笑的它能让你呕血数日。反正我不止一次遇到过这样的情况。
日前我打算在产品中加入吊牌打印的功能,涉及到合格证的制作。大伙都有去商店里买衣服的经历,留意看吊牌的话会看到成分一栏里分成好几个类别,如面料、里料、夹层等,每一类别又有好几个物料占比信息,如面料包含70%的棉和30%的xx纤维。如何能让用户较为方便地设置这些信息呢?我原本打算采用自定义集合控件的方式,但是有些问题不好解决,部分难点问题记录在随笔WPF自定义集合控件概述与遇到的问题中。于是我老老实实采用为ListBox新建Template的方式,期望达到如下效果:
毫无疑问,上述效果需要俩ListBox,一个呈现大类,一个呈现小类,还得为它们准备各自的DataTemplate,还需要俩GroupBox,它们各自的HeaderTemplate也要额外处理。我微微一笑,流利地敲出代码的第一版本:
1 <GroupBox DataContext="{Binding Composition,Mode=TwoWay,Converter={StaticResource compositionConverter}}"
2 RenderOptions.BitmapScalingMode="NearestNeighbor" Padding="3">
3 <GroupBox.HeaderTemplate>
4 <DataTemplate>
5 <StackPanel Orientation="Horizontal">
6 <TextBlock Text="成分" />
7 <!--猜测由于没有给GroupBox.Header赋予Content,因此HeaderTemplate里的控件的DataContext为Null-->
8 <!--猜测正确,具体请看WPF GroupBox HeaderTemplate and DataBinding http://*.com/questions/2425079/wpf-groupbox-headertemplate-and-databinding-->
9 <Button x:Name="PART_AddButton" Height="16" Click="PART_AddButton_Click">
10 <Button.Content>
11 <Image Source="pack://application:,,,/View.Extension;Component/Images/plus.png" />
12 </Button.Content>
13 </Button>
14 <Button x:Name="PART_DeleteButton" Height="16" Click="PART_DeleteButton_Click">
15 <Button.Content>
16 <Image Source="pack://application:,,,/View.Extension;Component/Images/minus.png" />
17 </Button.Content>
18 </Button>
19 </StackPanel>
20 </DataTemplate>
21 </GroupBox.HeaderTemplate>
22 <ListBox x:Name="lbxMateriels" ItemsSource="{Binding}" BorderThickness="0" ScrollViewer.VerticalScrollBarVisibility="Hidden">
23 <ListBox.ItemContainerStyle>
24 <Style TargetType="ListBoxItem">
25 <Setter Property="Margin" Value="2" />
26 <Setter Property="Template">
27 <Setter.Value>
28 <ControlTemplate TargetType="ListBoxItem">
29 <GroupBox Header="{Binding}">
30 <GroupBox.HeaderTemplate>
31 <DataTemplate>
32 <StackPanel Orientation="Horizontal">
33 <ComboBox ItemsSource="{Binding MaterielKinds,Source={StaticResource context}}"
34 MinWidth="60"
35 SelectedValue="{Binding KindName}"
36 SelectedValuePath="Name"
37 DisplayMemberPath="Name" />
38 <Button Height="16" Click="btnAddPercent_Click">
39 <Button.Content>
40 <Image Source="pack://application:,,,/View.Extension;Component/Images/plus.png" />
41 </Button.Content>
42 </Button>
43 <Button Height="16" Click="btnDeletePercent_Click">
44 <Button.Content>
45 <Image Source="pack://application:,,,/View.Extension;Component/Images/minus.png" />
46 </Button.Content>
47 </Button>
48 </StackPanel>
49 </DataTemplate>
50 </GroupBox.HeaderTemplate>
51 <ListBox x:Name="lbxMaterielPercents" ScrollViewer.VerticalScrollBarVisibility="Hidden"
52 ItemsSource="{Binding MaterielPercents}" BorderThickness="0">
53 <ListBox.ItemTemplate>
54 <DataTemplate>
55 <Grid Margin="0 3 0 0">
56 <Grid.ColumnDefinitions>
57 <ColumnDefinition Width="Auto" />
58 <ColumnDefinition Width="*" />
59 </Grid.ColumnDefinitions>
60 <NumericUpDown Value="{Binding Percent}" Minimum="0" Maximum="100" CustomUnit="%" />
61 <ComboBox ItemsSource="{Binding AvailableMateriels,Source={StaticResource context}}"
62 SelectedValue="{Binding MaterielName}"
63 SelectedValuePath="Name"
64 DisplayMemberPath="Name"
65 Grid.Column="1" MinWidth="60" Margin="5 0 0 0"/>
66 </Grid>
67 </DataTemplate>
68 </ListBox.ItemTemplate>
69 </ListBox>
70 </GroupBox>
71 </ControlTemplate>
72 </Setter.Value>
73 </Setter>
74 </Style>
75 </ListBox.ItemContainerStyle>
76 </ListBox>
77 </GroupBox>
代码有点多,但结构还算清晰,编译也能通过。但是,在页面加载时抛出了NullReference异常,后台代码设置的断点都没执行到。我任劳任怨地开始排查各段代码,最后发现问题出在上述代码的38行到47行之间,我不敢相信自己的眼睛,于是将异常代码用另写的一个测试按钮<Button Content="Test" />代替,运行无问题。我接着开始重启VS、重启机子、拷贝代码的艰苦旅程,问题依旧。最后我灵光乍现,取消注册Click事件,运行无问题。此时我脆弱的大脑已经不堪思索,在尝试用Command代替Event之前,我将希望投给了Bing(经过xx多年努力,Google已经成功被罢用了),发现有人遇到同我一样的问题——Binding to a converter and having a separate button's event assigned results in a nullreference exception?虽然提问并不十分准确,但描述的问题与我的类似,非常幸运的是,有高手解答了。在查看了相关网址之后,我大致心中有数,在此作一纪录。
问题产生前提:
- You have a Microsoft .NET Framework 4.0-based Windows Presentation Foundation (WPF) application.
- In the application, there is one template that is nested inside another template.
- The inner (nested) template contains a control that specifies a style and an event. The style references a static resource.
解决方法:
- Set the style reference to a dynamic resource.
- Set the inner template as a resource for the outer template, and then remove the nest relationship.
更改后的代码相当简单,我就不贴出来了。
最后,我想把上面的整个GroupBox作为一个DataTemplate放到Resource*页面元素统一调用。
1 <DataTemplate x:Key="materielOutGPXTemplate">
2 <GroupBox ……
3 </DataTemplate>
然后同样的异常又抛出来了。也许最外围的DataTemplate也作为一层计算在内,此时第一个GroupBox的HeaderTemplate变成嵌套的内层了,又或许我的这个情况导致了多层嵌套,事情会变得更加复杂?反正我按照前述的两个方法都没能解决该问题,只好只将GroupBox的其余部分放入Resources中。世间事无完美,我只能这么安慰自己了。
转载请注明本文出处:http://www.cnblogs.com/newton/archive/2012/12/30/2839520.html