C#/WPF:绑定到视觉/逻辑树之外的元素

4

我正在使用来自扩展WPF工具包的DropDownButton控件。在其中,我托管了一个ListBox,当选择元素更改时,应该隐藏包含的DropDownButton。

我最初的做法是与Microsoft的交互框架一起使用:

<xctk:DropDownButton x:Name="WorkspaceSelectorContainer" Content="This is the active workspace">
                    <xctk:DropDownButton.DropDownContent>
                            <ListBox ItemsSource="{Binding workspaces}">
                                <i:Interaction.Triggers>
                                    <i:EventTrigger EventName="SelectionChanged">
                                        <ie:ChangePropertyAction PropertyName="IsOpen" Value="False" TargetObject="{Binding ElementName=WorkspaceSelectorContainer}"/>
                                    </i:EventTrigger>
                                </i:Interaction.Triggers>
                            </ListBox>
                        </StackPanel>
                    </xctk:DropDownButton.DropDownContent>
                </xctk:DropDownButton>

但是这并没有起作用,因为无法找到WorkspaceSelectorContainer。 我尝试通过查找DropDownButton类型的祖先元素来进行绑定,但这也不起作用。跟踪绑定显示它只解析了DropDownButton.DropDownContent的最外层元素的祖先,而没有进一步解析;例如DropDownButton本身不是祖先树的一部分。
好的,那么我的下一个尝试是:
<ie:ChangePropertyAction PropertyName="IsOpen" Value="False" TargetObject="{Binding Source={x:Reference WorkspaceSelectorContainer}}"/>

但是这也不起作用,因为它抛出了一个异常。

A first chance exception of type 'System.Xaml.XamlObjectWriterException' occurred in System.Xaml.dll

Additional information: Cannot call MarkupExtension.ProvideValue because of a cyclical dependency. Properties inside a MarkupExtension cannot reference objects that reference the result of the MarkupExtension. 

我已经尝试了各种方法,但仍无法解决问题。请问有没有人有什么好的想法可以解决这个问题?

是否可能以某种方式引用包含所有内容的根网格,并从中搜索DropDownButton元素? 类似于 {Binding Source={x:Reference MyRootGrid}, ElementName=WorkspaceSelectorContainer} (由于不能组合source和ElementName,因此这样做是不起作用的)

谢谢!


您的标记具有一个残留的</StackPanel>标签。 - Martin
2个回答

4

我曾经遇到过相同的问题(虽然是在Silverlight中)。 通过引入一个资源对象,该对象具有对DropDownButton的引用,并且可以轻松地从DropDownContent中进行绑定来解决它:

<Foo.Resources>
    <BindableObjectReference
      x:Key="WorkspaceSelectorContainerReference"
      Object="{Binding ElementName=WorkspaceSelectorContainer}"/>
</Foo.Resources>
<DropDownButton x:Name="WorkspaceSelectorContainer" ...>
    <DropDownButton.DropDownContent>
        ...
        <ChangePropertyAction
            PropertyName="IsOpen"
            Value="False"
            TargetObject="{Binding Path=Object,
                Source={StaticResource WorkspaceSelectorContainerReference}}"/>
        ...
    </DropDownButton.DropDownContent>
</DropDownButton>

...以及魔法对象

public class BindableObjectReference : DependencyObject
{
    public object Object
    {
        get { return GetValue( ObjectProperty ); }
        set { SetValue( ObjectProperty, value ); }
    }

    public static readonly DependencyProperty ObjectProperty =
        DependencyProperty.Register( "Object", typeof( object ),
        typeof( BindableObjectReference ), new PropertyMetadata( null ) );
}

这样优雅的方式,终于出现了。非常感谢您,先生! - AgentFire

0
<ie:ChangePropertyAction PropertyName="IsOpen" Value="False"
                         TargetName="WorkspaceSelectorContainer" />

很不幸,这也不起作用。虽然我不知道如何调试这个问题,但我猜想问题可能与尝试使用TargetObject绑定ElementName时相同。 - Bogey
嗯,尝试将ListBox添加到与DropDownButton相同的NameScope中 - 例如在ListBox.Initialized事件中。仅凭记忆:NameScope.SetNameScope(MyListBox, NameScope.GetNameScope(WorkspaceSelectorContainer)); - Liero

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