【问题标题】:Binding to code behind from custom control从自定义控件绑定到代码后面
【发布时间】:2013-06-17 12:03:08
【问题描述】:

我有一个有几个按钮的 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。

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

场景一: 在 GridView 的 DataTemplate 中:

<UserControl CustomParameter="Literal Text">

在用户控件中:

<TextBlock Text="{Binding CustomParameter}">

在 UserControl .cs 中:this.DataContext = this 有效!

场景二: 在 GridView 的 DataTemplate 中:

<UserControl CustomParameter="{Binding ValueFromDataItem">

在用户控件中:

<TextBlock Text="{Binding CustomParameter}">

在 UserControl .cs 中:this.DataContext = this 不!

【问题讨论】:

  • 没人知道吗?
  • 我假设您在某种列表中使用了DataTemplate。然后将模板的DataContext 设置为它所代表的项目。因此,您必须以不同的方式指定绑定源。例如。使用静态/动态资源或相对来源。
  • 你试过从cs文件设置绑定吗?
  • 您在 UserControl 中的属性只是属性吗?还是它们是 DependencyProperties?另外,您使用的是 Mvvm,对吗?你不能只绑定到虚拟机中的命令吗?如果 VM 在 Item(数据上下文)中,那么您可以通过 Path 直接访问它。似乎情况并非如此。命令是控件本身的属性吗?如果是这种情况,您可以使用 RelativeSource Self,尽管这不是建议的方法。
  • 我想在我的网格视图中显示按钮。按钮由特定项目(SubjectItem 类)表示。在这个类中,我设置了不同按钮图像的来源。在自定义按钮类中,我当然可以直接绑定到 SubjectItem 类。但是那样会消除很多灵活性。我希望能够在 SubjectItemTemplate(上图)中设置按钮图像源,以便我可以从 SubjectItem 传递源()或者我可以手动输入一些东西

标签: c# xaml windows-8 windows-store-apps winrt-xaml


【解决方案1】:

我明白了,

因此,在用户控件中设置与自定义属性的双向绑定可能会很棘手,因为用户控件无法绑定到 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 sn-p,它可以为您生成一个依赖属性。

看看这个用户控件。它有两个控件,一个 TextBox 和一个 TextBlock,用于演示此绑定方法的 OneWay 和 TwoWay 功能。我们还在用户控件上实现了 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));
    }
}

在上面的 ode 中,我创建了一个“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 中,就绑定而言,我没有做任何特别的事情。语法只是使用适合控件的模式绑定到 Text 属性。就像你平时做的那样。但是,值得注意的是 DataContext 没有设置在用户控件上。相反,它设置在网格上。事实上,除了用户控件之外,树中的任何控件都可以这样使用。只是不要设置用户控件的数据上下文。

顺便说一句。

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

祝你好运!

【讨论】:

  • 好的,修好了。刚从Build回来,所以一直没钱。您应该删除暗示它不起作用的评论,因此未来的开发人员不会认为它是错误的。
  • 谢谢!今天会检查一下,如果它解决了我的问题,回来告诉你!
  • 它仍然不适合我。请注意,我的用户控件位于 GridView 中使用的数据模板中。我今天将创建一个骨架项目。我会在这里发布一个 GIT 链接。
  • 这是我为你写的blog.jerrynixon.com/2013/07/…
  • 对不起,我还没看过。这就是作为承包商的事情。我不得不创建一个 hack 解决方案并暂时放弃 c# 以继续为我的客户其他应用程序更新 iOS 7 :) 不过谢谢你,当我回到 c# 土地时,我会试一试!
【解决方案2】:

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

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

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

【讨论】:

  • 找不到“AncestorType”。我正在编写一个 Windows 8 应用商店应用程序
猜你喜欢
  • 2016-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-08
  • 2013-04-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多