C# 工具栏中的WPF CommandParameter绑定失败?
情况是这样的:这有点难以描述,因此,如果需要,请跳到重新创建代码并将代码复制/粘贴到新项目中的步骤。ListViewModel包含ViewModel(项目)列表和ICommand(操作)列表。MainWindow有一个绑定到操作的工具栏、一个绑定到项目的ListView和一个绑定到ListView中的操作的ContextMenu。在我的ICommand(Command.cs)实现中,我添加了插入自定义代码(OnIsVisible属性)的功能,用于检查命令可见性是否应可见或折叠。这段代码非常适用于工具栏和ContextMenu中的操作,直到您打开ContextMenu。然后工具栏的CommandParameter将永远为空,除非ContextMenu处于打开状态 重新创建的步骤:C# 工具栏中的WPF CommandParameter绑定失败?,c#,wpf,mvvm,icommand,commandparameter,C#,Wpf,Mvvm,Icommand,Commandparameter,情况是这样的:这有点难以描述,因此,如果需要,请跳到重新创建代码并将代码复制/粘贴到新项目中的步骤。ListViewModel包含ViewModel(项目)列表和ICommand(操作)列表。MainWindow有一个绑定到操作的工具栏、一个绑定到项目的ListView和一个绑定到ListView中的操作的ContextMenu。在我的ICommand(Command.cs)实现中,我添加了插入自定义代码(OnIsVisible属性)的功能,用于检查命令可见性是否应可见或折叠。这段代码非常适用于
<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>
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);
}
}
}
因此,在阅读了一些关于CommandParameter DependencyProperty有多麻烦的帖子之后,我已经完全放弃了使用它。相反,我只是通过在ListViewModel中传递所选项目的列表来构造命令对象。然后在CanExecute和Execute方法中,我使用存储的选定项列表,而不是.NET提供的参数 虽然这提供了一个可行的解决方案,但并不一定能解决最初问题所带来的问题。因此,我将把这一点留在这里,作为对其他不幸遇到这些问题的人的建议
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);
}
}
}
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)));
}
}
}
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"));
}
}
}
}