绑定 ElementName return 来自先前实例化的 DataTemplate 的实例

Binding ElementName return an instance from a previously instancied DataTemplate

我有以下数据模板

<DataTemplate x:Key="ArchiveModeContentTemplate">
    <Button Style="{x:Static ui:ButtonStyles.DrawingButtonLabel}" Grid.Row="1" Grid.Column="0" Foreground="{x:Static ui:UbiBrushes.UbiDarkBlue}"
                                Content="{StaticResource ValidateIcon48}" ui:StyleProperties.Label="{DynamicResource Archive}"
                                Command="{Binding ElementName=factory,Path=BuildPopup}">
                    <i:Interaction.Behaviors>
                        <pop:PopupFactory x:Name="factory" Factory="{Binding ConfirmArchivingFactory}" />
                    </i:Interaction.Behaviors>
                </Button>
    </DataTemplate>

PopupFactory 有一个命令 BuildPopup。此命令被提供给具有 ElementName.

绑定的按钮

第一次显示此数据模板时,它工作正常。按钮获取命令。但是,如果此 dataTemplate 被卸载然后再次显示,则绑定将 PopupFactory 的前一个实例而不是新创建的实例的命令提供给按钮。

我传入 PopupFactory 的构造函数并将其附加到新按钮。所以这不是 PopupFactory 在模板之间共享的问题。

为什么会这样?这是 xaml 缓存的错误吗?

编辑

我现在有一个更奇怪的错误。
我将语法更改为以下内容,以便在 Xaml 中的名称声明之后具有绑定 elementName。现在该命令可以正常工作,但是使用绑定 RelativeSource 查找名为 GoBack 的命令的第二个按钮不再起作用。我使用 snoop 检查绑定,它抱怨找不到命令 BuildPopup。 WPF 越来越疯狂了!

<Button Style="{x:Static ui:ButtonStyles.DrawingButtonLabel}" Grid.Row="1" Grid.Column="0" Foreground="{x:Static ui:UbiBrushes.UbiDarkBlue}"
                                    Content="{StaticResource ValidateIcon48}" ui:StyleProperties.Label="{DynamicResource Archive}">
  <i:Interaction.Behaviors>
  <pop:PopupFactory x:Name="Archivefactory" Factory="{Binding ConfirmArchivingFactory}" IsSingleInstance="False" />
  </i:Interaction.Behaviors>
    <Button.Command>
        <Binding ElementName="Archivefactory" Path="BuildPopup" />
    </Button.Command>
</Button>

<Button Grid.Row="1" Grid.Column="1"
                                Style="{x:Static ui:ButtonStyles.DrawingButtonLabel}"
                                Content="{StaticResource CrossIcon48}"
                                Foreground="Green"
                                ui:StyleProperties.Label="{DynamicResource Cancel}"
                                Command="{Binding Path=GoBack, RelativeSource={RelativeSource AncestorType={x:Type ui:DrillDown}}}" />

编辑

这里是PopupFactory的代码

public class PopupFactory : Behavior<UIElement>
    {
        public ICommand BuildPopup { get; private set; }

        private bool _canExecute;

        private IDisposable _canexecuteSubscription = null;

        public IObservable<bool> CanExecuteSource
        {
            get { return (IObservable<bool>)GetValue(CanExecuteSourceProperty); }
            set { SetValue(CanExecuteSourceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CanExecute.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CanExecuteSourceProperty =
            DependencyProperty.Register("CanExecute", typeof(IObservable<bool>), typeof(PopupFactory), new PropertyMetadata(null));

        private static void OnCanExecuteSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs arg)
        {
            var factory = obj as PopupFactory;
            factory._canexecuteSubscription?.Dispose();
            if (arg.NewValue != null)
            {
                factory._canexecuteSubscription = ((IObservable<bool>)arg.NewValue)
                    .ObserveOnDispatcher()
                    .Subscribe(factory.UpdateCanExecute);
            }
        }

        private void UpdateCanExecute(bool value)
        {
            _canExecute = value;
            ((RelayCommand<object>)BuildPopup).RaiseCanExecuteChanged();
        }

        public IFactory Factory
        {
            get { return (IFactory)GetValue(FactoryProperty); }
            set { SetValue(FactoryProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Factory.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FactoryProperty =
            DependencyProperty.Register("Factory", typeof(IFactory), typeof(PopupFactory), new PropertyMetadata(null, OnFactoryChanged));

        private static void OnFactoryChanged(DependencyObject obj, DependencyPropertyChangedEventArgs arg)
        {
            var factory = obj as PopupFactory;
            ((RelayCommand<object>)factory.BuildPopup).RaiseCanExecuteChanged();
        }

        public UIElement PlacementTarget
        {
            get { return (UIElement)GetValue(PlacementTargetProperty); }
            set { SetValue(PlacementTargetProperty, value); }
        }

        // Using a DependencyProperty as the backing store for PlacementTarget.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PlacementTargetProperty =
            DependencyProperty.Register("PlacementTarget", typeof(UIElement), typeof(PopupFactory), new PropertyMetadata(null));

        public PlacementMode Placement
        {
            get { return (PlacementMode)GetValue(PlacementProperty); }
            set { SetValue(PlacementProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Placement.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PlacementProperty =
            DependencyProperty.Register("Placement", typeof(PlacementMode), typeof(PopupFactory), new PropertyMetadata(PlacementMode.Center));

        public bool IsSingleInstance
        {
            get { return (bool)GetValue(IsSingleInstanceProperty); }
            set { SetValue(IsSingleInstanceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsSingleInsance.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsSingleInstanceProperty =
            DependencyProperty.Register("IsSingleInstance", typeof(bool), typeof(PopupFactory), new PropertyMetadata(false));

        private bool _singleInstanceShowed = false;

        public PopupFactory()
        {
            BuildPopup = new RelayCommand<object>((f) =>
            {
                ShowPopup(f);
            }, (p) =>
            {
                return _canExecute && Factory != null && !_singleInstanceShowed;
            });
            UpdateCanExecute(true);
        }

        public IOverlayContainer ShowPopup(object parameter)
        {
            var param = new PopupParameter() { Owner = AssociatedObject };

            UIElement target = PlacementTarget != null ? PlacementTarget : AssociatedObject;

            var item = Factory.Build(parameter);
            param.Content = item.Item;
            param.Owner = AssociatedObject;
            param.RemoveCondition = item.DisposeStream;
            var container = OverlayManager.ShowPopup(param);
            var placement = new PopupRelativePlacement(container as FrameworkElement, target,
                       Placement, false);
            item.PostFactory?.Invoke();
            if (IsSingleInstance)
            {
                _singleInstanceShowed = true;
                OverlayManager.PopupOperations.Where((op) => op.Id == container.Id && op.Operationtype == OverlayOperation.OpType.PopupRemoved)
                .Once((_) =>
                {
                    _singleInstanceShowed = false;
                    ((RelayCommand<object>)BuildPopup).RaiseCanExecuteChanged();
                });
            }
            return container;
        }
    }

问题已解决。

我将 PopupFactory 行为移至按钮的可视父级。这样,行为是在按钮之前创建的,并且 WPF 不会在绑定期间搞乱名称解析。