Binding ElementName возвращает экземпляр из ранее созданного экземпляра DataTemplate.

У меня есть следующий 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.

При первом отображении этого шаблона данных он работает нормально. Кнопка получить команду. Но если этот шаблон данных выгружается, а затем отображается снова, привязка дает кнопке команду предыдущего экземпляра PopupFactory, а не вновь созданного экземпляра.

Перехожу в конструктор PopupFactory и он привязывается к новой кнопке. Так что это не проблема PopupFactory совместного использования шаблонами.

Почему это происходит? это ошибка с кешем xaml?

Изменить

Теперь у меня есть еще более странная ошибка.
Я изменил синтаксис на следующий, чтобы иметь привязку elementName после объявления имени в Xaml. Теперь команда работает правильно, но вторая кнопка, которая использует привязку 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;
        }
    }

person Gael    schedule 11.09.2019    source источник
comment
IsSingleInstance не имеет отношения к проблеме. Он только сообщает popupFactory, что он не может генерировать всплывающее окно, если оно уже открыто. Я уберу его из примера кода, чтобы избежать путаницы.   -  person Gael    schedule 11.09.2019
comment
Пожалуйста, поделитесь кодом для PopupFactory.   -  person 15ee8f99-57ff-4f92-890c-b56153    schedule 11.09.2019


Ответы (1)


Проблема решена.

Я переместил поведение PopupFactory в визуальный родитель кнопки. Таким образом, поведение создается перед кнопкой, и WPF не искажает разрешение имени во время привязки.

person Gael    schedule 12.09.2019