【问题标题】:Error "Specified element is already the logical child of another element"?错误“指定元素已经是另一个元素的逻辑子元素”?
【发布时间】:2011-04-30 19:13:47
【问题描述】:

我有一个 TabControl,每个 Tab 可以包含相同的 UI,但数据不同。在任何选项卡中,用户都可以单击一个按钮并弹出一个弹出窗口。这会为 ViewModel 设置一个 Style 属性,告诉它要为弹出 UI 使用什么样式。 Style 绑定到自定义的 DependecyProperty,该 DependecyProperty 附加到自定义的 PopupUserControl。我的问题是,当弹出窗口的第二个副本在另一个选项卡中打开时,我收到以下错误(无论应用什么样式):

指定的元素已经是 另一个元素的逻辑子元素。 先断开连接。

ButtonClick 命令:

MyViewModel vm = ((Button)sender).DataContext as MyViewModel;
if (vm != null)
{
    Style popupStyle = (Style)Application.Current.FindResource("SomePopupStyle");
    vm.EditPanelStyle= popupStyle ;
}

这会触发 Style 上的 PropertyChange 事件

public Style EditPanelStyle
{
    get { return _editPanelStyle; }
    set
    {
        if (_editPanelStyle != value)
        {
            _editPanelStyle = value;
            OnPropertyChanged("EditPanelStyle");
        }
    }
}

触发 ViewModelBase 中的 OnPropertyChanged 事件

protected virtual void OnPropertyChanged(string propertyName)
{
    this.VerifyPropertyName(propertyName);

    PropertyChangedEventHandler handler = this.PropertyChanged;
    if (handler != null)
    {
        var e = new PropertyChangedEventArgs(propertyName);
        handler(this, e);
    }
}

错误发生在 ViewModelBase 中的handler(this, e);

编辑

TabItem 包含一个 Canvas 和一堆可以添加/删除/移动/等的面板。每个 Panel 都有自己的资源目录。在面板中,我可以很好地设置 PopupStyle,它可以毫无问题地应用。面板中使用的样式也在 PanelResourceDictionary 中定义。

失败和成功的主要区别在于样式位于不同的位置。

编辑 #2 失败的样式 - LookupDialog 是自定义 WPF 用户控件

<!-- Popup Style for LookupDialog -->
<Style x:Key="LookupDialogBaseStyle" TargetType="{x:Type localControls:DraggablePanel}" BasedOn="{StaticResource GenericPopupStyle}">
    <Setter Property="Canvas.Left" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type localControls:PopupPanel}}, 
        Path=ActualWidth, Converter={StaticResource PercentToDoubleConverter}, ConverterParameter=.25}" />
    <Setter Property="Canvas.Top" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type localControls:PopupPanel}}, 
        Path=ActualHeight, Converter={StaticResource PercentToDoubleConverter}, ConverterParameter=.3}" />
    <Setter Property="Width" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type localControls:PopupPanel}}, 
        Path=ActualWidth, Converter={StaticResource PercentToDoubleConverter}, ConverterParameter=.5}" />
    <Setter Property="Height" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type localControls:PopupPanel}}, 
        Path=ActualHeight, Converter={StaticResource PercentToDoubleConverter}, ConverterParameter=.4}" />

    <!--<Setter Property="localControls:PopupPanel.PopupEnterKeyCommand" Value="{Binding Path=SaveCommand}" />-->
    <Setter Property="localControls:PopupPanel.PopupEscapeKeyCommand" Value="{Binding Path=CancelCommand}" />

    <Setter Property="Header" Value="{Binding Path=Header}" />
    <Setter Property="localControls:PopupPanel.BackgroundOpacity" Value="0" />

    <Setter Property="Content">
        <Setter.Value>
            <localControls:LookupDialog 
                DataContext="{Binding}"
                BorderBrush="{StaticResource DarkColor}" />
        </Setter.Value>
    </Setter>
</Style>

<!-- Base Style for a Popup (DraggablePanel) -->
<Style x:Key="GenericPopupStyle" TargetType="{x:Type localControls:DraggablePanel}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type localControls:DraggablePanel}">
                <Border Height="{TemplateBinding Height}" Width="{TemplateBinding Width}">
                    <DockPanel>
                        <!-- Header -->
                        <Border 
                            DockPanel.Dock="Top"
                            MinHeight="20"
                            Background="{DynamicResource TabItem_BackgroundBrush_Unselected}"
                            BorderBrush="{StaticResource DarkColor}"
                            BorderThickness="1"
                            CornerRadius="5,5,0,0"
                            Padding="2,3,2,2"
                            SnapsToDevicePixels="True"
                            >

                            <ContentPresenter x:Name="PART_DraggablePanelHeader" ContentSource="Header" />
                        </Border>

                        <!-- Content -->
                        <Border Background="{StaticResource DefaultBackground}" 
                                BorderBrush="{StaticResource DarkColor}" 
                                BorderThickness="1,0,1,1"
                                SnapsToDevicePixels="True">
                            <ContentPresenter ContentSource="Content" />
                        </Border>
                    </DockPanel>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

有效的风格:

<!-- Example Popup Style for a Panel -->
<Style x:Key="AgentDesktop_NotesPanelPopupStyle" TargetType="{x:Type ContentControl}">
    <Setter Property="Canvas.Left" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type localControls:PopupPanel}}, 
        Path=ActualWidth, Converter={StaticResource PercentToDoubleConverter}, ConverterParameter=.2}" />
    <Setter Property="Canvas.Top" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type localControls:PopupPanel}}, 
        Path=ActualHeight, Converter={StaticResource PercentToDoubleConverter}, ConverterParameter=.32}" />
    <Setter Property="Width" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type localControls:PopupPanel}}, 
        Path=ActualWidth, Converter={StaticResource PercentToDoubleConverter}, ConverterParameter=.6}" />
    <Setter Property="Height" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type localControls:PopupPanel}}, 
        Path=ActualHeight, Converter={StaticResource PercentToDoubleConverter}, ConverterParameter=.36}" />

    <Setter Property="localControls:PopupPanel.PopupEnterKeyCommand" Value="{Binding Path=SaveCommand}" />
    <Setter Property="localControls:PopupPanel.PopupEscapeKeyCommand" Value="{Binding Path=HidePopupCommand}" />

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <!-- Control Template removed to make this easier to read, but it's created from standard WPF controls with nothing special -->
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

【问题讨论】:

    标签: wpf xaml mvvm


    【解决方案1】:

    我的问题是我在我的样式中设置了Content,而Content 的逻辑父级不能超过一个。我需要将其移至Template。由于我不想丢失基本样式,因此我在 HeaderedContentControl(我的代码中的 DraggablePanel)的 ContentTemplate 属性中设置了内容。

    无论如何都要感谢戴维帮助我解决这个问题。

    【讨论】:

    • 对于每个有内容的控件,内容似乎也需要是唯一的资源。至少这就是我的图像所需要的样式来更改用于 2 个切换按钮的图像。
    • @Dave 要么是唯一的,要么是某种模板。 ControlTemplate、DataTemplate、ContentTemplate 等
    【解决方案2】:

    确保您正在创建一个新的选项卡对象,并且您没有第二次尝试将相同的选项卡插入选项卡控件。一个控件只能有 1 个父级,问题似乎是您试图将选项卡插入 2 个不同的容器,或者更可能是同一选项卡控件两次。

    【讨论】:

    • 它绝对是一个单独的 TabControl。我对这件事如此沮丧的部分原因是我已经从 TabControl 内部以完全相同的方式成功地设置了 PopupStyle,唯一的区别是这个弹出窗口是从 TabControl 本身而不是从其子项中设置的。嗯,我将在我的问题中对此进行扩展
    • @Rachel 你能显示绑定到 EditPanelStyle 的内容吗?我不认为您显示的代码直接是错误所在。如果您没有任何代码显式处理 PropertyChanged 事件,则错误可能在 xaml 端数据绑定中。
    • 我认为你是对的,我只是在运行一些测试,似乎是样式本身导致了问题,而不是代码。我会发布样式
    • 我想我刚刚弄明白了......这是因为我在失败的风格中应用了Content,而我在新的风格中应用了Template。并且内容不能添加到两个不同的逻辑父级。现在我只需要找到一种方法来重写它......感谢您帮助我完成它!
    • 不客气,对不起,我无法更直接地提供帮助,实际上已经有几个月没有在 WPF 中工作了。
    【解决方案3】:

    要不阅读以上所有内容,简短的回答是:您需要将 Content 更改为 Template(在您的 XAML 中,无论是样式还是直接声明 ContentControl)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-05
      • 2012-02-20
      • 1970-01-01
      • 2012-08-23
      • 1970-01-01
      • 1970-01-01
      • 2012-11-17
      相关资源
      最近更新 更多