WPF中如何将DataTemplate内的ListView列表项绑定到自定义UserControl?
嘿,看起来你已经把核心逻辑写得八九不离十了,问题可能出在一些容易被忽略的小细节上,我来帮你梳理下,让你的FooBarControlList正常跑起来~
首先得夸夸你:你正确定义了FooBarControl的依赖属性,这可是WPF自定义控件实现数据绑定的核心,而且你还特意给UserControl设置了DataContext="{Binding RelativeSource={RelativeSource Self}}"——这个操作非常专业,完美避免了UserControl继承外部DataContext导致的绑定混乱,内部的{Binding FooBar.Foo}也能准确指向自身的属性,思路完全没问题!
你在DataTemplate里写的<local:FooBarControl FooBar="{Binding .}"/>也是正确的,{Binding .}就是绑定到当前的ListView列表项(也就是每个FooBar实例),那为啥没效果呢?大概率是下面这些小问题:
1. 先把UserControl的布局调稳
你的FooBarControl里两个Label直接塞在没定义列的Grid里,虽然加了Margin,但有时候会因为Grid的布局优先级问题导致内容被遮挡或者显示错位。给Grid加个列定义,让两个Label各占一列,布局会稳定很多:
<UserControl ... DataContext="{Binding RelativeSource={RelativeSource Self}}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Label Grid.Column="0" Content="Foo:" VerticalAlignment="Center"/> <Label Grid.Column="1" Content="{Binding FooBar.Foo}" Margin="5,0,0,0" VerticalAlignment="Center" /> </Grid> </UserControl>
2. 绑定语法可以更简洁(可选优化)
你写的FooBar="{Binding .}"完全没问题,不过可以简化成FooBar="{Binding}",效果是一样的,看起来更清爽。另外,你的依赖属性定义是标准写法,如果之后需要在FooBar属性变化时做额外逻辑,还可以给PropertyMetadata加个回调:
public static readonly DependencyProperty FooBarProperty = DependencyProperty.Register( nameof(FooBar), typeof(FooBar), typeof(FooBarControl), new PropertyMetadata(new FooBar(), OnFooBarChanged) ); private static void OnFooBarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // 比如属性变化时更新UI、做数据校验等操作都可以放这 var control = (FooBarControl)d; }
3. 换个更符合WPF习惯的DataContext设置方式
你现在是直接给ListView的DataContext赋值,虽然能跑,但更符合WPF常规写法的是把集合作为MainWindow的属性,然后把窗口自身设为DataContext:
// MainWindow.xaml.cs public ObservableCollection<FooBar> FooBars { get; set; } = new ObservableCollection<FooBar>(); public MainWindow() { try { InitializeComponent(); FooBars.Add(new FooBar() { Foo = "Hello" }); FooBars.Add(new FooBar() { Foo = "World" }); this.DataContext = this; // 把窗口自身设为DataContext } catch (Exception ex) { MessageBox.Show(ex.Message); } }
然后MainWindow的ListView里直接绑定这个属性:
<ListView Grid.Row="0" ItemsSource="{Binding FooBars}" Name="FooBarControlList"> <ListView.ItemTemplate> <DataTemplate DataType="local:FooBar"> <local:FooBarControl FooBar="{Binding}"/> </DataTemplate> </ListView.ItemTemplate> </ListView>
这种方式更清晰,也方便后续扩展MVVM模式。
最后验证下效果
按照上面的调整,你的FooBarControlList应该就能正常显示每个FooBar的内容了。核心逻辑你都对了,就是布局和绑定细节的小优化,调整完就能正常工作啦~
内容来源于stack exchange

