在我的WPF应用程序中,有一个包含项目的列表框。列表框通过XAML中的xmldataprovider填充,然后将其绑定到列表框的Itemssource属性。
好吧,从XAML,我通过执行以下命令将命令绑定到列表框:
<ListBox.CommandBindings>
<CommandBinding
Command="{x:Static local:mainApp.MyCmd}"
CanExecute="CanExecute"
Executed ="Executed" />
</ListBox.CommandBindings>
但我不知道如何以编程方式将命令绑定到每个listboxitem。怎么做?
提前致谢。
首先抱歉不发表评论。我不能把所有这些都放在评论中。
好的,是的,我没有使用ICommandSource的Executed和CanExecute属性,尽管我已经在自定义类中注册并实现了它们(在xaml中它们也被注释)。我已经在routedCommand中指定了它们但没有在自定义类中指定它们,我通过这样做在窗口的构造函数中完成了它:
WinMain代码背后:
public WinMain()
{
InitializeComponent();
// Command binding. If I don't do this Executed and CanExecute are not executed
CommandBindings.Add(new CommandBinding(rcmd,
CommandBinding_Executed, CommandBinding_CanExecute));
}
然后我也在WinMain代码中实现这些方法:
// ExecutedRoutedEventHandler
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
// Do stuff
}
// CanExecuteRoutedEventHandler
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
// cBgWorkers is a class that check if a background worker is running
e.CanExecute = !cBgWorkers.isRunning;
//if (LayoutListBox != null) LayoutListBox.IsEnabled = !cBgWorkers.isRunning;
}
并在WinMain XAML中我调用这样的命令:
<Classes:CommandListBox x:Name="LayoutListBox"
Command="{x:Static local:WinMain.rcmd}"
... >
<...>
</Classes:CommandListBox>
在我的自定义类CommandListBox中,我有一个CanExecuteChanged,您可以在其中看到我启用或禁用了控件,具体取决于后台工作程序是否已完成:
private void CanExecuteChanged(object sender, EventArgs e)
{
this.Enabled = !cBgWorkers.isRunning;
}
但是在自定义类中我没有实现你说的OnSelected。
没有实现它一切正常,自定义控件调用命令和CanExecute方法,并且CanExecute获取正确的值,true或false取决于后台worker是否完成,并且CanExecute时引发自定义控件中的CanExecuteChanged改变它的价值。当后台工作程序启动时,它会被禁用,但是当它完成时它不会启用。我已经调试了,当后台工作程序完成时我可以看到CanExecuteChanged被执行并且this.Enabled正在获得正确的值(true)但由于某种原因在UI中控件继续被禁用,尽管它获得了正确的值,尽管在RunWOrkerCompleted中(在后台) worker)我强制用CommandManager.InvalidateRequerySuggested()更新UI。
我通过取消注释行来解决这个问题:
if(LayoutListBox!= null)LayoutListBox.IsEnabled =!cBgWorkers.isRunning;
CanExecute方法中的。我不明白会发生什么。
然后,如果我按照你说的做,没有必要这样做:
CommandBindings.Add(new CommandBinding(rcmd,
CommandBinding_Executed, CommandBinding_CanExecute));
和CommandBinding_Executed&amp; CommandBinding_CanExecute实现。我是对的吗?
但如果我删除这些方法,我可以在哪里设置this.enabled =!cBgWorkers.isRunning?
我希望WPF为我的自定义控件自动设置isEnabled属性。怎么做?
提前致谢。
我正在应用您所说的关于附加行为的文章,并进行一些更改以使其适应我的ListBox。它不能很好地工作,或者我做错了。我想要的是避免在长任务(后台工作程序)运行时可以选择ListBox成员(listBoxItems)。所以我修改过的文章的一个方法是:
static void OnListBoxItemSelected(object sender, RoutedEventArgs e)
{
// Only react to the Selected event raised by the ListBoxItem
// whose IsSelected property was modified. Ignore all ancestors
// who are merely reporting that a descendant's Selected fired.
if (!Object.ReferenceEquals(sender, e.OriginalSource))
return;
ListBoxItem item = e.OriginalSource as ListBoxItem;
if (item != null)
{
// (*) See comment under
item.IsEnabled = !cBgWorkers.isRunning;
if (!cBgWorkers.isRunning)
{
item.BringIntoView();
}
}
}
(*)cBgWorkers是一个公共静态类,它有一些方法和属性。 其中一个属性是isRunning,表示当前没有后台工作程序正在运行。然后,如果没有后台工作程序正在运行,则必须启用列表框成员,否则必须禁用它们,因此当用户单击一个列表框项目时,当前页面不会更改为另一个,因为我之前禁用了它(每个listBox项目附加了一个我主应用程序中的页面)。
当其中一个后台工作人员(bw)或全部正在运行并且我选择列表框项时,一切正常:列表框项被禁用,因为有bw正在运行并且它避免将当前页面更改为另一个页面。当然,如果我禁用了列表框项目(或列表框项目),我无法再次选择它,因为它已被禁用,这是我的问题,因为我想要当bw完成在bw运行时已禁用的列表框项目时,他们再次启用。不幸的是,附加行为,因为我看到它不是由WPF自动完成的,并且命令具有此优势(控件由WPF自动更新)。那么,如何在bw分别运行时禁用/重新启用列表框项目?
据我所知,附加行为的一个优点是我认为它更有效率,因为它们不会不断地调用动作(只有当动作,例如,选择产生时)。命令经常(不经常)检查是否可以执行绑定到控件的操作(因此,如果它们可以执行,WPF会自动启用控件,否则它们会被禁用),对吗?
感谢。
答案 0 :(得分:1)
您可以尝试创建一个从ListBoxItem派生并实现ICommandSource接口的自定义控件。到目前为止,我想不出更简单的解决方案。
答案 1 :(得分:1)
我已经完成了你的解决方案。我已经完成了一个自定义用户控件,从listbox派生出来并按照你的说法实现了ISourceCommand,它现在可以正常运行!!!! ;)
我的自定义课程:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Input;
namespace GParts.Classes
{
public class CommandListBox : ListBox, ICommandSource
{
public CommandListBox() : base()
{
}
// ICommand Interface Members
// Make Command a dependency property so it can use databinding.
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register(
"Command",
typeof(ICommand),
typeof(CommandListBox),
new PropertyMetadata((ICommand)null,
new PropertyChangedCallback(CommandChanged)));
public ICommand Command
{
get
{
return (ICommand)GetValue(CommandProperty);
}
set
{
SetValue(CommandProperty, value);
}
}
// Make Command a dependency property so it can use databinding.
public static readonly DependencyProperty ExecutedProperty =
DependencyProperty.Register(
"Executed",
typeof(object),
typeof(CommandListBox),
new PropertyMetadata((object)null));
public object Executed
{
get
{
return (object)GetValue(ExecutedProperty);
}
set
{
SetValue(ExecutedProperty, value);
}
}
// Make Command a dependency property so it can use databinding.
public static readonly DependencyProperty CanExecuteProperty =
DependencyProperty.Register(
"CanExecute",
typeof(object),
typeof(CommandListBox),
new PropertyMetadata((object)null));
public object CanExecute
{
get
{
return (object)GetValue(CanExecuteProperty);
}
set
{
SetValue(CanExecuteProperty, value);
}
}
// Make CommandTarget a dependency property so it can use databinding.
public static readonly DependencyProperty CommandTargetProperty =
DependencyProperty.Register(
"CommandTarget",
typeof(IInputElement),
typeof(CommandListBox),
new PropertyMetadata((IInputElement)null));
public IInputElement CommandTarget
{
get
{
return (IInputElement)GetValue(CommandTargetProperty);
}
set
{
SetValue(CommandTargetProperty, value);
}
}
// Make CommandParameter a dependency property so it can use databinding.
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register(
"CommandParameter",
typeof(object),
typeof(CommandListBox),
new PropertyMetadata((object)null));
public object CommandParameter
{
get
{
return (object)GetValue(CommandParameterProperty);
}
set
{
SetValue(CommandParameterProperty, value);
}
}
// Command dependency property change callback.
private static void CommandChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
CommandListBox clb = (CommandListBox)d;
clb.HookUpCommand((ICommand)e.OldValue,(ICommand)e.NewValue);
}
// Add a new command to the Command Property.
private void HookUpCommand(ICommand oldCommand, ICommand newCommand)
{
// If oldCommand is not null, then we need to remove the handlers.
if (oldCommand != null)
{
RemoveCommand(oldCommand, newCommand);
}
AddCommand(oldCommand, newCommand);
}
// Remove an old command from the Command Property.
private void RemoveCommand(ICommand oldCommand, ICommand newCommand)
{
EventHandler handler = CanExecuteChanged;
oldCommand.CanExecuteChanged -= handler;
//newCommand.Execute(null);
//newCommand.CanExecute(null);
}
// Add the command.
private void AddCommand(ICommand oldCommand, ICommand newCommand)
{
EventHandler handler = new EventHandler(CanExecuteChanged);
canExecuteChangedHandler = handler;
if (newCommand != null)
{
newCommand.CanExecuteChanged += canExecuteChangedHandler;
//newCommand.Execute(Executed);
//newCommand.CanExecute(CanExecute);
}
}
private void CanExecuteChanged(object sender, EventArgs e)
{
if (this.Command != null)
{
RoutedCommand command = this.Command as RoutedCommand;
// If a RoutedCommand.
if (command != null)
{
if (command.CanExecute(CommandParameter, CommandTarget))
{
this.IsEnabled = true;
}
else
{
this.IsEnabled = false;
}
}
// If a not RoutedCommand.
else
{
if (Command.CanExecute(CommandParameter))
{
this.IsEnabled = true;
}
else
{
this.IsEnabled = false;
}
}
}
}
// Keep a copy of the handler so it doesn't get garbage collected.
private static EventHandler canExecuteChangedHandler;
}
}
和我的WinMain.xaml:
<Classes:CommandListBox x:Name="LayoutListBox"
Command="{x:Static local:WinMain.rcmd}"
<!-- These lines doesn't work I explain it following
Executed="CommandBinding_Executed"
CanExecute="CommandBinding_CanExecute"
-->
... >
<...>
</Classes:CommandListBox>
和后面的窗口代码:
public WinMain()
{
InitializeComponent();
// Command binding. If I don't do this Executed and CanExecute are not executed
CommandBindings.Add(new CommandBinding(rcmd,
CommandBinding_Executed, CommandBinding_CanExecute));
}
public static RoutedCommand rcmd = new RoutedCommand();
// ExecutedRoutedEventHandler
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
// Do stuff
}
// CanExecuteRoutedEventHandler
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = !cBgWorkers.isRunning;
//if (LayoutListBox != null) LayoutListBox.IsEnabled = !cBgWorkers.isRunning;
}
但我和另一个解决方案有同样的问题。如果我没有放置最后一行(这里显示在CommandBinding_CanExecute中注释),当后台工作程序完成时,列表框不会由wpf自动启用。如果我把它放在这条线上就行了。会发生什么事?
另一件事,正如你在我的代码片段中看到的那样,我想做的就像我用一个按钮一样,你可以指示命令,执行和canexecute。我已经在课堂上注册了它们,在列表框中我检查了传递方法,但它没有用。我怎么能这样做?
非常感谢。
答案 2 :(得分:0)
答案 3 :(得分:0)
根据我发布的第一个问题,在列表框中使用CommandBindings它不起作用。 CanExecute的实现是:
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = !cBgWorkers.isRunning;
}
通过执行WPF不会自动启用/禁用列表框控件,具体取决于后台工作程序状态(运行与否),我不明白为什么,因为我有其他控件,如带有命令绑定的按钮和WPF自动启用/禁用它们。
所以我做了以下修改:
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = !cBgWorkers.isRunning;
if (LayoutListBox != null) LayoutListBox.IsEnabled = !cBgWorkers.isRunning;
}
现在,它有效。当没有后台工作程序正在运行和禁用时启用列表框,否则我不喜欢的是放入方法的最后一行,我手动启用/禁用列表框的属性isEnabled。这是低效的,所以我只想在CanExecute更改其值时更改列表框的isEnabled属性。据我所知有一个事件要做,这是CanExecuteChanged但我不知道如何实现它。有什么想法吗?
现在,在尝试了几个解决方案后,我正在实施迈克的解决方案,因为我认为它更容易,更清晰,并且只需进行一些更改就可以重新用于其他控件。
答案 4 :(得分:0)
我没能完成整个帖子。这很长。无论如何,我以为你想在ListBoxItem上放一个命令?从我看到的,你继承自ListBox。您无需指定ICommandSource的Executed和CanExecute属性。这应该在RoutedCommand中指定,而不是在自定义控件中指定。要执行命令,需要在自定义控件中提供事件处理程序。例如,如果选择了某个项目,则执行该命令。这是一个例子。
protected override void OnSelected(RoutedEventArgs e)
{
base.OnSelected(e);
if (this.Command != null)
{
RoutedCommand command = Command as RoutedCommand;
if (command != null)
{
command.Execute(CommandParameter, CommandTarget);
}
else
{
((ICommand)Command).Execute(CommandParameter);
}
}
}