WPF CommandParameter является ПУСТЫМ первым разом, когда CanExecute называют

Я не эксперт по bash, но этот работал для меня:

STR1="Hello"
STR2="World"
NEWSTR=$(cat << EOF
$STR1

$STR2
EOF
)
echo "$NEWSTR"

Мне было легче форматировать тексты.

81
задан Jonas Follesø 5 December 2008 в 02:35
поделиться

8 ответов

Я наткнулся на подобную проблему и решил ее с помощью своего испытанного TriggerConverter.

public class TriggerConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // First value is target value.
        // All others are update triggers only.
        if (values.Length < 1) return Binding.DoNothing;
        return values[0];
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Этот преобразователь значения берет любое количество параметров и пасует назад первого из них как преобразованное значение. При использовании в MultiBinding в случае он похож на следующее.

<ItemsControl 
    x:Name="commentsList"
    ItemsSource="{Binding Path=SharedDataItemPM.Comments}"
    Width="Auto" Height="Auto">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Button                             
                    Content="Delete"
                    FontSize="10"
                    CommandParameter="{Binding}">
                    <Button.Command>
                        <MultiBinding Converter="{StaticResource TriggerConverter}">
                            <Binding Path="DataContext.DeleteCommentCommand"
                                     ElementName="commentsList" />
                            <Binding />
                        </MultiBinding> 
                    </Button.Command>
                </Button>
            </StackPanel>                                       
         </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

необходимо будет добавить TriggerConverter как ресурс где-нибудь, чтобы это работало. Теперь свойство Command установлено не, прежде чем значение для CommandParameter стало доступным. Вы могли даже связать с RelativeSource. Сам и CommandParameter вместо. достигнуть того же эффекта.

14
ответ дан David Liersch 5 November 2019 в 17:25
поделиться

commandManager. InvalidateRequerySuggested работает на меня также. Я верю переговорам по следующей ссылке о подобной проблеме, и M$ dev подтвердил ограничение в текущей версии и commandManager. InvalidateRequerySuggested является обходным решением. http://social.expression.microsoft.com/Forums/en-US/wpf/thread/c45d2272-e8ba-4219-bb41-1e5eaed08a1f/

, Что важный синхронизация вызова commandManager. InvalidateRequerySuggested. Это должно быть вызвано после того, как соответствующее изменение значения уведомляется.

-1
ответ дан 5 November 2019 в 17:25
поделиться

Съемка общим планом. для отладки этого, можно попробовать:
- проверка события PreviewCanExecute.
- используют snoop/wpf родинку, чтобы посмотреть внутри и видеть, каков commandparameter.

HTH,

-1
ответ дан Dennis 5 November 2019 в 17:25
поделиться

Эй Jonas, не уверенный, если это будет работать в шаблоне данных, но здесь является обязательным синтаксисом, я использую в Контекстном меню ListView для захвата текущего объекта как параметра команды:

CommandParameter = "{Привязка RelativeSource = {RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget. SelectedItem, Mode=TwoWay}"

0
ответ дан 5 November 2019 в 17:25
поделиться

Вы можете использовать мой CommandParameterBehavior, что я отправил на форумы Призмы вчера. Это добавляет недостающее поведение, где изменение в эти CommandParameter заставляет Command быть повторно запрошенным.

существует некоторая сложность, здесь вызванная моими попытками избежать утечки памяти, вызванной, если Вы звоните PropertyDescriptor.AddValueChanged без более позднего вызова PropertyDescriptor.RemoveValueChanged. Я пытаюсь зафиксировать это путем нерегистрации обработчика, когда элемент разгружен.

необходимо будет, вероятно, удалить эти IDelegateCommand материал, если Вы не будете использовать Призму (и хотят внести те же изменения как я к библиотеке Prism). Также обратите внимание, что мы обычно не используем RoutedCommand с здесь (мы используем Призму DelegateCommand<T> для в значительной степени всего), поэтому не держите меня ответственный, если мой вызов к CommandManager.InvalidateRequerySuggested выделяет своего рода квант wavefuntion каскад коллапса, который уничтожает известную вселенную или что-либо.

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;

namespace Microsoft.Practices.Composite.Wpf.Commands
{
    /// <summary>
    /// This class provides an attached property that, when set to true, will cause changes to the element's CommandParameter to 
    /// trigger the CanExecute handler to be called on the Command.
    /// </summary>
    public static class CommandParameterBehavior
    {
        /// <summary>
        /// Identifies the IsCommandRequeriedOnChange attached property
        /// </summary>
        /// <remarks>
        /// When a control has the <see cref="IsCommandRequeriedOnChangeProperty" />
        /// attached property set to true, then any change to it's 
        /// <see cref="System.Windows.Controls.Primitives.ButtonBase.CommandParameter" /> property will cause the state of
        /// the command attached to it's <see cref="System.Windows.Controls.Primitives.ButtonBase.Command" /> property to 
        /// be reevaluated.
        /// </remarks>
        public static readonly DependencyProperty IsCommandRequeriedOnChangeProperty =
            DependencyProperty.RegisterAttached("IsCommandRequeriedOnChange",
                                                typeof(bool),
                                                typeof(CommandParameterBehavior),
                                                new UIPropertyMetadata(false, new PropertyChangedCallback(OnIsCommandRequeriedOnChangeChanged)));

        /// <summary>
        /// Gets the value for the <see cref="IsCommandRequeriedOnChangeProperty"/> attached property.
        /// </summary>
        /// <param name="target">The object to adapt.</param>
        /// <returns>Whether the update on change behavior is enabled.</returns>
        public static bool GetIsCommandRequeriedOnChange(DependencyObject target)
        {
            return (bool)target.GetValue(IsCommandRequeriedOnChangeProperty);
        }

        /// <summary>
        /// Sets the <see cref="IsCommandRequeriedOnChangeProperty"/> attached property.
        /// </summary>
        /// <param name="target">The object to adapt. This is typically a <see cref="System.Windows.Controls.Primitives.ButtonBase" />, 
        /// <see cref="System.Windows.Controls.MenuItem" /> or <see cref="System.Windows.Documents.Hyperlink" /></param>
        /// <param name="value">Whether the update behaviour should be enabled.</param>
        public static void SetIsCommandRequeriedOnChange(DependencyObject target, bool value)
        {
            target.SetValue(IsCommandRequeriedOnChangeProperty, value);
        }

        private static void OnIsCommandRequeriedOnChangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is ICommandSource))
                return;

            if (!(d is FrameworkElement || d is FrameworkContentElement))
                return;

            if ((bool)e.NewValue)
            {
                HookCommandParameterChanged(d);
            }
            else
            {
                UnhookCommandParameterChanged(d);
            }

            UpdateCommandState(d);
        }

        private static PropertyDescriptor GetCommandParameterPropertyDescriptor(object source)
        {
            return TypeDescriptor.GetProperties(source.GetType())["CommandParameter"];
        }

        private static void HookCommandParameterChanged(object source)
        {
            var propertyDescriptor = GetCommandParameterPropertyDescriptor(source);
            propertyDescriptor.AddValueChanged(source, OnCommandParameterChanged);

            // N.B. Using PropertyDescriptor.AddValueChanged will cause "source" to never be garbage collected,
            // so we need to hook the Unloaded event and call RemoveValueChanged there.
            HookUnloaded(source);
        }

        private static void UnhookCommandParameterChanged(object source)
        {
            var propertyDescriptor = GetCommandParameterPropertyDescriptor(source);
            propertyDescriptor.RemoveValueChanged(source, OnCommandParameterChanged);

            UnhookUnloaded(source);
        }

        private static void HookUnloaded(object source)
        {
            var fe = source as FrameworkElement;
            if (fe != null)
            {
                fe.Unloaded += OnUnloaded;
            }

            var fce = source as FrameworkContentElement;
            if (fce != null)
            {
                fce.Unloaded += OnUnloaded;
            }
        }

        private static void UnhookUnloaded(object source)
        {
            var fe = source as FrameworkElement;
            if (fe != null)
            {
                fe.Unloaded -= OnUnloaded;
            }

            var fce = source as FrameworkContentElement;
            if (fce != null)
            {
                fce.Unloaded -= OnUnloaded;
            }
        }

        static void OnUnloaded(object sender, RoutedEventArgs e)
        {
            UnhookCommandParameterChanged(sender);
        }

        static void OnCommandParameterChanged(object sender, EventArgs ea)
        {
            UpdateCommandState(sender);
        }

        private static void UpdateCommandState(object target)
        {
            var commandSource = target as ICommandSource;

            if (commandSource == null)
                return;

            var rc = commandSource.Command as RoutedCommand;
            if (rc != null)
            {
                CommandManager.InvalidateRequerySuggested();
            }

            var dc = commandSource.Command as IDelegateCommand;
            if (dc != null)
            {
                dc.RaiseCanExecuteChanged();
            }

        }
    }
}
5
ответ дан Swythan 5 November 2019 в 17:25
поделиться

Я нашел, что порядок, в котором я установил Команду и CommandParameter, имеет значение. Установка свойства Command заставляет CanExecute быть сразу названным, таким образом, Вы хотите, чтобы CommandParameter уже был установлен в той точке.

я нашел, что переключение порядка свойств в XAML может на самом деле иметь эффект, хотя я не уверен, что это решит Вашу проблему. Это стоит попытки, все же.

Вы, кажется, предполагаете, что кнопка никогда не становится включенной, что удивительно, так как я ожидал бы, что CommandParameter будет установлен вскоре после свойства Command в Вашем примере. Делает вызов CommandManager. InvalidateRequerySuggested () заставляют кнопку становиться включенной?

28
ответ дан Ed Ball 5 November 2019 в 17:25
поделиться

I've logged this as a bug against WPF in .Net 4.0, as the problem still exists in Beta 2.

https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=504976

0
ответ дан 24 November 2019 в 09:41
поделиться

Существует относительно простой способ «исправить» эту проблему с помощью DelegateCommand, хотя он требует обновления исходного кода DelegateCommand и повторной компиляции Microsoft.Practices. Composite.Presentation.dll.

1) Загрузите исходный код Prism 1.2 и откройте CompositeApplicationLibrary_Desktop.sln. Здесь находится проект Composite.Presentation.Desktop, содержащий исходный код DelegateCommand.

2) В общедоступном событии EventHandler CanExecuteChanged измените его следующим образом:

public event EventHandler CanExecuteChanged
{
     add
     {
          WeakEventHandlerManager.AddWeakReferenceHandler( ref _canExecuteChangedHandlers, value, 2 );
          // add this line
          CommandManager.RequerySuggested += value;
     }
     remove
     {
          WeakEventHandlerManager.RemoveWeakReferenceHandler( _canExecuteChangedHandlers, value );
          // add this line
          CommandManager.RequerySuggested -= value;
     }
}

3) В защищенной виртуальной пустоте OnCanExecuteChanged () измените его следующим образом:

protected virtual void OnCanExecuteChanged()
{
     // add this line
     CommandManager.InvalidateRequerySuggested();
     WeakEventHandlerManager.CallWeakReferenceHandlers( this, _canExecuteChangedHandlers );
}

4) Перекомпилируйте решение, затем перейдите к Папка отладки или выпуска, в которой находятся скомпилированные библиотеки DLL. Скопируйте Microsoft.Practices.Composite.Presentation.dll и .pdb (если хотите) туда, где вы ссылаетесь на свои внешние сборки, а затем перекомпилируйте приложение, чтобы получить новые версии.

После этого CanExecute должен запускаться каждый раз, когда пользовательский интерфейс отображает элементы, привязанные к рассматриваемой DelegateCommand.

Будь осторожен, Джо

, судья, Джо в Gmail

1
ответ дан 24 November 2019 в 09:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: