WPF 嵌套项控件
WPF Nesting ItemsControls
我刚开始使用 ItemsControls/Binding,我遇到了一个问题。我看过有关嵌套 ItemsControl 的各种教程,所以我不确定我做错了什么。我相信所有代码都正确编码,但 Expander 没有按应有的方式显示内容。 Header 正确地将自己对齐到其父级的顶部,但 ScrollViewer 不会出现,它只会滚动父级 "TimeScrollViewer"。我是不是绑定错了什么?
欢迎所有建议。
C#:
private string[][] hours = new string[][]
{
new string[] { "11:00", "11:30", "12:00", "12:30", "1:00", "1:30", "2:00", "2:30", "3:00", "3:30", "4:00", "4:30", "5:00", "5:30", "6:00", "6:30", "7:00", "7:30", "8:00", "8:30" },
new string[] { "5:00", "5:30", "6:00", "6:30", "7:00", "7:30", "8:00", "8:30", "9:00" },
new string[] { "5:00", "5:30", "6:00", "6:30", "7:00", "7:30", "8:00", "8:30", "9:00" },
new string[] { "5:00", "5:30", "6:00", "6:30", "7:00", "7:30", "8:00", "8:30", "9:00" },
new string[] { "5:00", "5:30", "6:00", "6:30", "7:00", "7:30", "8:00", "8:30", "9:00" },
new string[] { "5:00", "5:30", "6:00", "6:30", "7:00", "7:30", "8:00", "8:30", "9:00" },
new string[] { "5:00", "5:30", "6:00", "6:30", "7:00", "7:30", "8:00", "8:30", "9:00" }
};
public class GuestItem
{
public string GuestName { get; set; }
}
public class RegistryItem
{
public string Header { get; set; }
public List<GuestItem> GuestList = new List<GuestItem>();
}
Expander currentExpander = null;
public MainWindow()
{
int day = (int)DateTime.Now.DayOfWeek;
InitializeComponent();
List<RegistryItem> items = new List<RegistryItem>();
foreach(string hour in hours[day])
{
RegistryItem registryItem = new RegistryItem(){ Header = hour };
registryItem.GuestList.Add(new GuestItem() { GuestName = "Bob" });
registryItem.GuestList.Add(new GuestItem() { GuestName = "Frank" });
registryItem.GuestList.Add(new GuestItem() { GuestName = "Jim" });
items.Add(registryItem);
}
TimeItemsControl.ItemsSource = items;
}
private void ExpanderExpanded(object sender, RoutedEventArgs e)
{
if(currentExpander != null)
{
currentExpander.IsExpanded = false;
}
currentExpander = e.Source as Expander;
currentExpander.IsExpanded = true;
}
private void ExpanderCollapsed(object sender, EventArgs e)
{
currentExpander = null;
}
XAML:
<s:SurfaceScrollViewer Name="TimeScrollViewer" Grid.Row="1" Grid.Column="1" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Hidden" Background="#4CAAAAFF" Style="{DynamicResource SurfaceScrollViewerHorizontalTop}" Foreground="#4CAAAAFF">
<ItemsControl Name="TimeItemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Expanded="ExpanderExpanded" Collapsed="ExpanderCollapsed" Header="{Binding Header}" Style="{DynamicResource SurfaceExpander}" HorizontalContentAlignment="Center" FontSize="21.333" Width="100">
<s:SurfaceScrollViewer Width="{Binding ElementName=TimeScrollViewer, Path=ActualWidth}" Height="{Binding ElementName=TimeScrollViewer, Path=ActualHeight}" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Hidden">
<ItemsControl ItemsSource="{Binding GuestList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<s:SurfaceButton Content="{Binding GuestName}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</s:SurfaceScrollViewer>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</s:SurfaceScrollViewer>
当您 运行(调试)您的应用程序并检查 Visual Studio 中的输出选项卡时,您可以多次看到以下输出:
System.Windows.Data Error: 40 : BindingExpression path error: 'GuestList' property not found on 'object' ''RegistryItem' (HashCode=15478206)'. BindingExpression:Path=GuestList; DataItem='RegistryItem' (HashCode=15478206); target element is 'ItemsControl' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
因此无法在 RegistryItem
对象上解析绑定到 GuestList
属性 的数据。如果你仔细查看类型的定义,你就会明白为什么:
public class RegistryItem
{
public string Header { get; set; }
public List<GuestItem> GuestList = new List<GuestItem>();
}
GuestList
不是 属性,而是一个字段。 WPF 绑定引擎需要属性,因此 GuestList
字段尽管是 public,但对于绑定引擎来说并不存在,从而导致上述错误。要解决此问题,只需将其设为 属性。您可以使用空构造函数来初始化列表:
public class RegistryItem
{
public string Header { get; set; }
public List<GuestItem> GuestList { get; set; }
public RegistryItem ()
{
GuestList = new List<GuestItem>();
}
}
然后一切都会正常工作。所以底线是:始终检查错误消息,尤其是绑定错误,它们通常会告诉您可能出了什么问题。由于绑定错误通常是隐藏的(因为它们不会 break 东西),您可以使用 [在另一个问题中]描述的技术(
How can I turn binding errors into runtime exceptions?) 将它们变成完全异常或至少将它们记录在其他地方。
我刚开始使用 ItemsControls/Binding,我遇到了一个问题。我看过有关嵌套 ItemsControl 的各种教程,所以我不确定我做错了什么。我相信所有代码都正确编码,但 Expander 没有按应有的方式显示内容。 Header 正确地将自己对齐到其父级的顶部,但 ScrollViewer 不会出现,它只会滚动父级 "TimeScrollViewer"。我是不是绑定错了什么?
欢迎所有建议。
C#:
private string[][] hours = new string[][]
{
new string[] { "11:00", "11:30", "12:00", "12:30", "1:00", "1:30", "2:00", "2:30", "3:00", "3:30", "4:00", "4:30", "5:00", "5:30", "6:00", "6:30", "7:00", "7:30", "8:00", "8:30" },
new string[] { "5:00", "5:30", "6:00", "6:30", "7:00", "7:30", "8:00", "8:30", "9:00" },
new string[] { "5:00", "5:30", "6:00", "6:30", "7:00", "7:30", "8:00", "8:30", "9:00" },
new string[] { "5:00", "5:30", "6:00", "6:30", "7:00", "7:30", "8:00", "8:30", "9:00" },
new string[] { "5:00", "5:30", "6:00", "6:30", "7:00", "7:30", "8:00", "8:30", "9:00" },
new string[] { "5:00", "5:30", "6:00", "6:30", "7:00", "7:30", "8:00", "8:30", "9:00" },
new string[] { "5:00", "5:30", "6:00", "6:30", "7:00", "7:30", "8:00", "8:30", "9:00" }
};
public class GuestItem
{
public string GuestName { get; set; }
}
public class RegistryItem
{
public string Header { get; set; }
public List<GuestItem> GuestList = new List<GuestItem>();
}
Expander currentExpander = null;
public MainWindow()
{
int day = (int)DateTime.Now.DayOfWeek;
InitializeComponent();
List<RegistryItem> items = new List<RegistryItem>();
foreach(string hour in hours[day])
{
RegistryItem registryItem = new RegistryItem(){ Header = hour };
registryItem.GuestList.Add(new GuestItem() { GuestName = "Bob" });
registryItem.GuestList.Add(new GuestItem() { GuestName = "Frank" });
registryItem.GuestList.Add(new GuestItem() { GuestName = "Jim" });
items.Add(registryItem);
}
TimeItemsControl.ItemsSource = items;
}
private void ExpanderExpanded(object sender, RoutedEventArgs e)
{
if(currentExpander != null)
{
currentExpander.IsExpanded = false;
}
currentExpander = e.Source as Expander;
currentExpander.IsExpanded = true;
}
private void ExpanderCollapsed(object sender, EventArgs e)
{
currentExpander = null;
}
XAML:
<s:SurfaceScrollViewer Name="TimeScrollViewer" Grid.Row="1" Grid.Column="1" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Hidden" Background="#4CAAAAFF" Style="{DynamicResource SurfaceScrollViewerHorizontalTop}" Foreground="#4CAAAAFF">
<ItemsControl Name="TimeItemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Expanded="ExpanderExpanded" Collapsed="ExpanderCollapsed" Header="{Binding Header}" Style="{DynamicResource SurfaceExpander}" HorizontalContentAlignment="Center" FontSize="21.333" Width="100">
<s:SurfaceScrollViewer Width="{Binding ElementName=TimeScrollViewer, Path=ActualWidth}" Height="{Binding ElementName=TimeScrollViewer, Path=ActualHeight}" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Hidden">
<ItemsControl ItemsSource="{Binding GuestList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<s:SurfaceButton Content="{Binding GuestName}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</s:SurfaceScrollViewer>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</s:SurfaceScrollViewer>
当您 运行(调试)您的应用程序并检查 Visual Studio 中的输出选项卡时,您可以多次看到以下输出:
System.Windows.Data Error: 40 : BindingExpression path error: 'GuestList' property not found on 'object' ''RegistryItem' (HashCode=15478206)'. BindingExpression:Path=GuestList; DataItem='RegistryItem' (HashCode=15478206); target element is 'ItemsControl' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
因此无法在 RegistryItem
对象上解析绑定到 GuestList
属性 的数据。如果你仔细查看类型的定义,你就会明白为什么:
public class RegistryItem
{
public string Header { get; set; }
public List<GuestItem> GuestList = new List<GuestItem>();
}
GuestList
不是 属性,而是一个字段。 WPF 绑定引擎需要属性,因此 GuestList
字段尽管是 public,但对于绑定引擎来说并不存在,从而导致上述错误。要解决此问题,只需将其设为 属性。您可以使用空构造函数来初始化列表:
public class RegistryItem
{
public string Header { get; set; }
public List<GuestItem> GuestList { get; set; }
public RegistryItem ()
{
GuestList = new List<GuestItem>();
}
}
然后一切都会正常工作。所以底线是:始终检查错误消息,尤其是绑定错误,它们通常会告诉您可能出了什么问题。由于绑定错误通常是隐藏的(因为它们不会 break 东西),您可以使用 [在另一个问题中]描述的技术( How can I turn binding errors into runtime exceptions?) 将它们变成完全异常或至少将它们记录在其他地方。