【问题标题】:Cannot use an attached property in my DataTemplate无法在我的 DataTemplate 中使用附加属性
【发布时间】:2021-09-29 11:53:16
【问题描述】:

我对 WPF 比较陌生,我正在尝试了解附加属性,以便在我用作学习基础的测试应用程序中使用它们。 AP 位于它自己的名为 AP.cs 的源文件中,该文件位于命名空间 WPFPages.ViewModels 中。

所以我创建了一个简单的 AP,如下所示,它存储了一个 int 值。

public class AP: DependencyObject
{
    public static int Gettest ( DependencyObject obj )
    {
       return ( int ) obj . GetValue ( testProperty );
    }

    public static void Settest ( DependencyObject obj, int value )
    {
       obj . SetValue ( testProperty, value );
    }
    
    public static readonly DependencyProperty testProperty =
        DependencyProperty . RegisterAttached ( "test", typeof ( int ), typeof ( AP ), new PropertyMetadata ( 2 ), Ontestchanged);

    private static bool Ontestchanged ( object value )
    {
            Console . WriteLine ($"test value changed to {value}");
            return true;
    }
}

在我的 XAML 中,我使用 models:AP.test="21" 设置了值,该值按预期工作,由我在 AP 中的 WriteLine 调试验证。

但是,我希望将此值作为 ListBox 中的一列,因此有一个 DataTemplate 具有以下条目:

<TextBlock x:Name="tst" 
           Text="{Binding Path=(models:AP.test), RelativeSource={RelativeSource Self}}" 
           Width="55"/>

遗憾的是,我只在ListBox 中看到 AP 中的默认设置,而不是 21 的值。我已将其设置为我的 ListBox 属性。我已经在我的 XAML 文件中声明了命名空间,如下所示。

xmlns:models="clr-namespace:WPFPages.ViewModels"

我已经阅读了有关附加属性的所有可能的文章,并在语法方面尝试了主题的更多变体以使其工作,但根本没有成功。到目前为止,我已经花了 3 天时间试图让这个简单的 AP 工作。

由于我对 Binding 的复杂性缺乏经验,我确信这很简单,但非常感谢一些帮助,以了解我如何完成这项工作,因为我可以看到 AP 的强大功能,如果我能让它们正常工作就好了?

更新而不是使用评论:

<ListBox x:Name="listboxclass"
    ItemContainerStyle="{StaticResource lvItemStyle1}"
    BorderThickness="2"
    BorderBrush="{StaticResource Blue0}"
    FontWeight="Normal"
    FontSize="14" Margin="0,0,0,164"   >
    <ListBoxItem
          Height="45"
          models:AP.test="21"/>

相关Style代码为:

希望这可以澄清它。我很高兴现在选择的列中出现了相同的值。

<Style x:Key="lvItemStyle1" TargetType="{x:Type ListBoxItem}">
    <!--<Setter Property="Background" Value="{Binding (models:AP.Background)}"/>-->

    <Setter Property="Template">

          <Setter.Value>
                
                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                      <Grid>
                            <!--<Border x:Name="Bd" 
                                    BorderBrush="{TemplateBinding BorderBrush}" 
                                    BorderThickness="{TemplateBinding BorderThickness}" 
                                    Padding="{TemplateBinding Padding}" 
                                    SnapsToDevicePixels="true">
                                  <Border.Background>
                                        <SolidColorBrush x:Name="borderbckgrnd" Color="{TemplateBinding models:AP.Background}" />
                                  </Border.Background>

                                  <ContentPresenter x:Name="contentpresenter"                                                            
                                                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                                    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
                                  </ContentPresenter>



                            </Border>-->

                            <!--I have tried ALL of these & they ALL cause Exceptions on loading
                                  "{Binding models:AP.test}"
                                  "{Binding Path=(models:AP.test)}"
                                  "{Binding (models:AP.test)}"
                                  "{Binding Source=models:AP.test}"
                                  "{TemplateBinding  models:AP.test}"-->
                            <Border x:Name="Bd" >
                                  <StackPanel Orientation="Horizontal" >
                                        <TextBlock Text="{Binding CustomerId}" 
                                             Height="{Binding ActualHeight, ElementName=NwCustomerDataTemplateBorder1}"  
                                             Width="50" 
                                             Padding="1" 
                                              x:Name="CustomerId"/>
                                        
                                        <!--NB Output window shows :
                                              AP : test value changed to 2
                                              AP : test value changed to 21
                                                    so it is definitely being set.
                                        but the field is empty in the list when run-->
                                        <TextBlock x:Name="tst" 
                                             Text="{Binding Path=models:Attach.test}"
                                              Foreground="Red"                                                           
                                             Width="55"/>
                                        <!--this does  not give error, but does not work either
                                        Height="{Binding Path=models:AP.test, RelativeSource={RelativeSource Self}}"-->
                                        
                                        <TextBlock Text="{Binding CompanyName}" Width="165" Padding="1" x:Name="CompanyName"/>
                                        <TextBlock Text="{Binding ContactName}" Width="135" Padding="1" x:Name="ContactName"/>
                                        <TextBlock Text="{Binding ContactTitle}" Width="40" Padding="1"  x:Name="ContactTitle"/>
                                        <TextBlock Text="{Binding Address}" Width="150" Padding="1"  x:Name="Address"/>
                                        <TextBlock Text="{Binding City }" Width="80" Padding="1"  x:Name="City"/>
                                        <TextBlock Text="{Binding PostalCode}" Width="75" Padding="1"  x:Name="PostalCode"/>
                                        <TextBlock Text="{Binding Country}" Width="75" Padding="1"  x:Name="Country"/>
                                        <TextBlock Text="{Binding Phone}" Width="95" Padding="1"  x:Name="Phone"/>
                                        <TextBlock Text="{Binding Fax}" Width="95" Padding="1"  x:Name="Fax"/>
                                  </StackPanel>
                            </Border>
                            <!-- ... -->

【问题讨论】:

  • 你在哪个元素上设置了models:AP.test="21"?应该为每个列表框项设置不同的值还是对所有项设置相同的值?您写道您想要在 ListBox 中添加一个列,那么您是否使用了 GridView 或者您的意思是什么?
  • 请注意,仅声明附加属性的类不需要从 DependencyObject 派生。该类中的所有声明都是静态的。
  • RelativeSource={RelativeSource Self} 表示您将在 DataTemplate 中名为 tst 的 TextBlock 上设置附加属性。你是怎么做到的?
  • 澄清一下,我的列表框声明是:-
  • 糟糕

标签: c# wpf xaml data-binding attached-properties


【解决方案1】:

这就是您可以绑定到ItemTemplate 中父ListBox 的附加属性的方式:

<ListBox models:AP.test="21">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock x:Name="tst" 
                       Text="{Binding Path=(models:AP.test), 
                           RelativeSource={RelativeSource AncestorType=ListBox}}" 
                       Width="55"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

【讨论】:

  • 非常感谢,您(终于)在我心中澄清了这一点,我现在在我的列表框中得到了正确的值 - 感谢你们对我的耐心
  • @IanTurner:如果您原来的问题已经解决,请记得给accept the answer and vote it up
  • 我一定是太老了,因为我似乎找不到任何方法来接受、投票或在页面上投票。你能告诉我具体是怎么做的吗?我会很乐意这样做吗??
  • 你看到我之前评论中的链接了吗?请检查一下。
  • 是的,你们俩都把手指放在了问题上!你能告诉我如何按照我之前的消息进行投票吗?
【解决方案2】:

附加的AP.test 属性是在ListBoxItem 上定义的,因此您需要引用该元素才能获得分配的值,否则绑定将不起作用。您的试验无效,因为:

  • {Binding models:AP.test} - 这不是绑定附加属性的正确语法,请使用括号 (),参见 binding path syntax
  • {Binding Path=(models:AP.test)} - 语法正确,但附加属性将在元素本身上解析,这里是 TextBlock,它没有定义它。
  • {Binding (models:AP.test)} - 同上。
  • {Binding Source=models:AP.test} - 将附加属性设置为绑定源,这是错误的。
  • {TemplateBinding models:AP.test} - 不适用于附加属性。

您可以通过使用附加的属性绑定语法并引用定义值的ListBoxItem 来使绑定工作。由于TextBlockListBoxItem 模板的一部分,您可以使用RelativeSource 绑定TemplateParent

<TextBlock x:Name="tst"
           Text="{Binding (local:AP.test), RelativeSource={RelativeSource TemplatedParent}}"
           Foreground="Red"
           Width="55" />

或者,您可以指定AncestorType,因为ListBoxItemListBoxItem 的父级。

<TextBlock x:Name="tst"
           Text="{Binding (models:AP.test), RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}"
           Foreground="Red"
           Width="55" />

【讨论】:

  • 非常感谢您对我的错误的详细而完整的澄清,以及对我编写 wpf 代码的微弱尝试非常有效的正确语法。我现在将开始花一些时间进一步试验这个复杂的 WPF 绑定世界。感谢您和 mm8,他们也提供了相同的解决方案。非常感谢
  • @IanTurner 请注意,此解决方案适用于绑定ListBoxItem 上的附加属性集,而不是ListBox 本身,这就是区别。此外,模板绑定仅适用于ControlTemplate,而不是DataTemplate。两个答案都针对不同的用例。
  • 评论及时记录!也许这解释了以下一项,但不是另一项?实验后又出现了一个小问题。我在访问 AP.test 字段的 ListBox DataTemplate 中有一个 TextBlocks,但是在运行时,我得到了 AP 的默认值 (32767),而不是它在我的 xaml 代码中实际设置的值 (21),它确实显示在输出窗口中设置。 :- Text="{Binding (models:AP.test), RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" 我也尝试过 Listbox,结果相同。
猜你喜欢
  • 2017-03-12
  • 1970-01-01
  • 2021-01-08
  • 2013-07-27
  • 2020-02-25
  • 2013-01-27
  • 2016-03-02
  • 1970-01-01
  • 2021-06-27
相关资源
最近更新 更多