|
Tony,
I've started integrating Simple MVVM (With WCF RIA) into a new project. In addition to using RIA this project also has the need to connect directly to a WCF service. In the past I have invoke queries from this service using a standard delegate command and
binding to this using command binding from the view. I am currently trying this using the toolkit but I have experiencing a few errors. Hopefully you or someone can point me in the right direction.
From the view (Search.xaml) I have a user control (searchcontrol.xaml). The user control has a text field used for searching and then a search button
<TextBox x:Name="txtSearchField"
Grid.Column="0"
Style="{StaticResource SearchTxtBoxStyle}"
Text="{Binding SearchTerm, Mode=TwoWay}"
ToolTipService.ToolTip="{StaticResource TTsearchField}">
<i:Interaction.Triggers>
<ei:KeyTrigger Key="Enter" FiredOn="KeyUp">
<i:InvokeCommandAction Command="{Binding GetSearchResultCommand, Mode=OneWay}" />
</ei:KeyTrigger>
</i:Interaction.Triggers>
</TextBox>
<StackPanel x:Name="searchButtons"
Grid.Row="0"
Grid.Column="1"
Margin="3,2,5,2"
Orientation="Horizontal">
<Button x:Name="SearchButton"
Margin="13,1,9,-1"
ap:AttachedProperties.TabIndex="2"
Content="{StaticResource btnSearch}"
Style="{StaticResource blackButton}"
ToolTipService.ToolTip="{StaticResource TTSavebtn}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction
TargetObject="{Binding}"
MethodName="MyFoo"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
I've set the data context within the view which is being picked up by the control so all is good there.
Where I am having issues is in the ability to properly call the commands from the view model using the SimpleMVVM toolkit delegate command or by the CallMethodAction.
my View model looks as:
public class SearchViewModel : ViewModelBase<SearchViewModel>
{
#region Delegates
private SearchModel _DataModel = new SearchModel();
private string _SelectedItemID;
#endregion
#region Initialization and Cleanup
//// TODO: Add a member for IXxxServiceAgent
//private /* IXxxServiceAgent */ serviceAgent;
// Default ctor
public SearchViewModel() { }
//// TODO: ctor that accepts IXxxServiceAgent
//public SearchViewModel(/* IXxxServiceAgent */ serviceAgent)
//{
// this.serviceAgent = serviceAgent;
//}
#endregion
#region Commands
public ICommand GetSearchResultCommand
{
get
{
return new DelegateCommand<string>(GetSearchResultCommandExecute);
}
}
public ICommand ClearHistoryCommand
{
get
{
return new DelegateCommand<string>(ClearHistoryCommandExecute);
}
}
public ICommand SaveSearchCommand
{
get
{
return new DelegateCommand<string>(SaveSearchCommandExecute);
}
}
#endregion
#region Notifications
// TODO: Add events to notify the view or obtain data from the view
public event EventHandler<NotificationEventArgs<Exception>> ErrorNotice;
#endregion
#region Properties
#region Search Term
private string _SearchTerm = string.Empty;
public string SearchTerm
{
get { return this._SearchTerm; }
set
{
this._SearchTerm = value;
NotifyPropertyChanged(vm => vm.SearchTerm);
}
}
#endregion
#region Search Results
private ObservableCollection<QueryResponse> _SearchResults = new ObservableCollection<QueryResponse>();
public ObservableCollection<QueryResponse> SearchResults
{
get { return this._SearchResults; }
set
{
this._SearchResults = value;
NotifyPropertyChanged(vm => vm.SearchResults);
}
}
#endregion
#region Search History
private ObservableCollection<SearchHistoryModel> _SearchHistory = new ObservableCollection<SearchHistoryModel>();
public ObservableCollection<SearchHistoryModel> SearchHistory
{
get { return this._SearchHistory; }
set
{
this._SearchHistory = value;
NotifyPropertyChanged(vm => vm.SearchHistory);
}
}
#endregion
#region Saved Search Result
private ObservableCollection<SavedSearchModel> _SavedSearchItems = new ObservableCollection<SavedSearchModel>();
public ObservableCollection<SavedSearchModel> SavedSearchItems
{
get { return this._SavedSearchItems; }
set
{
this._SavedSearchItems = value;
NotifyPropertyChanged(vm => vm.SavedSearchItems);
}
}
#endregion
#region Time of Saving Search Result
/// <summary>
/// to do this result may be handled someplace else. For now it is stubbed in to illustrate how a time stamp is added
/// to a search result when it is saved and displayed within the UI
/// </summary>
private string _TimeOfSave;
public string TimeOfSave
{
get { return _TimeOfSave; }
set
{
_TimeOfSave = value;
NotifyPropertyChanged(vm => vm.TimeOfSave);
}
}
#endregion
#endregion
#region Methods
public void MyFoo(object parameter)
{
//query
this.SearchResults = this._DataModel.GetSearchResults(this.SearchTerm);
//search history items
this.SearchHistory = this._DataModel.AddSearchHistoryItem(this.SearchTerm);
}
private void GetSearchResultCommandExecute(object parameter)
{
//query
this.SearchResults = this._DataModel.GetSearchResults(this.SearchTerm);
//search history items
this.SearchHistory = this._DataModel.AddSearchHistoryItem(this.SearchTerm);
}
private void ClearHistoryCommandExecute(object parameter)
{
this.ClearSearchHistoryItems();
}
private void SaveSearchItemCommand(string item)
{
//search is saved to a temporary collection
//currently persists only for session
//TODO: update data model to write to database vs. collection
this._DataModel.SaveSearchItem(item);
}
private void SaveSearchCommandExecute(object parameter)
{
// this.SaveSearchItemCommand(this.SearchTerm);
this.SavedSearchItems = this._DataModel.SaveSearchItem(this.SearchTerm);
}
private void ClearSearchHistoryItems()
{
//search history is maintained within the data model
// currently persists only for the session
this._DataModel.ClearSearchHistoryItems();
this.NotifyPropertyChanged(m => m.SearchHistory);
}
#endregion
#region Completion Callbacks
// TODO: Optionally add callback methods for async calls to the service agent
#endregion
#region Helpers
// Helper method to notify View of an error
private void NotifyError(string message, Exception error)
{
// Notify view of an error
Notify(ErrorNotice, new NotificationEventArgs<Exception>(message, error));
}
#endregion
}
}
When I use the key click (enter key) to call the Delegate Command GetSearchResultCommand I receive the following error:
Object reference not set to an instance of an object.
at SimpleMvvmToolkit.DelegateCommand`1.ConvertParameter(Object parameter)
at SimpleMvvmToolkit.DelegateCommand`1.Execute(Object parameter)
at System.Windows.Interactivity.InvokeCommandAction.Invoke(Object parameter)
at System.Windows.Interactivity.TriggerAction.CallInvoke(Object parameter)
at System.Windows.Interactivity.TriggerBase.InvokeActions(Object parameter)
at Microsoft.Expression.Interactivity.Input.KeyTrigger.OnKeyPress(Object sender, KeyEventArgs e)
at MS.Internal.CoreInvokeHandler.InvokeEventHandler(Int32 typeIndex, Delegate handlerDelegate, Object sender, Object args)
at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName)
If I try the button click to call the method I receive the following error:
Could not find method named 'MyFoo' on object of type 'SearchViewModel' that matches the expected signature.
at Microsoft.Expression.Interactivity.Core.CallMethodAction.Invoke(Object parameter)
at System.Windows.Interactivity.TriggerAction.CallInvoke(Object parameter)
at System.Windows.Interactivity.TriggerBase.InvokeActions(Object parameter)
at System.Windows.Interactivity.EventTriggerBase.OnEvent(EventArgs eventArgs)
at System.Windows.Interactivity.EventTriggerBase.OnEventImpl(Object sender, EventArgs eventArgs)
at System.Windows.Controls.Primitives.ButtonBase.OnClick()
at System.Windows.Controls.Button.OnClick()
at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
at System.Windows.Controls.Control.OnMouseLeftButtonUp(Control ctrl, EventArgs e)
at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName)
Note: I created the MyFoo method just as a test to see if I could even hit a method from within the view model. In the code MyFoo resides above the original method that needed to be invoked. These methods then called methods within my model/service which
handled the actual query.
SearchModel.cs
public class SearchModel : ModelBase<SearchModel>
{
#region Delegates
private ObservableCollection<SearchHistoryModel> SearchHistory = new ObservableCollection<SearchHistoryModel>();
private ObservableCollection<SavedSearchModel> SavedSearches;
#endregion
#region Methods
#region Get Search Results
public ObservableCollection<QueryResponse> GetSearchResults(string searchQuery)
{
SearchClient sc = new SearchClient();
sc.QueryCompleted +=new EventHandler<QueryCompletedEventArgs>(sc_QueryCompleted);
sc.QueryAsync(new Query { QueryText = searchQuery });
return this.SearchResults;
}
void sc_QueryCompleted(object sender, QueryCompletedEventArgs e)
{
try
{
if (SearchResults != null)
{
SearchResults.Clear();
this.SearchResults.Add(e.Result);
}
else
{
this.SearchResults.Add(e.Result);
}
}
catch(Exception ex)
{
ex.StackTrace.ToString();
}
}
..rest omitted for brevity.
I realize I am cutting in existing code and the challenge is trying to figure out what is available vs. how I used to write it. Any advice or suggestions (from you or others) as to what I am missing would be appreciated.
|