我有一个 WPF 应用程序(我之前写过的同一个应用程序),我正在使用塞缪尔杰克页面http://blog.functionalfun.net/2008/09/hooking-up-commands-to-中显示的事件处理程序wpf.html 中的事件。我正在尝试向 UI 中的 TextBox 添加一个事件处理程序,以执行按下按钮时发生的相同操作。但是,它不会编译,编译错误是这样的:
错误 2 不能在“文本框”集合中使用“绑定”。只能在 DependencyObject 的 DependencyProperty 上设置“绑定”。
这是 XAML(注意,我希望在 TextBox 处于焦点时按 Enter 与单击按钮具有相同的操作时发生相同的行为:
这是视图模型代码:
/// <summary>
/// The ViewModel in the MVVM pattern
/// </summary>
public class ApplicationViewModel : INotifyPropertyChanged
{
#region Private members
private string searchString;
private string emailString;
private string toolName;
private NexusApp selectedApp;
private ICommand searchButtonCmd;
private ICommand clearButtonCmd;
private ICommand emailButtonCmd;
private ICommand textboxKeyCmd;
#endregion
#region INotifyPropertyChanged implementation
/// <summary>
/// Notifies the view that the observablecollection bound to the listview has changed
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region Properties
/// <summary>
/// Collection bound to listview in ui view
/// </summary>
public ObservableCollection<NexusApp> AppCollection { get; set; }
/// <summary>
/// The application record selected by user
/// </summary>
public NexusApp SelectedApp
{
get
{
return this.selectedApp;
}
set
{
this.selectedApp = value;
this.emailString = this.selectedApp.Email;
this.toolName = this.selectedApp.Name;
}
}
/// <summary>
/// String entered by user into search box
/// </summary>
public string AppToSearch
{
get
{
return searchString;
}
set
{
searchString = value;
}
}
/// <summary>
/// Email address of team that owns app user is trying to request
/// </summary>
public string AppToRequest
{
get
{
return emailString;
}
set
{
emailString = value;
}
}
#endregion
/// <summary>
/// Default constructor, initialises and populates collection
/// </summary>
public ApplicationViewModel()
{
this.AppCollection = ApplicationsModel.Current;
}
#region Button command handlers
/// <summary>
/// Handles search button
/// </summary>
public ICommand SearchButtonPressed
{
get
{
if (this.searchButtonCmd == null)
{
this.searchButtonCmd = new RelayCommand(SearchButtonPressedExecute, c => CanSearch);
}
return this.searchButtonCmd;
}
}
/// <summary>
/// Handles clear button
/// </summary>
public ICommand ClearButtonPressed
{
get
{
if (this.clearButtonCmd == null)
{
this.clearButtonCmd = new RelayCommand(ClearButtonPressedExecute, c => CanClear);
}
return this.clearButtonCmd;
}
}
/// <summary>
/// Handles Request Application button
/// </summary>
public ICommand EmailButtonPressed
{
get
{
if (this.emailButtonCmd == null)
{
this.emailButtonCmd = new RelayCommand(EmailButtonPressedExecute, c => CanEmail);
}
return this.emailButtonCmd;
}
}
public ICommand TextBoxKeyPressed
{
get
{
if (this.textboxKeyCmd == null)
{
this.textboxKeyCmd = new RelayCommand(TextBoxKeyPressedExecute, c => CanSearch);
}
return this.textboxKeyCmd;
}
}
private void SearchButtonPressedExecute(object parameter)
{
try
{
ApplicationsModel.Current.Search(this.searchString);
}
catch (Exception e)
{
string error = String.Format("Could not refresh repository list: {0}", e.Message);
MessageBox.Show(error, "Error Performing Search", MessageBoxButton.OK, MessageBoxImage.Error);
}
NotifyPropertyChanged();
}
private void ClearButtonPressedExecute(object parameter)
{
try
{
this.searchString = String.Empty;
this.AppToSearch = String.Empty;
this.AppToSearch = String.Empty;
ApplicationsModel.Current.ClearSearch();
NotifyPropertyChanged();
}
catch (Exception e)
{
string error = String.Format("Could not refresh repository list: {0}", e.Message);
MessageBox.Show(error, "Error Clearing Search", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void EmailButtonPressedExecute(object parameter)
{
if (String.IsNullOrEmpty(this.toolName))
{
MessageBox.Show("Please select one of the applications to request", "Please select application", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
try
{
string message;
if (EmailHelper.IsValidEmail(this.emailString))
{
message = String.Format("Request for application {0} successfully sent", this.toolName);
}
else
{
message = String.Format("Could not send request for access\nThe email address is not defined for tool {0}.\nThe Equity Nexus team has been alerted, asking them to investigate", this.toolName);
}
ApplicationsModel.Current.Email(this.emailString, this.toolName);
MessageBox.Show(message, "Request Sent", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception e)
{
string error = String.Format("Could not send application request email, error: {0}", e.Message);
MessageBox.Show(error, "Erorr sending request", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void TextBoxKeyPressedExecute(object parameter)
{
MessageBox.Show("Hit textbox event execute");
}
/// <summary>
/// Ability to do search, always true
/// </summary>
public bool CanSearch
{ get { return true; } }
/// <summary>
/// Ability to clear search, always true
/// </summary>
public bool CanClear
{ get { return true; } }
/// <summary>
/// Ability to send email, always true
/// </summary>
public bool CanEmail
{ get { return true; } }
#endregion
}
这是 RelayCommand 代码:
/// <summary>
/// Hooks up commands to the actions in Button Behaviour class
/// </summary>
public class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
这是 ButtonBehaviour 代码:
public static class ButtonBehaviour
{
public static readonly DependencyProperty SearchCommand;
public static readonly DependencyProperty ClearCommand;
public static readonly DependencyProperty EmailCommand;
public static readonly DependencyProperty TextBoxKeyCommand;
static ButtonBehaviour()
{
SearchCommand = EventBehaviourFactory.CreateCommandExecutionEventBehaviour(Button.ClickEvent, "SearchCommand", typeof(ButtonBehaviour));
ClearCommand = EventBehaviourFactory.CreateCommandExecutionEventBehaviour(Button.ClickEvent, "ClearCommand", typeof(ButtonBehaviour));
EmailCommand = EventBehaviourFactory.CreateCommandExecutionEventBehaviour(Button.ClickEvent, "EmailCommand", typeof(ButtonBehaviour));
TextBoxKeyCommand = EventBehaviourFactory.CreateCommandExecutionEventBehaviour(TextBox.KeyDownEvent, "EnterKeyCommand", typeof(ButtonBehaviour));
}
public static void SetSearchCommand(DependencyObject depobj, ICommand value)
{
depobj.SetValue(SearchCommand, value);
}
public static ICommand GetSearchCommand(DependencyObject depobj)
{
return depobj.GetValue(SearchCommand) as ICommand;
}
public static void SetClearCommand(DependencyObject depobj, ICommand value)
{
depobj.SetValue(ClearCommand, value);
}
public static ICommand GetClearCommand(DependencyObject depobj)
{
return depobj.GetValue(ClearCommand) as ICommand;
}
public static void SetEmailCommand(DependencyObject depobj, ICommand value)
{
depobj.SetValue(EmailCommand, value);
}
public static ICommand GetEmailCommand(DependencyObject depobj)
{
return depobj.GetValue(EmailCommand) as ICommand;
}
public static void SetTextBoxKeyCommand(DependencyObject depobj, ICommand value)
{
depobj.SetValue(TextBoxKeyCommand, value);
}
public static ICommand GetTextBoxKeyCommand(DependencyObject depobj)
{
return depobj.GetValue(TextBoxKeyCommand) as ICommand;
}
}
XAML 代码:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Height="84" HorizontalAlignment="Left" Margin="0,5,5,5" Name="imgNexusLogo" Stretch="Fill" VerticalAlignment="Top" Width="600" Source="nexus1bannerlong.png" />
<Grid Grid.Row="1" HorizontalAlignment="Center" Margin="0,5,5,5" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Search for Application">
<Label.Foreground>
<SolidColorBrush Color="LightCyan" />
</Label.Foreground>
</Label>
<TextBox Grid.Row="0" Grid.Column="1" Margin="3" Width="500" Text="{Binding AppToSearch}" vm:ButtonBehaviour.TextBoxKeyCommand="{Binding TextBoxKeyPressed}" />
<Button Grid.Row="0" Grid.Column="2" HorizontalAlignment="Right" Width="100" Height="20" Margin="3" Background="LightCyan" Content="Search" ToolTip="Click to filter application list by search value" vm:ButtonBehaviour.SearchCommand="{Binding SearchButtonPressed}" />
<Button Grid.Row="0" Grid.Column="3" HorizontalAlignment="Right" Width="100" Height="20" Margin="3" Background="LightCyan" Content="Clear Search" ToolTip="Click to restore application list to full listing" vm:ButtonBehaviour.ClearCommand="{Binding ClearButtonPressed}"/>
</Grid>
<ListView Grid.Row="2" BorderBrush="Black" HorizontalAlignment="Stretch" ItemsSource="{Binding Path=AppCollection}" SelectedItem="{Binding SelectedApp}">
<ListView.View>
<GridView>
<GridViewColumn Header="Application Name" Width="200" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Application Description" Width="631" DisplayMemberBinding="{Binding Description}"/>
<GridViewColumn Header="Application Owner" Width="150" DisplayMemberBinding="{Binding Owner}"/>
</GridView>
</ListView.View>
</ListView>
<Button Grid.Row="3" HorizontalAlignment="Center" Width="200" Height="30" Margin="3" Background="LightCyan" Content="Request Application" ToolTip="Click here to contact app owners" vm:ButtonBehaviour.EmailCommand="{Binding EmailButtonPressed}" />
</Grid>
事件行为工厂代码可以在http://blog.functionalfun.net/2008/09/hooking-up-commands-to-events-in-wpf.html找到,我没有对其进行任何更改。
有人知道我在做什么错吗?这可能很简单,但由于我是 WPF 和 MVVM 的新手,我需要了解它。
谢谢!
跟进
我只是在后面做了一个代码并处理了这样的事件。避免任何代码落后是不值得的,而且这个项目的截止日期比 MVVM 思想纯度更高。对于任何感兴趣的读者,这是我所做的:
C#代码:
private void TextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
ApplicationViewModel viewModel = DataContext as ApplicationViewModel;
if (viewModel != null)
{
viewModel.AppToSearch = textboxSearch.Text;
viewModel.SearchButtonPressedExecute(textboxSearch.Text);
}
}
}
XAML:
<TextBox Name="textboxSearch" Grid.Row="0" Grid.Column="1" Margin="3" Width="500" Text="{Binding AppToSearch}" KeyDown="TextBox_KeyDown"/>