簡體   English   中英

WPF CommandParameter綁定在工具欄中失敗?

[英]WPF CommandParameter binding fails in ToolBar?

情況如下:很難描述,因此,如果需要,請跳至重新創建代碼並將其復制/粘貼到新項目中的步驟。 ListViewModel包含ViewModel列表(項)和ICommand列表(動作)。 MainWindow具有一個綁定到Action的工具欄,一個綁定到Items的ListView以及一個綁定到ListView中的動作的ContextMenu。 在我的ICommand(Command.cs)實現中,我添加了插入自定義代碼(OnIsVisible屬性)的功能,該代碼檢查命令“可見性”是應顯示還是應折疊。 在打開ContextMenu之前,此代碼非常適合ToolBar和ContextMenu中的操作。 然后,除非ContextMenu打開,否則ToolBar的CommandParameter永遠為null。

重建步驟:

  1. 在ListView中選擇一個項目
  2. 在上下文菜單中單擊“選中時顯示”
  3. 在ListView中選擇另一個項目

此時,CommandParameter綁定對於命令對象將始終為NULL。 因此,工具欄中的“選擇時顯示”按鈕將不再顯示。

碼:

在名為“ NullParameter”的新WPF應用程序項目中,創建/編輯以下文件...

MainWindow.xaml:

<Window x:Class="NullParameter.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <Style TargetType="{x:Type ListViewItem}">
            <Setter Property="IsSelected"
                    Value="{Binding IsSelected}" />
        </Style>
        <Style x:Key="contextMenuItemStyle"
               TargetType="{x:Type MenuItem}">
            <Setter Property="Header"
                    Value="{Binding Header}" />
            <Setter Property="Command"
                    Value="{Binding}" />
            <Setter Property="Visibility"
                    Value="{Binding Visibility}" />
            <Setter Property="CommandParameter"
                    Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.Tag.SelectedItems}" />
        </Style>
        <DataTemplate x:Key="toolBarActionItemTemplate">
            <Button Content="{Binding Header}"
                    Command="{Binding}"
                    CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ToolBar}, Path=Tag.SelectedItems}"
                    Visibility="{Binding Visibility}" />
        </DataTemplate>
    </Window.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.Children>
            <ToolBar Grid.Row="0"
                     ItemsSource="{Binding Actions}"
                     ItemTemplate="{StaticResource toolBarActionItemTemplate}"
                     Tag="{Binding}" />
            <ListView Grid.Row="1"
                      ItemsSource="{Binding Items}"
                      SelectionMode="Extended"
                      Tag="{Binding}">
                <ListView.ContextMenu>
                    <ContextMenu ItemsSource="{Binding Actions}"
                                 ItemContainerStyle="{StaticResource contextMenuItemStyle}" />
                </ListView.ContextMenu>
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="Id"
                                        DisplayMemberBinding="{Binding Id}"/>
                    </GridView>
                </ListView.View>
            </ListView>
        </Grid.Children>
    </Grid>

</Window>

CommandBase.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;

namespace NullParameter
{
    public abstract class CommandBase : ICommand, INotifyPropertyChanged
    {
        private Visibility _visibility;
        private string _error;

        public Visibility Visibility
        {
            get { return _visibility; }
            protected set
            {
                if (_visibility == value)
                    return;

                _visibility = value;

                if (PropertyChanged == null)
                    return;

                PropertyChanged(this, new PropertyChangedEventArgs("Visibility"));
            }
        }
        public string Error
        {
            get { return _error; }
            set
            {
                if (_error == value)
                    return;

                _error = value;

                if (PropertyChanged == null)
                    return;

                PropertyChanged(this, new PropertyChangedEventArgs("Error"));
            }
        }

        public bool CanExecute(object parameter)
        {
            Error = DoCanExecute(parameter);

            Visibility = DoIsVisible(parameter) ? Visibility.Visible : Visibility.Collapsed;

            return Error == null;
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            DoExecute(parameter);
        }

        protected abstract string DoCanExecute(object parameter);
        protected abstract bool DoIsVisible(object parameter);
        protected abstract void DoExecute(object parameter);

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

Command.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;

namespace NullParameter
{
    public class Command : CommandBase
    {
        public Action OnExecute { get; set; }
        public Func<bool> OnIsVisible { get; set; }
        public Func<string> OnCanExecute { get; set; }
        public string Header { get; set; }

        protected override string DoCanExecute(object parameter)
        {
            if (OnCanExecute == null)
                return null;

            return OnCanExecute();
        }

        protected override bool DoIsVisible(object parameter)
        {
            if (OnIsVisible == null)
                return true;

            return OnIsVisible();
        }

        protected override void DoExecute(object parameter)
        {
            if (OnExecute == null)
                return;

            OnExecute();
        }
    }

    public class Command<T> : CommandBase
        where T : class
    {
        public Action<T> OnExecute { get; set; }
        public Func<T, bool> OnIsVisible { get; set; }
        public Func<T, string> OnCanExecute { get; set; }
        public string Header { get; set; }

        protected T Convert(object parameter)
        {
            if (parameter == null)
                Console.WriteLine("NULL");

            return parameter as T;
        }

        protected override string DoCanExecute(object parameter)
        {
            if (OnCanExecute == null)
                return null;

            var p = Convert(parameter);
            if (p == null)
                return "Invalid Parameter";

            return OnCanExecute(p);
        }

        protected override bool DoIsVisible(object parameter)
        {
            if (OnIsVisible == null)
                return true;

            var p = Convert(parameter);
            if (p == null)
                return false;

            return OnIsVisible(p);
        }
        protected override void DoExecute(object parameter)
        {
            if (OnExecute == null)
                return;

            var p = Convert(parameter);
            if (p == null)
                return;

            OnExecute(p);
        }
    }
}

ListViewModel.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace NullParameter
{
    public class ListViewModel
    {
        public IList<ViewModel> Items { get; private set; }
        public IList SelectedItems { get; private set; }
        public IList<ICommand> Actions { get; private set; }

        public ListViewModel()
        {
            var items = new ObservableCollection<ViewModel>()
            {
                new ViewModel()
                {
                    Id = 1
                },
                new ViewModel()
                {
                    Id = 2
                },
                new ViewModel()
                {
                    Id = 3
                },
            };
            Items = items;
            SelectedItems = items;
            Actions = new List<ICommand>()
            {
                new Command()
                {
                    OnExecute = ShowAlways,
                    Header = "Show Always"
                },
                new Command<IList<ViewModel>>()
                {
                    OnExecute = ShowWhenSelected,
                    OnIsVisible = (list) => { return list.Count(o => o.IsSelected) > 0; },
                    Header = "Show When Selected"
                }
            };
        }

        public void ShowAlways()
        {
            Console.WriteLine("ShowAlways()");
        }

        public void ShowWhenSelected(IList<ViewModel> viewModels)
        {
            Console.WriteLine("ShowWhenSelected({0})", String.Join(",", viewModels.Where(o => o.IsSelected).Select(o => o.Id)));
        }
    }
}

ViewModel.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NullParameter
{
    public class ViewModel : INotifyPropertyChanged
    {
        private bool _isSelected;
        private int _id;

        public event PropertyChangedEventHandler PropertyChanged;

        public int Id
        {
            get { return _id; }
            set
            {
                if (_id == value)
                    return;

                _id = value;

                if (PropertyChanged == null)
                    return;

                PropertyChanged(this, new PropertyChangedEventArgs("Id"));
            }
        }
        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                if (_isSelected == value)
                    return;

                _isSelected = value;

                if (PropertyChanged == null)
                    return;

                PropertyChanged(this, new PropertyChangedEventArgs("IsSelected"));
            }
        }
    }
}

因此,在閱讀了幾篇有關CommandParameter DependencyProperty有多麻煩的文章之后,我已經放棄了完全使用它的知識。 相反,我只是通過傳入ListViewModel中選定項的列表來構造Command對象。 然后在CanExecute和Execute方法中,我使用所選項目的存儲列表而不是.NET提供的參數。

盡管這提供了可行的解決方案,但不一定解決最初的問題。 因此,對於那些不幸遇到這些問題的人,我將在此處保留其建議。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM