【问题标题】:Binding OnwayToSource not working as expected - what alternatives?Binding OnwayToSource 无法按预期工作 - 有什么替代方案?
【发布时间】:2011-12-15 19:12:23
【问题描述】:

我有一个业务对象 - 称它为 Fish(不是从任何东西派生的,即不是 DependancyObject),它使用 DataTemplate 显示在 ListBox 中。在代码中的其他地方,我需要通过对 Fish 的引用来了解 Fish DataTemplate 的 TextBlock 部分的渲染宽度。没问题,我想。我向 Fish 类添加了宽度和高度属性,并在我的数据模板中使用 Mode=OnwayToSource 将 TextBlock 宽度/高度绑定到这些属性。
问题:设置我的 Fish.width/heigh 属性时,宽度/高度始终为 NaN。我试过这个解决方法:
OneWayToSource Binding seems broken in .NET 4.0
但它也不起作用(值始终为 NaN)。
我无法绑定到 ActualWidth/ActualHeight,因为它们是只读的(为什么我不能在只读属性上绑定 OnwayToSource !!)
我有什么替代方案?我必须从中派生 Fish DependancyObject 并使我的属性成为 DP?
XAML:

<DataTemplate DataType="{x:Type p:Fish}">
<Border BorderBrush="Black" BorderThickness="2" >
<TextBlock FontSize="14" TextAlignment="Center" VerticalAlignment="Center"
           Width="{Binding Path=width, Mode=OneWayToSource}"
           Height="{Binding Path=height, Mode=OneWayToSource}" ...

代码:

class Fish {
    public double width { get; set; } // From DataTemplate TextBlock.Width.
    public double height { get; set; } // From DataTemplate TextBlock.Height
}

...
double rendered_width = my_fish.width; // Use the rendered width!

【问题讨论】:

  • 您使用了错误的绑定模式。请参阅 BalamBalam 的回答。您链接到的问题与绑定在更新后无法获取绑定属性的方式有关,与此问题无关。您只想阅读 - 使用 Mode=OneWay。您只想写 - 使用 Mode=OneWayToSource。
  • 要么我对不清楚感到困惑(要么可能!)。 TextBlock 呈现并具有宽度/实际宽度。我想将该值推送到我的业务(鱼)对象宽度属性 - 所以我可以从代码中的鱼中读取它。所以我的模板中 TextBlock 上的绑定需要是 OnewayToSource - 将 TextBlock.Width 写入 Fish.width
  • 据我了解,这正是 OneWayToSource 存在的原因 - 通过将绑定放在源对象而不是目标上来反转绑定,从而绑定到不是依赖属性的对象上的属性。
  • 哦,我明白你现在在说什么了。 OneWayToSource 存在的真正原因是您可以在 UI 中设置一个值来更改 VM 中的绑定值,但对 VM 中的值的更改不会影响 UI 中的元素。绑定到只读属性不起作用的原因是因为它仍在尝试在 UI 元素(宽度/高度)更改时执行 Get - 根据您链接到的问题。
  • 看起来你不能按照你想要的方式向后使用绑定。您真正应该做的是将鱼宽度绑定到文本块宽度,而不是将文本块宽度“反向绑定”到鱼宽度。如果您的属性(fish.Width / fish.Height)仅存在于您的 .cs 代码中,那么您的绑定应该在 .cs 代码中定义。

标签: wpf binding mvvm


【解决方案1】:

我终于意识到你想要做什么,你是对的,它应该可以工作。然而,WPF 不同意。我看到othershad beforeproblem,但这显然是设计使然。即使您只想绑定 OneWayToSource,也无法在只读属性上设置绑定。

这是一个有同样问题的问题:OneWayToSource binding from readonly property in XAML 他们的解决方法是在 xaml 元素周围放置一个容器(具有读/写宽度/高度)并在该容器上设置绑定。这可能对你有用。

在 Microsoft Connect 上存在与此相关的未解决问题,据称这是设计行为:http://connect.microsoft.com/VisualStudio/feedback/details/540833/onewaytosource-binding-from-a-readonly-dependency-property。有人在related thread 中声称使用转换器的解决方法。您可以尝试一下,但我不确定它是否适用于您的情况,因为它们的绑定是自定义控件,而不是内置框架元素。

更好

This Solution 中,Boogaart 提出了一个定义一个新的附加属性(类似于 DockPanel.Dock="Top")的实现,它允许任何元素提供其宽度和高度以供观察:

<TextBlock ...
     SizeObserver.Observe="True"
     SizeObserver.ObservedWidth="{Binding Width, Mode=OneWayToSource}"
     SizeObserver.ObservedHeight="{Binding Height, Mode=OneWayToSource}"

试穿看看是否合身。

【讨论】:

  • +1 以获得如此详细的答案。我个人喜欢最好的容器和转换器解决方案。
  • @Alain 我使用了上面 ms 连接链接中的转换器方法 - 这对我有用。它使代码有点混乱,因为我已经需要 TextBlock.Tag 来设置 ContextMenu 数据上下文 - 所以这个和 Width 和 Height 都必须打包到 MultiBinding 中 - 丑陋但有效。非常感谢您在这方面的帮助。
【解决方案2】:

如果您在某种操作(即按下按钮或单击超链接)后使用这些属性,则可以通过命令的 CommandParameter 传入 ActualWidth 和 Height。否则我会建议使用触发器,例如这里可用的触发器:

http://expressionblend.codeplex.com/wikipage?title=Behaviors%20and%20Effects&referringTitle=Documentation

我同意 OneWayToSource 绑定在只读依赖属性上不起作用似乎违反直觉。

【讨论】:

  • 这可能是一种解决方法。除非它仅在用户与控件交互时才起作用。当窗口第一次加载时,高度属性将丢失,这可能是一个问题。如果元素设置为随窗口或父控件调整大小,那么这个问题会变得更糟,因为高度和宽度会不同步,直到用户下次将鼠标悬停在控件上、单击或键入控件时。试图找到一个足够频繁触发以保持最新状态的事件将是一场噩梦,更不用说不优雅了。
  • @Alain - 这就是为什么我在最初的建议前加上 if。在某些情况下,我已经摆脱了 CommandParameter 方法,因为例如在单击按钮后需要只读属性。否则没有按照您的建议使用自定义附加属性,那么我选择使用混合触发器和操作的组合。
  • @Alian 我同意(目前)最干净的解决方案是使用自定义附加属性。
  • @Damian 感谢您的意见 Damian - 但在我的情况下,我无法采取任何行动来使用这种方法。
【解决方案3】:

尝试绑定 OneWay。我认为 OneWayToSource 是指要写入源。

http://msdn.microsoft.com/en-us/library/system.windows.data.bindingmode.aspx

我做了一个测试,确定 Width = NaN 直到宽度被分配(设置)。我知道这不是您想要的行为。尝试这个。在分配宽度的地方报告它(如 200)。如果未分配宽度,则报告为 NaN。但实际宽度是正确的。 ActualWidth 存在,但显然您尝试获取它的方式不起作用。

    <StackPanel Orientation="Vertical">
            <Border BorderThickness="1" BorderBrush="Red">
                <TextBlock Name="tbwidthA" Text="{Binding Path=Howdy}" HorizontalAlignment="Left" Width="200"/>
            </Border>
            <TextBlock Name="tbwidthAw" Text="{Binding ElementName=tbwidthA, Path=Width}"  HorizontalAlignment="Left"/>
            <TextBlock Name="tbwidthAaw" Text="{Binding ElementName=tbwidthA, Path=ActualWidth}" HorizontalAlignment="Left" />
            <Border BorderThickness="1" BorderBrush="Red">
                <TextBlock Name="tbwidthB" Text="{Binding Path=Howdy}" HorizontalAlignment="Left" />
            </Border>
            <TextBlock Name="tbwidthBw" Text="{Binding ElementName=tbwidthB, Path=Width}" HorizontalAlignment="Left" />
            <TextBlock Name="tbwidthAbw" Text="{Binding ElementName=tbwidthB, Path=ActualWidth}" HorizontalAlignment="Left" />
            <Button Content="TBwidth" Click="Button_Click_1" Width="60" HorizontalAlignment="Left" />
        </StackPanel>

有趣的是 Button 确实报告了正确的 ActualWidth 但 Width 为 NaN。

        Debug.WriteLine(tbwidthB.Width.ToString());
        Debug.WriteLine(tbwidthB.ActualWidth.ToString());

【讨论】:

  • OneWayToSource does 表示 write 到源
  • “目标”是 TextBlock.Width(一个 DP)——源是我的 Fish.width——但我需要反转它并将目标写入源,因此是 OnwayToSource。即 OnewayToSource 是正确的模式。
  • 我添加了 DataTemplate 代码以帮助澄清 - 我相信 OnewayToSource 是我想要的。
  • 您尝试过 OneWay 吗?您的问题陈述是“知道 TextBlock 的呈现宽度”。您需要绑定到 UI 元素 ActualWidth(而不是对象)。该 UI 元素就是 SOURCE。显示宽度的TextBlock是TARGET,绑定模式应该是一种方式。请发布您对 ListBox 和 TextBlock 的绑定。
  • @BalamBalam 抱歉 - 也许我不清楚 - 文本块不显示宽度。文本框位于 ListBox 中(与问题无关)。文本块呈现一些动态内容 - 这决定了其呈现的宽度/实际宽度。然后,我需要将该呈现的宽度值推送到我的 Fish 对象,以用于代码中的计算。这是否澄清。模式不是这里的问题 - OneWayToSource 是正确的 - 问题是它总是 NaN 和/或我真正想要做的是获取 ActualWidth 但我无法绑定到它(甚至不是 OneWayToSource),因为它是只读的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-04
  • 2013-03-26
  • 1970-01-01
  • 1970-01-01
  • 2023-01-05
  • 2016-11-12
相关资源
最近更新 更多