我希望对某人来说是一个明显的问题。我希望当用户双击ListBox中的ListBoxItem时触发编辑命令。我之前在用户控件中已经完成了这个操作但是想直接在VIEW中完成它,因为它是一个足够简单的ListBox。但它不会接线。
这是列表框:
<ListBox SelectedItem="{Binding DataQuerySortSelected}"
ItemContainerStyle="{StaticResource SortListItemStyle}"
ItemsSource="{Binding DataQueryHolder.DataQuerySorts}">
<ListBox.InputBindings>
<KeyBinding Key="Delete" Command="{Binding DataQuerySortDelete}" />
</ListBox.InputBindings>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="AllowDrop" Value="True" />
<EventSetter Event="PreviewMouseMove" Handler="DragDropListBoxItem_PreviewMouseMoveEvent" />
<EventSetter Event="Drop" Handler="DragDropListBoxItem_Drop" />
</Style>
</ListBox.Resources>
</ListBox>
请注意顶层的Delete Key绑定工作正常。这是引用的样式(作为单独的ResourceDictionary引入但是内联样式没有区别):
<Style x:Key="SortListItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="MainBorder">
<ContentPresenter>
<ContentPresenter.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding DataQuerySortEdit}" />
</ContentPresenter.InputBindings>
</ContentPresenter>
<Border.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding DataQuerySortEdit}" />
</Border.InputBindings>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="MainBorder" Value="Yellow" Property="Background" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
我将鼠标绑定在两个地方只是为了看它是否有所不同,但事实并非如此。那里没有任何接线。其他一切都按预期工作。如果我在视图中创建一个普通按钮并将其指向DataQuerySortEdit命令,它将按预期工作。
我错过了什么吗?谢谢你的帮助。
编辑:只是添加一些信息以回应J的回应。我给ControlTemplate的Border一个绑定相对于最近的列表框,并给列表框一个名称,以便输出确认它会找到它。这是输出窗口:
System.Windows.Data Error: 40 : BindingExpression path error: 'DataQuerySortEdit' property not found on 'object' ''DataQuerySort' (HashCode=7641038)'. BindingExpression:Path=DataQuerySortEdit; DataItem='DataQuerySort' (HashCode=7641038); target element is 'MouseBinding' (HashCode=65119131); target property is 'Command' (type 'ICommand')
System.Windows.Data Error: 40 : BindingExpression path error: 'DataQuerySortEdit' property not found on 'object' ''DataQuerySort' (HashCode=50439840)'. BindingExpression:Path=DataQuerySortEdit; DataItem='DataQuerySort' (HashCode=50439840); target element is 'MouseBinding' (HashCode=3649016); target property is 'Command' (type 'ICommand')
System.Windows.Data Error: 40 : BindingExpression path error: 'DataQuerySortEdit' property not found on 'object' ''DataQuerySort' (HashCode=65588106)'. BindingExpression:Path=DataQuerySortEdit; DataItem='DataQuerySort' (HashCode=65588106); target element is 'MouseBinding' (HashCode=35717517); target property is 'Command' (type 'ICommand')
System.Windows.Data Error: 40 : BindingExpression path error: 'DataQuerySortEdit' property not found on 'object' ''DataQuerySort' (HashCode=32836053)'. BindingExpression:Path=DataQuerySortEdit; DataItem='DataQuerySort' (HashCode=32836053); target element is 'MouseBinding' (HashCode=66172851); target property is 'Command' (type 'ICommand')
System.Windows.Data Error: 40 : BindingExpression path error: 'DataQuerySortEdit' property not found on 'object' ''ListBox' (Name='SortListBox')'. BindingExpression:Path=DataQuerySortEdit; DataItem='ListBox' (Name='SortListBox'); target element is 'MouseBinding' (HashCode=28263486); target property is 'Command' (type 'ICommand')
System.Windows.Data Error: 40 : BindingExpression path error: 'DataQuerySortEdit' property not found on 'object' ''ListBox' (Name='SortListBox')'. BindingExpression:Path=DataQuerySortEdit; DataItem='ListBox' (Name='SortListBox'); target element is 'MouseBinding' (HashCode=27134857); target property is 'Command' (type 'ICommand')
System.Windows.Data Error: 40 : BindingExpression path error: 'DataQuerySortEdit' property not found on 'object' ''ListBox' (Name='SortListBox')'. BindingExpression:Path=DataQuerySortEdit; DataItem='ListBox' (Name='SortListBox'); target element is 'MouseBinding' (HashCode=7437765); target property is 'Command' (type 'ICommand')
System.Windows.Data Error: 40 : BindingExpression path error: 'DataQuerySortEdit' property not found on 'object' ''ListBox' (Name='SortListBox')'. BindingExpression:Path=DataQuerySortEdit; DataItem='ListBox' (Name='SortListBox'); target element is 'MouseBinding' (HashCode=58400697); target property is 'Command' (type 'ICommand')
所以第二次尝试绑定(我猜测Border InputBinding中的那个)确实很好的正确列表框但仍然无法找到ICommand。我试着做一个相对找到窗口,包含列表等的Gird,但仍然无法将其连接到网络。我也试过J提到将相对搜索直接放在MouseBindings中,它们会导致相同的错误。
EDIT2:以下是ViewModel中的命令和属性,使用MVVMLight
public DataQuerySort DataQuerySortSelected
{
get { return _DataQuerySortSelected; }
set { NotifySetProperty(ref _DataQuerySortSelected, value, () => DataQuerySortSelected); }
}
private DataQuerySort _DataQuerySortSelected;
public RelayCommand DataQuerySortEdit
{
get { return new RelayCommand(_DataQuerySortEdit, CanDataQuerySortEdit); }
}
private void _DataQuerySortEdit()
{
DataQuerySortHolder = DataQuerySortSelected.Copy();
DataQuerySortEditMode = EditMode.Edit;
}
private bool CanDataQuerySortEdit()
{
return DataQuerySortSelected != null;
}
取出CanDataQuerySortEdit没有区别。我知道一切正常,因为如果我创建一个按钮并指向它就可以了。如果我也在ListBox中为鼠标创建一个输入绑定,就像Delete键一样有效 - 只要我点击ListBoxItems之外。
EDIT3:这是View本身的一部分,包括class,datacontext和resources。我已经尝试将相对绑定设置为“{x:Type Window}”和“{x:Type l:ToolkitWindowBase}”。 ToolkitWindowBase直接扩展Window。 frmDataBrowserViewModel扩展了一个名为ToolkitViewModelBase的类,它从MVVMLight扩展了ViewModelBase:
<l:ToolkitWindowBase x:Class="GISToolkit.frmDataBrowser" x:Name="mainWindow" Icon="Images/favicon.ico"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d"
xmlns:l="clr-namespace:GISToolkit;assembly="
xmlns:lc="clr-namespace:GISToolkit.Controls;assembly="
xmlns:ls="clr-namespace:GISToolkit.Settings;assembly="
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:xctk="clr-namespace:Xceed.Wpf.Toolkit;assembly=Xceed.Wpf.Toolkit"
xmlns:xctkp="clr-namespace:Xceed.Wpf.Toolkit.Primitives;assembly=Xceed.Wpf.Toolkit"
Title="Solutions GIS Toolkit - Setup"
ResizeMode="CanResizeWithGrip" Foreground="White"
l:ControlBox.HasMaximizeButton="False" l:ControlBox.HasMinimizeButton="False" l:ControlBox.HasCloseButton="False"
Height="{Binding RunTimeHeight, Mode=TwoWay}"
Width="{Binding RunTimeWidth, Mode=TwoWay}" IsSettingsDirty="{Binding IsCurrentSettingsDirty}" IsEnabled="True">
<Window.DataContext>
<l:frmDataBrowserViewModel />
</Window.DataContext>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/DataBrowser.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
..................
<l:ToolkitWindowBase />
EDIT4:以防有人在那里仍然列出,帮我一个忙,创建一个名为“WpfMvvmApplication1”的新WPF项目,其中一个名为“BindingTestWindow”的窗口和一个名为“MainWindowViewModel”的视图模型然后用于放置窗口(应该是简单的剪切/粘贴,除非你为文件/项目使用不同的名称):
<Window x:Class="WpfMvvmApplication1.BindingTestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfMvvmApplication1"
Title="BindingTestWindow" Height="300" Width="300">
<Window.DataContext>
<l:BindingTestViewModel />
</Window.DataContext>
<Grid>
<GroupBox Header="Sorting:" >
<Grid>
<ListBox Background="White" Name="SortListBox" ItemsSource="{Binding TestCollection}">
<ListBox.InputBindings>
<KeyBinding Key="Delete" Command="{Binding TestCommand}" />
</ListBox.InputBindings>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="AllowDrop" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="MainBorder" Padding="0" Margin="0">
<ContentPresenter />
<Border.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding TestCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
</Border.InputBindings>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="MainBorder" Value="Yellow" Property="Background" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>
</Grid>
</GroupBox>
</Grid>
</Window>
和VIEWMODEL:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Windows.Input;
namespace WpfMvvmApplication1
{
public class BindingTestViewModel : NotificationObject
{
public BindingTestViewModel()
{
TestCollection = new ObservableCollection<string>();
for (int i = 0; i < 10; i++ )
TestCollection.Add("test" + i);
}
public ICommand TestCommand { get { return new DelegateCommand(_TestCommand); } }
private void _TestCommand() { System.Diagnostics.Debugger.Break(); }
public ObservableCollection<string> TestCollection
{
get { return _TestCollection; }
set
{
_TestCollection = value;
RaisePropertyChanged(() => TestCollection);
}
}
private ObservableCollection<string> _TestCollection;
}
public class DelegateCommand : ICommand
{
private readonly Action _command;
private readonly Func<bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public DelegateCommand(Action command, Func<bool> canExecute = null)
{
if (command == null)
throw new ArgumentNullException();
_canExecute = canExecute;
_command = command;
}
public void Execute(object parameter)
{
_command();
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute();
}
}
public class NotificationObject : INotifyPropertyChanged
{
protected void RaisePropertyChanged<T>(Expression<Func<T>> action)
{
var propertyName = GetPropertyName(action);
RaisePropertyChanged(propertyName);
}
private static string GetPropertyName<T>(Expression<Func<T>> action)
{
var expression = (MemberExpression)action.Body;
var propertyName = expression.Member.Name;
return propertyName;
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
这里面没有别的东西。当输入绑定在listboxtiem中时它给出了绑定错误,但在列表框本身时却没有。似乎它应该工作,因为输出说它确实在FindAncestor中找到了窗口。
答案 0 :(得分:1)
尝试将命令绑定更改为以下代码。列表框项是列表框的可视子项,您需要正确连接您的datacontext。我认为。请注意窗口类型。将其更改为声明命令的位置和列表框存在的顶级datacontext。即:用户控制,窗口等......
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding DataQuerySortEdit, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}}" />
您可以在运行应用程序时调试此输出窗口,它会显示这样的绑定错误。
好吧,我完全忽略了你起诉Mvvm Light的事实。 MVVM-Light内置了一个函数,用于将事件绑定到视图模型上的命令。
将以下内容添加到Windows命名空间:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="http://www.galasoft.ch/mvvmlight"
现在更改列表框项目中的边框以包含以下内容:
<Border ...>
<i:Interaction.Triggers>
<i:EventTrigger EventName="LeftDoubleClick">
<cmd:EventToCommand Command="{Binding DataContext.DataQuerySortEdit, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Border>
Mvvm light已经建立了这个,以便准确地提供您正在寻找的东西。它允许您接受任何事件并将命令绑定到它。我通过控件等的lostfocus事件将其用于表单验证。
确保您的项目中包含以下参考:
希望这有帮助
这是最后的努力,如果你使用以下内容怎么办:
它将Datacontext添加到命令名称。
答案 1 :(得分:0)
非常感谢J King挂在那里。但似乎在XAML中做的不起作用。我最终在VIEW的代码中做了这个(希望它可以帮助某人):
public BindingTestWindow()
{
InitializeComponent();
SortListBox.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
}
private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
if (SortListBox.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
BindingTestViewModel vm = (BindingTestViewModel)this.DataContext;
for(int i = 0; i < SortListBox.Items.Count; i++)
{
ListBoxItem lbi = (ListBoxItem)SortListBox.ItemContainerGenerator.ContainerFromIndex(i);
lbi.InputBindings.Clear();
lbi.InputBindings.Add(new InputBinding(vm.TestCommand, new MouseGesture(MouseAction.LeftDoubleClick)));
}
}
}
为了操纵实际的listboxitems(而不是通过ListBox.Items操作它们的内容),我们必须使用ItemContainerGenerator。但是为了做到这一点,你必须等待它完全生成,因此需要事件处理程序。它不漂亮,但它的工作原理。
欧尼
答案 2 :(得分:0)
我花了很多时间摆弄这个问题。设法将一些答案与可行的结果混合在一起。 ListBox上的Inputbindings具有奇怪的行为。在ListBox本身上的某些工作,例如MiddleClick,需要在项目上实施其他工作。 J Kings绑定到DataContext在LisBoxItemStyle的ContentPresenter中起到了作用。
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<ContentPresenter.InputBindings>
<MouseBinding Gesture="Ctrl+MiddleClick"
Command="{Binding DataContext.MiddleClickCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
<MouseBinding Gesture="Ctrl+RightClick"
Command="{Binding DataContext.CtrlRightClickCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
<MouseBinding MouseAction="RightClick"
Command="{Binding DataContext.RightClickCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
</ContentPresenter.InputBindings>
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.MouseOver.Border}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="False"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Border}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="True"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>