在ItemsControl的DataTemplate中绑定自定义控件

7

我在使用ItemsControl时,想要基于定义的DataType绑定DataTemplate到我的自定义用户控件上时遇到了问题。

为了演示,我创建了一个简单的项类示例,其中我有如下项目集合:

public class Item
{
    public string ItemNameToBeSureWhatPropertyIsBound { get; set; } 
}

在我的ViewModel中,我创建了这样的集合,并将其公开(单独使用一个项进行比较):
public class MainWindowViewModel : INotifyPropertyChanged
{
    private ObservableCollection<Item> _items;
    private Item _exampleItem;

    public MainWindowViewModel()
    {
        Items = new ObservableCollection<Item>(new[] { new Item { ItemNameToBeSureWhatPropertyIsBound = "Me" }, new Item { ItemNameToBeSureWhatPropertyIsBound = "MySelf" }, new Item { ItemNameToBeSureWhatPropertyIsBound = "Ich" }, });
        ExampleItem = Items.LastOrDefault();
    }

    public ObservableCollection<Item> Items
    {
        get { return _items; }
        set { _items = value; OnPropertyChanged(); }
    }

    public Item ExampleItem
    {
        get { return _exampleItem; }
        set { _exampleItem = value; OnPropertyChanged();}
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

我的自定义用户控件定义如下:

<UserControl x:Class="WpfDataTemplate.ItemRowUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="40" d:DesignWidth="300"
         x:Name="ItemRowControl" DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}}">

    <Grid Background="Yellow" Height="40">
        <TextBlock Text="{Binding ItemName}" Foreground="Black"/>
    </Grid>
</UserControl>

...并且它在代码后台中有一个DependencyProperty

public partial class ItemRowUserControl : UserControl
{
    public ItemRowUserControl()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty ItemNameProperty = DependencyProperty.Register(
        "ItemName", typeof (string), typeof (ItemRowUserControl), new PropertyMetadata(default(string)));

    public string ItemName
    {
        get { return (string) GetValue(ItemNameProperty); }
        set { SetValue(ItemNameProperty, value); }
    }
}

问题在于,当我尝试绑定ItemsControl中的DataTemplate中的Item属性时,在MainWindow中进行如下操作(注意:我只是为了调试而使用了一个虚拟转换器,返回值而已):

<Window.DataContext>
    <my:MainWindowViewModel />
</Window.DataContext>
<Window.Resources>
    <my:MyDummyConverter x:Key="MyDummyConverter" />
</Window.Resources>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="50" />
    </Grid.RowDefinitions>

    <ItemsControl Name="ItemsControl" ItemsSource="{Binding Items}" Grid.Row="0" Background="Red">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="{x:Type my:Item}">
                <my:ItemRowUserControl ItemName="{Binding ItemNameToBeSureWhatPropertyIsBound, Converter={StaticResource MyDummyConverter}}"  />
                <!--<Grid Background="Pink">
                    <TextBlock Text="{Binding ItemNameToBeSureWhatPropertyIsBound, Converter={StaticResource MyDummyConverter}}" Foreground="Black" Height="30" />
                </Grid>-->
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

    <Grid Grid.Row="1">
        <my:ItemRowUserControl ItemName="{Binding DataContext.ExampleItem.ItemNameToBeSureWhatPropertyIsBound, ElementName=MyWindow, Converter={StaticResource MyDummyConverter}}" />
    </Grid>
</Grid>

现在,如果我将自定义的ItemRowUserControl绑定,我得到的值是ItemRowUserControl本身(在Debug Output中也是如此)。但是如果我将其绑定到被注释掉的代码上,一切都正常工作。为什么会这样,我该如何使用自定义控件作为DataTemplate,以使绑定(由智能感知提供)正常工作?顺便提一下:在第一行(底部)网格中绑定我的ItemRowUserControl可以正常工作,所以我想控件已经按预期设置了吗?

1个回答

9
问题在于您将UserControl的DataContext显式设置为自身:
DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}}

移除该赋值语句,并将 ItemName 绑定写成以下形式:

<TextBlock Text="{Binding ItemName,
    RelativeSource={RelativeSource AncestorType=UserControl}}"/>

or like this

<TextBlock Text="{Binding ItemName, ElementName=ItemRowControl}"/>

谢谢,这个按预期工作。你能解释一下绑定中RelativeSource的部分吗?在你的回答中使用ElementName进行绑定,对我来说是有意义的。再次感谢。 - Janez Lukan
1
请参考MSDN上的在线文档。在任何情况下,UserControl实例都被设置为绑定的源对象。 - Clemens
2
@JanezLukan 这意味着在控件树中相对查找,直到找到类型为“UserControl”的控件,并且查找将是向上的,因为它说“AncestorType”。 - Harsh Baid
@Clemens:如果我想为自定义UserControl设置DataContext,有什么方法可以处理吗? - gartenriese
@Clemens 非常感谢!这个问题已经困扰我几个小时了。 - Dru Steeby

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接