【问题标题】:Binding ElementName return an instance from a previously instancied DataTemplate绑定 ElementName 从先前实例化的 DataTemplate 返回一个实例
【发布时间】:2019-09-11 14:09:22
【问题描述】:

我有以下数据模板

<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;
        }
    }

【问题讨论】:

  • IsSingleInstance 与问题无关。它只告诉 popupFactory 如果 t 已经打开了它就不能生成弹出窗口。我将从代码示例中删除它以避免混淆。
  • 请分享 PopupFactory 的代码。

标签: wpf xaml data-binding attachedbehaviors


【解决方案1】:

问题解决了。

我将 PopupFactory Behavior 移至按钮的可视父级。这样,行为是在按钮之前创建的,WPF 在绑定过程中不会弄乱名称解析。

【讨论】:

    猜你喜欢
    • 2021-11-30
    • 2012-07-14
    • 2019-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-21
    相关资源
    最近更新 更多