WPF嵌套ItemsControl中RelativeSource绑定MyArrayBorder属性失效如何解决
问题原因
- 绑定的祖先控件层级错误:你指定
AncestorLevel=2找到的是最外层名为List的ItemsControl,该控件的DataContext是MainWindow实例,并没有MyArrayBorder属性。MyArrayBorder属于内层ItemsControl(名为MyArrayInt)的DataContext(即MyArrayClass实例),因此你只需要查找层级为1的ItemsControl祖先即可,同时需要访问其DataContext下的属性。 - 属性类型不匹配:即使正确拿到
MyArrayBorder,它是一个ObservableCollection<Thickness>集合,而BorderThickness需要单个Thickness类型值,你需要获取当前MyArrayInt元素下标对应的MyArrayBorder集合元素。
解决方案
下面提供两种可行方案,优先推荐调整模型结构的方案,符合MVVM开发规范。
方案1:调整模型结构(推荐)
将相关的数值和边框属性封装到同一个子模型类中,避免维护两个需要同步的集合:
// 新增子项模型 public class MyItem { public int Value { get; set; } public Thickness Border { get; set; } } // 调整MyArrayClass定义 public class MyArrayClass { public ObservableCollection<MyItem> Items { get; set; } } // 初始化逻辑调整 MyArrayClass sample3 = new MyArrayClass { Items = new ObservableCollection<MyItem> { new MyItem { Value = 0, Border = new Thickness(0,0,0,2) }, new MyItem { Value = 1, Border = new Thickness(0,0,0,2) }, new MyItem { Value = 2, Border = new Thickness(0,0,0,2) }, new MyItem { Value = 3, Border = new Thickness(0,0,0,2) } } }; MyArrayData = new List<MyArrayClass>(); MyArrayData.Add(sample3);
对应的XAML代码可以大幅简化,无需复杂的RelativeSource绑定:
<ItemsControl Name="List" ItemsSource="{Binding MyArrayData}" Grid.Row="1" Grid.Column="1"> <ItemsControl.ItemTemplate> <DataTemplate> <ItemsControl ItemsSource="{Binding Items}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <Border BorderBrush="Red" BorderThickness="{Binding Border}"> <TextBlock Text="{Binding Value}" Background="Azure" Margin="2,0,0,0"/> </Border> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl>
方案2:适配现有代码结构
如果不希望修改现有模型定义,可以通过AlternationIndex获取项下标,配合多值转换器实现绑定:
- 首先创建多值转换器:
public class IndexToThicknessConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values.Length < 2 || values[0] is not int index || values[1] is not IList<Thickness> borders) return new Thickness(0); return index >= 0 && index < borders.Count ? borders[index] : new Thickness(0); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
- 在窗口资源中声明转换器:
<Window.Resources> <local:IndexToThicknessConverter x:Key="IndexToThicknessConverter"/> </Window.Resources>
- 修改XAML绑定逻辑:
<ItemsControl Name="List" ItemsSource="{Binding MyArrayData}" Grid.Row="1" Grid.Column="1"> <ItemsControl.ItemTemplate> <DataTemplate> <ItemsControl Name="MyArrayInt" ItemsSource="{Binding MyArrayInt}" AlternationCount="{Binding MyArrayInt.Count}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <Border BorderBrush="Red"> <Border.BorderThickness> <MultiBinding Converter="{StaticResource IndexToThicknessConverter}"> <Binding Path="(ItemsControl.AlternationIndex)" RelativeSource="{RelativeSource Mode=TemplatedParent}"/> <Binding Path="DataContext.MyArrayBorder" RelativeSource="{RelativeSource FindAncestor, AncestorType=ItemsControl, AncestorLevel=1}"/> </MultiBinding> </Border.BorderThickness> <TextBlock Text="{Binding Path=.}" Background="Azure" Margin="2,0,0,0"/> </Border> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl>
内容的提问来源于stack exchange,提问作者Giovanni Nieddu
相关产品推荐
相关产品推荐

