Cross threading exception accessing viewmodel property set from code behind - wpf

I have a WPF MVVM app that displays drug information in a telerik grid view. I want to do some filtering and paging in the view model but I keep getting cross-thread exceptions. The initial load works fine. I can change pages/page size without a problem. When I filter the grid, I create an IEnumerable of FilterDescriptors in the code behind and then set it on the Filters property of the view model. At this point, I get the cross-thread exception when doing the query using the filters. I have tried everything I can think of but can't get it to work.
public partial class DrugEdit : Page
{
public DrugEdit()
{
InitializeComponent();
}
private void RadGridView1_Filtered(object sender, Telerik.Windows.Controls.GridView.GridViewFilteredEventArgs e)
{
DrugEditViewModel vm = this.DataContext as DrugEditViewModel;
Telerik.Windows.Controls.RadGridView gv = sender as Telerik.Windows.Controls.RadGridView;
if (gv.FilterDescriptors == null || gv.FilterDescriptors.Count == 0)
vm.Filters = null;
else
{
List<FilterDescriptor> filters = (vm.Filters==null? new List<FilterDescriptor>(): vm.Filters.ToList());
foreach (FilterDescriptor r in e.Removed)
{
var fnd = filters.FirstOrDefault(x => x.Member == r.Member);
if (fnd == null) continue;
filters.Remove(fnd);
}
foreach (FilterDescriptor a in e.Added)
{
var fnd = filters.FirstOrDefault(x => x.Member == a.Member);
if (fnd == null)
{
filters.Add(new FilterDescriptor(a.Member, a.Operator, a.Value, false, a.MemberType));
}
else
{
fnd.Operator = a.Operator;
fnd.Value = a.Value;
}
}
vm.Filters = filters;
}
}
}
public class DrugEditViewModel
: ViewModelBase
{
private Data.DBContext ctx = new Data.DBContext();
private List<FilterDescriptor> FiltersValue = new List<FilterDescriptor>();
public List<FilterDescriptor> Filters
{
get { return FiltersValue; }
set
{
SetPropertyValue((() => Filters), ref FiltersValue, value);
RecordCount = 0;
LoadData();
}
}
private int PageValue = 0;
public int Page
{
get { return PageValue; }
set
{
SetPropertyValue((() => Page), ref PageValue, value);
LoadData();
}
}
private int PageSizeValue = 20;
public int PageSize
{
get { return PageSizeValue; }
set
{
SetPropertyValue((() => PageSize), ref PageSizeValue, value);
Page = 0;
}
}
private int RecordCountValue;
public int RecordCount
{
get { return RecordCountValue; }
set
{
SetPropertyValue((() => RecordCount), ref RecordCountValue, value);
}
}
private ObservableCollection<Models.Drug> DrugsValue;
public ObservableCollection<Models.Drug> Drugs
{
get { return DrugsValue; }
set
{
SetPropertyValue((() => Drugs), ref DrugsValue, value);
}
}
#endregion
#region Methods
private void LoadData()
{
if (ctx == null)
ctx = new Data.DBContext();
//load record count if we don't have it.
if (RecordCount == 0)
{
IsBusy = true;
Task.Run(() =>
{
if (Filters == null || Filters.Count() == 0)
{
RecordCount = ctx.Set<Entities.Drug>().Count();
}
else
{
RecordCount = ctx.Set<Entities.Drug>().Where(Filters).Count();
}
IsBusy = false;
LoadData();
});
return;
}
IsBusy = true;
Task.Run(() =>
{
if (Filters == null || Filters.Count() == 0)
currentPage = (from d in ctx.Query<Entities.Drug>()
orderby d.NDC
select d)
.Skip(PageSize * Page)
.Take(PageSize).ToList();
else
currentPage = (from d in ctx.Query<Entities.Drug>()
orderby d.NDC
select d)
.Where(Filters)
.Skip(PageSize * Page)
.Take(PageSize)
.ToIList() as List<Entities.Drug>;
Drugs = new ObservableCollection<Models.Drug>((from c in currentPage
select new Models.Drug(c)));
IsBusy = false;
});
}
#endregion
}

Related

Unsubscribe from SceneView draw calls when Edited Unity PropertyDrawer Array element got deleted

I'm making an editor in PropertyDrawer using SceneView.duringSceneGui. So it involves subscribing to SceneView.duringSceneGui when a property needs to draw stuff in SceneView and unsubscribing when it's gone. However I have no idea how to know if edited array element was removed from an array. It still exists in the memory and SceneView.duringSceneGui subscribed method is still there. I need to know when to stop editing and unsubscribe from it.
I guess I need to implement some context object, to store property value, edited object, PropertyDrawer and that subscription method should be there, to be able to unsubscribe exactly that editor... Although there may be only one editor running at once.
Does anybody found that out? Couldn't find anything with PropertyDrawers and array elements being deleted or removed.
TL.DR. Does Unity has an event to tell that PropertyDrawer's array element was removed or is there a simple or neat way to figure this out?
So, while making an example, I've solved my problem by getting property value every SceneView draw call and on catching an exception or if that value isn't edited stopped editor. I've added [NonSerialized] to _IsInEditMode to fix a new issue that I caught at the last moment, so that one is crucial.
Not sure if it's the best way to do that. If anybody will ever need to make a SceneView editor for some class, here's the example that works on arrays and lists also. Just separate it into 3 files and put them in respective folders, like Editor/ for MyClassDrawer.
using InspectorSerializedUtility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
public class MyScript : MonoBehaviour
{
public MyClass field;
public List<MyClass> list = new List<MyClass>();
}
[Serializable]
public class MyClass
{
#if UNITY_EDITOR
[NonSerialized]
public bool _IsInEditMode;
#endif
public Vector3 position;
public void Reset()
{
position = Vector3.zero;
#if UNITY_EDITOR
_IsInEditMode = false;
#endif
}
}
[CustomPropertyDrawer(typeof(MyClass))]
public class MyClassDrawer : PropertyDrawer
{
public MyClass value;
public Transform targetTransform;
private Tool internalTool;
bool editorStarted {
get => value?._IsInEditMode ?? false;
set {
if (this.value != null)
this.value._IsInEditMode = value;
}
}
private SerializedProperty currentProperty;
private SerializedProperty drawerProperty;
private static MyClassDrawer currentlyEditedDrawer;
string editorButtonText(bool isInEditMode) => isInEditMode ? "Stop Editing" : "Start Editing";
Color editorButtonColor(bool isInEditMode) => isInEditMode ? Color.red + Color.white / 2f : Color.white;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
//Debug.Log("OnGUI");
drawerProperty = property;
targetTransform = ((Component)property.serializedObject.targetObject).transform;
var val = property.GetValue<MyClass>();
GUI.color = editorButtonColor(val._IsInEditMode);
var toggle = GUI.Toggle(position, val._IsInEditMode, editorButtonText(val._IsInEditMode), "Button");
if (toggle != val._IsInEditMode)
{
if (toggle && currentlyEditedDrawer != null && currentlyEditedDrawer.editorStarted)
currentlyEditedDrawer.StopEditor();
value = val;
currentlyEditedDrawer = this;
if (toggle)
StartEditor();
else
StopEditor();
}
GUI.color = Color.white;
}
public void OnDrawScene(SceneView sv) => OnDrawScene();
public void OnDrawScene()
{
//Debug.Log("OnDrawScene");
MyClass value = null;
try
{
value = currentProperty.GetValue<MyClass>();
if (!value._IsInEditMode)
{
StopEditor();
return;
}
} catch
{
StopEditor();
return;
}
var m = Handles.matrix;
Handles.matrix = targetTransform.localToWorldMatrix;
if (Tools.current == Tool.Move)
{
internalTool = Tool.Move;
Tools.current = Tool.None;
}
if (internalTool == Tool.Move)
{
var pos = Handles.PositionHandle(value.position, Quaternion.identity);
if (value.position != pos)
{
Undo.RecordObject(targetTransform, "position changed");
value.position = pos;
}
}
Handles.matrix = m;
}
public void StartEditor()
{
currentProperty = drawerProperty;
editorStarted = true;
Debug.Log("StartEditor");
Subscribe();
CallAllSceneViewRepaint();
}
private void Subscribe()
{
Unsubscribe();
SceneView.duringSceneGui += OnDrawScene;
Selection.selectionChanged += StopEditor;
EditorSceneManager.sceneClosed += StopEditor;
AssemblyReloadEvents.beforeAssemblyReload += StopEditor;
}
public void StopEditor(Scene s) => StopEditor();
public void StopEditor()
{
Tools.current = internalTool;
editorStarted = false;
Unsubscribe();
currentProperty = null;
CallAllSceneViewRepaint();
}
private void Unsubscribe()
{
SceneView.duringSceneGui -= OnDrawScene;
Selection.selectionChanged -= StopEditor;
EditorSceneManager.sceneClosed -= StopEditor;
AssemblyReloadEvents.beforeAssemblyReload -= StopEditor;
}
private void CallAllSceneViewRepaint()
{
foreach (SceneView sv in SceneView.sceneViews)
sv.Repaint();
}
}
namespace InspectorSerializedUtility
{
/// <summary>
/// https://gist.github.com/douduck08/6d3e323b538a741466de00c30aa4b61f
/// </summary>
public static class InspectorSeriallizedUtils
{
public static T GetValue<T>(this SerializedProperty property) where T : class
{
try
{
if (property.serializedObject.targetObject == null) return null;
}
catch
{
return null;
}
object obj = property.serializedObject.targetObject;
string path = property.propertyPath.Replace(".Array.data", "");
string[] fieldStructure = path.Split('.');
Regex rgx = new Regex(#"\[\d+\]");
for (int i = 0; i < fieldStructure.Length; i++)
{
if (fieldStructure[i].Contains("["))
{
int index = System.Convert.ToInt32(new string(fieldStructure[i].Where(c => char.IsDigit(c)).ToArray()));
obj = GetFieldValueWithIndex(rgx.Replace(fieldStructure[i], ""), obj, index);
}
else
{
obj = GetFieldValue(fieldStructure[i], obj);
}
}
return (T)obj;
}
public static bool SetValue<T>(this SerializedProperty property, T value) where T : class
{
object obj = property.serializedObject.targetObject;
string path = property.propertyPath.Replace(".Array.data", "");
string[] fieldStructure = path.Split('.');
Regex rgx = new Regex(#"\[\d+\]");
for (int i = 0; i < fieldStructure.Length - 1; i++)
{
if (fieldStructure[i].Contains("["))
{
int index = System.Convert.ToInt32(new string(fieldStructure[i].Where(c => char.IsDigit(c)).ToArray()));
obj = GetFieldValueWithIndex(rgx.Replace(fieldStructure[i], ""), obj, index);
}
else
{
obj = GetFieldValue(fieldStructure[i], obj);
}
}
string fieldName = fieldStructure.Last();
if (fieldName.Contains("["))
{
int index = System.Convert.ToInt32(new string(fieldName.Where(c => char.IsDigit(c)).ToArray()));
return SetFieldValueWithIndex(rgx.Replace(fieldName, ""), obj, index, value);
}
else
{
Debug.Log(value);
return SetFieldValue(fieldName, obj, value);
}
}
private static object GetFieldValue(string fieldName, object obj, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
{
FieldInfo field = obj.GetType().GetField(fieldName, bindings);
if (field != null)
{
return field.GetValue(obj);
}
return default(object);
}
private static object GetFieldValueWithIndex(string fieldName, object obj, int index, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
{
FieldInfo field = obj.GetType().GetField(fieldName, bindings);
if (field != null)
{
object list = field.GetValue(obj);
if (list.GetType().IsArray)
{
return ((object[])list)[index];
}
else if (list is IEnumerable)
{
return ((IList)list)[index];
}
}
return default(object);
}
public static bool SetFieldValue(string fieldName, object obj, object value, bool includeAllBases = false, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
{
FieldInfo field = obj.GetType().GetField(fieldName, bindings);
if (field != null)
{
field.SetValue(obj, value);
return true;
}
return false;
}
public static bool SetFieldValueWithIndex(string fieldName, object obj, int index, object value, bool includeAllBases = false, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
{
FieldInfo field = obj.GetType().GetField(fieldName, bindings);
if (field != null)
{
object list = field.GetValue(obj);
if (list.GetType().IsArray)
{
((object[])list)[index] = value;
return true;
}
else if (value is IEnumerable)
{
((IList)list)[index] = value;
return true;
}
}
return false;
}
}
}

How do I set "OnPropertyChanged" on Properties from Model?

I Have Two ComboBoxes for example Country and State where State ComboBox's ItemSource depends on Selected Country of First ComboBox.
Country and State Properties are defined in separate CountryModel(CountryID,CountryName) and StateModel(CountryID,StateID,StateName).
Now there is the Model "UserModel" is like :
public class UserModel:ViewModelBase
{
private string _userName;
public string UserName
{
get { return _userName; }
set { _userName = value; OnPropertyChanged("UserName"); }
}
private long _stateID;
public long StateID
{
get { return _stateID; }
set { _stateID = value; OnPropertyChanged("StateID"); }
}
private int _countryID;
public int CountryID
{
get { return _countryID; }
set { _countryID = value; OnPropertyChanged("CountryID"); }
}
}
It has a Service "UserModelService" for populating :
public class UserModelService
{
public UserModelService()
{
}
public ObservableCollection<UserModel> GetUserList()
{...}
public ObservableCollection<CountryModel> GetCountryList()
{...}
public ObservableCollection<StateModel> GetStateList()
{...}
}
Now The UserViewModel is Like:
public class UserViewModel:ViewModelBase,IPageViewModel
{
UserModelService modelService;
public UserViewModel()
{
modelService = new UserModelService();
GetData();
}
private void GetData()
{
UserList = modelService.GetUserList();
CountryList = modelService.GetCountryList();
StateList = modelService.GetStateList();
}
private UserModel _currentUser=new UserModel();
public UserModel CurrentUser
{
get { return _currentUser; }
set
{
if (value == _currentUser) return;
_currentUser = value; OnPropertyChanged("CurrentUser");
}
}
private ObservableCollection<UserModel> _userList;
public ObservableCollection<UserModel> UserList
{
get { return _userList; }
set
{
if (value == _userList) return;
_userList = value;
OnPropertyChanged("UserList");
}
}
private ObservableCollection<CountryModel> _countryList;
public ObservableCollection<CountryModel> CountryList
{
get { return _countryList; }
set { _countryList = value; OnPropertyChanged("CountryList"); }
}
private ObservableCollection<StateModel> _stateList;
public ObservableCollection<StateModel> StateList
{
get { return _stateList; }
set
{
_stateList = value;
OnPropertyChanged("StateList");
}
}
}
Now the Country and State ComboBox's ItemSource is binded to UserViewModels "CountryList" and "StateList" respectively. In this scenario how to I repopulate StateList OnPropertyChange of CurrentUser.CountryID?
EDIT: I Solved it by introducing separate property for Selected Country and OnPropertyChanged CurrentUser.CountryID=selectedCountry. Thanks
Handle the PropertyChanged event for the current user:
public UserModel CurrentUser
{
get { return _currentUser; }
set
{
if (value == _currentUser)
return;
if (_currentUser != null)
_currentUser.PropertyChanged -= OnCurrentUserPropertyChanged;
_currentUser = value;
if (_currentUser != null)
_currentUser.PropertyChanged += OnCurrentUserPropertyChanged;
OnPropertyChanged("CurrentUser");
}
}
private void OnCurrentUserPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(UserModel.CountryID))
{
UserModel userModel = (UserModel)sender;
var states = ...; //get states by userModel.CountryID
StateList = states;
}
}

why does my ObservableCollection not filter

i have a WPF desktop app.
I load an ObservableCollection with a list of objects.
I use the ICollectionView object to 'wrap' a filter around this ObservableCollection .
I set the filter and refresh but it does nto work so:
public class DataFilters : ViewModelBase
{
private ICollectionView _UserMappedRolesView { get; set; }
private ObservableCollection<UserMappedRoles> _UserMappedRoles;
public ObservableCollection<UserMappedRoles> UserMappedRoles
{
get
{
_UserMappedRolesView = CollectionViewSource.GetDefaultView(_UserMappedRoles);
_UserMappedRolesView.Filter = UserMappedRolesFilter;
_UserMappedRolesView.Refresh();
return _UserMappedRoles;
}
set
{
_UserMappedRoles = value;
}
}
public void LoadUserMappedRoles()
{
var baseData = InformedWorkerBusinessService.UserMappedRoles.Get();
var modelData =
from data in baseData
select new UserMappedRoles
{
Enabled = 1,
Login = data.Login,
UserMappedRolesRef = data.UserMappedRolesRef,
UserRoleRef = data.UserRoleRef
};
_UserMappedRoles = new ObservableCollection<UserMappedRoles>(modelData);
}
public string Login { get; set; }
private bool UserMappedRolesFilter(object item)
{
UserMappedRoles UserMappedRole = item as UserMappedRoles;
if (UserMappedRole.Login== Login)
{
return true;
}
else
{
return false;
}
}
}
and my test script:
UI.InformedWorkerViewModel.Models.HeartBeat VM = new UI.InformedWorkerViewModel.Models.HeartBeat();
VM.CommonData.DataFilters = new UI.InformedWorkerViewModel.Models.DataFilters();
VM.CommonData.DataFilters.LoadUserMappedRoles();
var data = VM.CommonData.DataFilters.UserMappedRoles;
VM.CommonData.DataFilters.Login = "David";
var filtered = VM.CommonData.DataFilters.UserMappedRoles;
I know my data only contains oUserMappedRoles where the Login name is 'Andy' so, by setting the Login filter name to 'David' I expect to get no records back.
I have set breakpoints everywhere and everyline of code gets 'hit'.
Have I (obviously) implemented this wrong?
Thanks
It is the ICollectionView that gets filtered, not the ObservableCollection.
So you should bind to the ICollectionView property:
<ListBox ItemsSource="{Binding UserMappedRolesView}" DisplayMemberPath="Login" />
...or look for the filtered items in this one:
var filtered = VM.CommonData.DataFilters.UserMappedRolesView;
You also need to refresh the CollectionView whenever you want to re-apply the filter, i.e. whenever your Login property is set to a new value. Something like this:
public class DataFilters : ViewModelBase
{
private ICollectionView _UserMappedRolesView;
public ICollectionView UserMappedRolesView
{
get { return _UserMappedRolesView; }
set { _UserMappedRolesView = value; NotifyPropertyChanged(); }
}
private ObservableCollection<UserMappedRoles> _UserMappedRoles;
public ObservableCollection<UserMappedRoles> UserMappedRoles
{
get
{
return _UserMappedRoles;
}
set
{
_UserMappedRoles = value;
NotifyPropertyChanged();
UserMappedRolesView = CollectionViewSource.GetDefaultView(_UserMappedRoles);
UserMappedRolesView.Filter = UserMappedRolesFilter;
UserMappedRolesView.Refresh();
}
}
public void LoadUserMappedRoles()
{
var baseData = InformedWorkerBusinessService.UserMappedRoles.Get();
var modelData =
from data in baseData
select new UserMappedRoles
{
Enabled = 1,
Login = data.Login,
UserMappedRolesRef = data.UserMappedRolesRef,
UserRoleRef = data.UserRoleRef
};
UserMappedRoles = new ObservableCollection<UserMappedRoles>(modelData);
}
private string _login;
public string Login
{
get { return _login; }
set { _login = value; _UserMappedRolesView.Refresh(); }
}
private bool UserMappedRolesFilter(object item)
{
UserMappedRoles UserMappedRole = item as UserMappedRoles;
if (UserMappedRole.Login == Login)
{
return true;
}
else
{
return false;
}
}
}

Paging ListBox with ReactiveUI and Caliburn.Micro

I'm trying to implement a paging mechanism for a listbox using Caliburn.Micro.ReactiveUI with a call to EF using ".Skip(currentPage).Take(pageSize)". I'm new to ReactiveUI and Reactive in general. I'm sure this is supposed to be easy.
I've got a single "SearchParameters" class which I needs to be observed and the search function needs to execute when any of the properties on the SearchParameters object changes.
You can see from the commented-out code that I've tried to define the class as a ReactiveObject as well. The current implementation though is with CM's PropertyChangedBase. The individual properties are bound textboxes in my view using CM's conventions:
public class SearchParameters : PropertyChangedBase
{
private string _searchTerm;
public string SearchTerm
{
get { return _searchTerm; }
set
{
if (value == _searchTerm) return;
_searchTerm = value;
NotifyOfPropertyChange(() => SearchTerm);
}
}
private int _pageSize;
public int PageSize
{
get { return _pageSize; }
set
{
if (value == _pageSize) return;
_pageSize = value;
NotifyOfPropertyChange(() => PageSize);
}
}
private int _skipCount;
public int SkipCount
{
get { return _skipCount; }
set
{
if (value == _skipCount) return;
_skipCount = value;
NotifyOfPropertyChange(() => SkipCount);
}
}
//private string _searchTerm;
//public string SearchTerm
//{
// get { return _searchTerm; }
// set { this.RaiseAndSetIfChanged(ref _searchTerm, value); }
//}
//private int _pageSize;
//public int PageSize
//{
// get { return _pageSize; }
// set { this.RaiseAndSetIfChanged(ref _pageSize, value); }
//}
//private int _skipCount;
//public int SkipCount
//{
// get { return _skipCount; }
// set { this.RaiseAndSetIfChanged(ref _skipCount, value); }
//}
}
"SearchService" has the following method which needs to execute when any one of SearchParameter's values change:
public async Task<SearchResult> SearchAsync(SearchParameters searchParameters)
{
return await Task.Run(() =>
{
var query = (from m in _hrEntities.Departments select m);
if (!String.IsNullOrEmpty(searchParameters.SearchTerm))
{
searchParameters.SearchTerm = searchParameters.SearchTerm.Trim();
query = query.Where(
x => x.Employee.LastName.Contains(searchParameters.SearchTerm) || x.Employee.FirstName.Contains(searchParameters.SearchTerm)).Skip(searchParameters.SkipCount).Take(searchParameters.PageSize);
}
return new SearchResult
{
SearchTerm = searchParameters.SearchTerm,
Matches = new BindableCollection<DepartmentViewModel>(query.Select(x => new DepartmentViewModel{ Department = x }).Skip(searchParameters.SkipCount).Take(searchParameters.PageSize))
};
});
}
Here's how I've tried to wire all of this up in MainViewModel's ctor and where Rx gets hazy for me:
public class MainViewModel : ReactiveScreen
{
private SearchParameters _searchParameters;
public SearchParameters SearchParameters
{
get { return _searchParameters; }
set
{
if (value == _searchParameters) return;
_searchParameters = value;
NotifyOfPropertyChange(() => SearchParameters);
}
}
{
public void MainViewModel()
{
var searchService = new SearchService();
//default Skip and PageSize values
SearchParameters = new Services.SearchParameters { SkipCount = 0 , PageSize = 10};
var searchParameters = this.ObservableForProperty(x => x.SearchParameters)
.Value()
.Throttle(TimeSpan.FromSeconds(.3));
var searchResults = searchParameters.SelectMany(parameters => searchService.SearchAsync(parameters));
var latestMatches = searchParameters
.CombineLatest(searchResults,
(searchParameter, searchResult) =>
searchResult.SearchTerm != searchParameter.SearchTerm
? null
: searchResult.Matches)
.Where(matches => matches != null);
_departmentViewModels = latestMatches.ToProperty(this, x => x.DepartmentViewModels);
searchParameters.Subscribe(x => Debug.WriteLine(x));
}
}
In the above example the call to SearchAsync doesn't execute. It seems that changes to SearchParameter's properties aren't being observed.
Can anyone tell me what I'm doing wrong here?
Here's how I ended up doing this although I'd be interested in hearing other solutions if anyone has suggestions. I'm not sure if this is the best way but it works:
First, I defined a computed property in my SearchParameters class that returns a string and reevaluates anytime CurrentPage, SkipCount and PageSize are updated from the View:
public string ParameterString
{
get { return String.Format("SearchTerm={0}|SkipCount={1}|PageSize={2}", SearchTerm, SkipCount, PageSize); }
}
Next, in my MainViewModel ctor I simply observe the computed rather than attempting to react to SearchTerm, SkipCount and PageSize individually (which my original question was asking how to do):
var searchTerms = this
.ObservableForProperty(x => x.SearchParameters.ParameterString)
.Value()
.Throttle(TimeSpan.FromSeconds(.3));
var searchResults = searchTerms.SelectMany(parameters => SearchService.SearchAsync(parameters));
var latestMatches = searchTerms
.CombineLatest(searchResults,
(searchTerm, searchResult) =>
searchResult.SearchTerm != searchTerm
? null
: searchResult.Matches)
.Where(matches => matches != null);
Finally, in my SearchService I parse the parameter string to get the current values:
var parameters = searchParameters.Split('|');
var searchTerm = "";
var skipCount = 0;
var pageSize = 0;
foreach (var parameter in parameters)
{
if (parameter.Contains("SearchTerm="))
{searchTerm = parameter.Replace("SearchTerm=", "");}
else if (parameter.Contains("SkipCount="))
{ skipCount = Convert.ToInt32(parameter.Replace("SkipCount=", "")); }
else if (parameter.Contains("PageSize="))
{ pageSize = Convert.ToInt32(parameter.Replace("PageSize=", "")); }
}

mvvm how to pass data from one view model to another view model

I have One View which has one Data grid with radio Button , onchecking radio Box , the selected row should go to other View Screen Textbox
here is my first ViewModel
public class CampaignSearchResultsViewModel : ViewModelBase
{
public CampaignSearchResultsViewModel(List<Lead> obj)
{
foreach(Lead lead in obj)
{
SelectedLead = lead;
}
}
public CampaignSearchResultsViewModel()
{
this.Commands.Add("CheckedCommand", new ActionCommand<Lead>(CheckIt));
Commands.Add("OutboundSelect", new ActionCommand<Object>(OutboundSelection));
_leads = new ObservableCollection<Lead>();
}
public ICommand OutboundSelect
{
get
{
return Commands["OutboundSelect"];
}
}
public void OutboundSelection(Object obj)
{
}
private void CheckIt(Lead lead)
{
SelectedLead = lead;
LeadViewModel lmv = new LeadViewModel(this);
}
#region Private
private ObservableCollection<Lead> _leads;
public bool IsChecked { get; set; }
private ICommand _checkedCommand;
private object _testProperty;
private Lead _selectedLead;
private ICollectionView icv;
#endregion
private ICommand _checkedRadioCommand;
private bool _inboundChecked;
#region Properties
public ObservableCollection<Lead> Leads
{
get { return _leads; }
set
{
_leads = value;
FirePropertyChanged("Leads");
}
}
public Lead SelectedLead
{
get { return _selectedLead; }
set { _selectedLead = value; }
}
public ICommand CheckedCommand
{
get
{
return Commands["CheckedCommand"];
}
}
public bool InboundChecked
{
get
{
return _inboundChecked;
}
private set
{
if (_inboundChecked != value)
{
_inboundChecked = value;
FirePropertyChanged("InboundChecked");
}
}
}
#endregion
}
i have to map SelectedLead to the other view model i have pass info to SearchCampaignMembers() method , how
public partial class LeadViewModel : ViewModelBase
{
public void SearchCampaignMembers()
{
_service.Load(_service.SearchCampaignMembersQuery(Entity.FirstName, Entity.LastName), lo =>
{
if (!lo.HasError)
{
ListLead = lo.Entities.ToList();
_savedLeadStatusId = Entity.LeadStatusId;
EntitySet = _service.Leads;
if (ListLead.Count == 1)
{
if (Entity != null)
{
IsVendorLead = Entity.LeadTypeId == Lookups.LeadType.VendorLead;
//Lead Update History
EntityQuery<LeadUpdateHistory> historyquery = null;
historyquery = _service.GetLeadUpdateHistoryByLeadIdQuery(Entity.LeadId);
_service.Load(historyquery, l =>
{
if (!l.HasError)
{
EntityHistory = _service.LeadUpdateHistories;
}
}, null);
//Lead Assignment
EntityQuery<LeadsAssignment> assignmentquery = null;
assignmentquery = _service.GetLeadsAssignmentByLeadIdQuery(Entity.LeadId);
_service.Load(assignmentquery, l =>
{
if (!l.HasError)
{
EntityAssignment = _service.LeadsAssignments;
}
}, null);
if (Entity.LeadTypeId == Lookups.LeadType.PhoneLead)
{
IsInboundLead = Entity.VendorId == null;
IsOutboundLead = Entity.VendorId != null;
}
else
{
IsInboundLead = false;
IsOutboundLead = false;
}
//SelectTimeToCall(Entity);
if (IsOutboundLead)
SelectedCampaign = Entity.LeadCampaigns.FirstOrDefault().Campaign;
else
SelectCampaign(Entity);
OperationsListener listener = new OperationsListener();
listener.Completed += (s, args) =>
{
CompleteInitializing();
//SwitchTab(param.InitialTab);
Action action = () =>
{
SelectDealer(Entity);
};
//GetDealerRecommendation(Entity.Address.ZipCode, action);
SelectStatus(Entity);
//if (callback != null)
// callback();
};
LoadLookupData(listener);
listener.Start();
}
}
else if (ListLead.Count >= 1)
{
CampaignSearchResultsViewModel vm = new CampaignSearchResultsViewModel();
foreach (Lead lead in ListLead)
{
vm.Leads.Add(lead);
ObservableCollection<Lead> abc;
abc = new ObservableCollection<Server.DataAccess.Lead>();
}
ViewController.OpenDialog("SearchCampaignResults", vm, r =>
{
});
}
else if (ListLead.Count == 0)
{
ViewController.OpenDialog("NoResults", (r) =>
{
});
}
}
else
{
//if (callback != null)
// callback();
}
}, null);
}
}
If you use MVVM Light Toolkit, see Messenger class see this answer for sample.

Resources