绑定到自定义控件后面的代码

本文关键字:代码 自定义控件 绑定 | 更新日期: 2023-09-27 18:18:47

我有一个GridView,有几个按钮。其中一个由以下模板定义:

<DataTemplate x:Name="SubjectItemTemplate">
        <Canvas Width="340" Height="170" VerticalAlignment="Top">
            <Controls:ThreeImageButton HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,0,0,0"
                                              NormalStateImageSource="{Binding NormalImage}"
                                              HoverStateImageSource="{Binding HoverImage}"
                                              PressedStateImageSource="{Binding PressedImage}" Command="{Binding Path=NavigateToUnitsPage}"
                CommandParameter="{Binding}" Canvas.Left="0" Canvas.Top="0">

        </Controls:ThreeImageButton>
    </Canvas>
</DataTemplate>

现在我有一个自定义控件,叫做ThreeImageButton。当我单独使用时,这个按钮工作得很好。但是当我在DataTemplate中拥有它时,它不会将属性绑定到后面的代码。

现在,我有

x:Name="MyThreeImageButton"

中的自定义按钮。我像这样连接到代码后面:

<TextBlock Text="{Binding ElementName=MyThreeImageButton, Path=NormalStateImageSource}"/>

(这只是一个显示文本的测试,在实际代码中,我会将图像源分配给由元素引用的另一个属性)。

现在,TextBlock中没有显示任何内容。我应该使用什么正确的绑定语法来访问我的属性?

谢谢!

编辑:我在InitializeComponent函数中设置变量,我在DependencyProperty上使用SetValue。

编辑:让我添加以下信息以便更清楚

场景我:In DataTemplate for GridView:

<UserControl CustomParameter="Literal Text">

In UserControl:

<TextBlock Text="{Binding CustomParameter}">

在UserControl .cs: this。DataContext =这个作品!

场景二:In DataTemplate for GridView:

<UserControl CustomParameter="{Binding ValueFromDataItem">

In UserControl:

<TextBlock Text="{Binding CustomParameter}">

在UserControl .cs: this。DataContext =这个不!

绑定到自定义控件后面的代码

我明白了,

因此,在用户控件中设置到自定义属性的双向绑定可能很棘手,因为用户控件不能绑定到CLR属性。不仅如此,在用户控件上设置数据上下文还会对其内部的绑定产生意想不到的影响。

你可以用一点点代码解决这些问题。基本上使用依赖属性来支持CLR属性,并将数据上下文设置在子元素上,而不是根用户控件上。

看一下这个例子。假设您有以下MainPage。MainPage最终将使用我们的自定义用户控件。让我们来搭建舞台。

下面是隐藏代码:

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        this.DataContext = new /* your view model */
        {
            Title = Guid.NewGuid().ToString(),
        };
    }
}
在上面的代码中,我用一个简单的匿名类模拟一个复杂的视图模型。这将是愚蠢的,你实现自己像这样,但在同一时间,它是愚蠢的,我建立一个简单的样本与完整的脚手架。我提到这一点只是为了不让你感到困惑——因为它可能看起来像我在prod. 中建议使用这种方法。

XAML:

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <local:MyUserControl Text="{Binding Title}" />
</Grid>
在上面的XAML中,绝对没有什么特别的。我已经在本地名称空间中引用了用户控件,我只是在这里声明它。

好了,现在我们有了一个控件的消费者,值得指出的是,在测试中,开发人员可能会错误地认为他们的绑定正在工作,因为他们使用文字值进行测试。文字值绑定得很好。它是从底层视图模型绑定的。

让我们说另一件事,一些开发人员倾向于避免依赖属性,因为需要更多的输入。人们记得[kbd]propdp[/kbd]是一个方便的Visual Studio代码片段,它为你提供了一个依赖属性。

看看这个用户控件。它有两个控件,一个TextBox和一个TextBlock,用来演示这种绑定方法的单向和双向功能。我们还在用户控件上实现了INotifyPropertyChanged。在大多数情况下,在用户控件中添加视图模型是多余的,因为用户控件已经像视图模型一样起作用了。这取决于开发人员,但在我看来这似乎很愚蠢。

下面是代码:

public sealed partial class MyUserControl : UserControl, INotifyPropertyChanged
{
    public MyUserControl()
    {
        this.InitializeComponent();
    }
    // text property
    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValueDp(TextProperty, value); }
    }
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(MyUserControl), null);
    // bindable
    public event PropertyChangedEventHandler PropertyChanged;
    void SetValueDp(DependencyProperty property, object value,
        [System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
    {
        SetValue(property, value);
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

在上面的代码中,我创建了一个"Text"属性,并为它添加了一个依赖属性。为了重用,我还实现了SetValueDp(),如果我有多个属性,它可以一次又一次地使用。尽管这个演示只有一个,但我想包含这个,因为重复的逻辑肯定应该像这样抽象出来。

XAML:

<Grid Background="Black" DataContext="{Binding ElementName=userControl}">
    <StackPanel>
        <TextBox Text="{Binding Text, Mode=TwoWay}"
            MinHeight="100" Padding="15" FontWeight="Light" FontSize="50" />
        <TextBlock Text="{Binding Text}"
            MinHeight="100" Padding="15" FontWeight="Light" FontSize="50" />
    </StackPanel>
</Grid>
在上面的XAML中,就绑定而言,我没有做任何特别的事情。语法只是使用适合于控件的Mode绑定到Text属性。就像你平时做的那样。然而,值得注意的是DataContext没有在用户控件上设置。相反,它被设置在网格上。事实上,除了用户控件之外,树中的任何控件都可以这样使用。只是不要设置用户控件的数据上下文。

我已经测试了它,以确保它的工作。这里演示单向绑定和双向绑定非常方便。我甚至可能把它变成一个博客,以防其他开发人员想要找到它,但没有发现这个问题。谢谢你的问题!

祝你好运!

正如注释所暗示的那样,您的DataTemplate将项目的数据上下文放置到您要添加到列表中的任何对象中。这与周围用户控件的数据上下文不同。如果您想引用该数据上下文的命令,请在DataTemplate的绑定中执行以下操作:

{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.NormalImage}

这就是说出去并找到用户控件祖先并使用它的数据上下文,然后寻找NormalImage属性。如果遇到问题,请检查输出窗口是否存在绑定错误。