I am currently doing on a project that requires me to move my data between tabs/fragments.. Let's say the user clicks on the listView item, they will move to another tab, instead of staying in the same tab. May I know how can I achieve that? Can someone help to solve my query? Thank you!
You can trasnfer data between tabs by set and get Arguments,
Here is Example
FragmentTwo fragmentTwo = new FragmentTwo();
Bundle bundle = new Bundle();
bundle.putString("key1", "data1");
bundle.putString("key2", "data2");
bundle.putString("key3", "data3");
fragmentTwo.setArguments(bundle);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.main_container, fragmentTwo);
fragmentTransaction.commit();
There are 3 ways to do this
1) Use Interfaces -use interface to pass data objects. Meshy solution
Read more
public interface onDataCHange{
public void updateData(String data);
}
2) Use Activity Class - Store model object in activity class and set and get using activity Instance. Quick and Dirty solution
Read more
//Get
Object dataModel = (ContainerActivity) getActivity()).getData();
//Set
((ContainerActivity) getActivity()).setData(dataModel );
3) Clean architecture - Center repository hold model objects. View update model via Singleton Center repository object. Single copy of data flow between throughout the App.
Read more
#Singleton
public class UserDataRepository implements UserRepository {
private final UserDataStoreFactory userDataStoreFactory;
private final UserEntityDataMapper userEntityDataMapper;
/**
* Constructs a {#link UserRepository}.
*
* #param dataStoreFactory A factory to construct different data source implementations.
* #param userEntityDataMapper {#link UserEntityDataMapper}.
*/
#Inject
UserDataRepository(UserDataStoreFactory dataStoreFactory,
UserEntityDataMapper userEntityDataMapper) {
this.userDataStoreFactory = dataStoreFactory;
this.userEntityDataMapper = userEntityDataMapper;
}
#Override public Observable<List<User>> users() {
//we always get all users from the cloud
final UserDataStore userDataStore = this.userDataStoreFactory.createCloudDataStore();
return userDataStore.userEntityList().map(this.userEntityDataMapper::transform);
}
#Override public Observable<User> user(int userId) {
final UserDataStore userDataStore = this.userDataStoreFactory.create(userId);
return userDataStore.userEntityDetails(userId).map(this.userEntityDataMapper::transform);
}
}
Related
I have a Trade class which contains a property currentPrice, which downloads price data from a website using getPricedata() method. The Trade object will show up as a table row in TableView. Now, my task: is to
use the getPricedata() method to grab data from internet, populate the currentPrice cell, whenever the object is created.
relaunch the getPricedata() method to every 1 minute after the object has been created and update table cell.
Below is the basic structure of my code. But I have no idea how to implement this ?
Which package do I need ? Task ? Service ? ScheduledService ?
public class Trade{
private DoubleProperty currentPrice;
// need thread here
public double getPricedata(){
.......
}
}
Use a ScheduledService<Number>, whose Task<Number>'s call() method retrieves and returns the value. Then you can either register an onSucceeded handler with the service, or just bind the Trade's currentPrice to service.lastValue(). Call setPeriod(..) on the service (once) to configure it to run every minute.
Since the currentPrice is being set from the service, you should only expose a ReadOnlyDoubleProperty from your Trade class (otherwise you might try to call currentPriceProperty().set(...) or setCurrentPrice(...), which would fail as it's bound).
I would do something like
public class Trade {
private final ReadOnlyDoubleWrapper currentPrice ;
private final ScheduledService<Number> priceService = new ScheduledService<Number>() {
#Override
public Task<Number> createTask() {
return new Task<Number>() {
#Override
public Number call() {
return getPriceData();
}
};
}
};
public Trade() {
priceService.setPeriod(Duration.minutes(1));
// in case of errors running service:
priceService.setOnFailed(e -> priceService.getException().printStackTrace());
currentPrice = new ReadOnlyDoubleWrapper(0);
currentPrice.bind(priceService.lastValueProperty());
startMonitoring();
}
public final void startMonitoring() {
priceService.restart();
}
public final void stopMonitoring() {
priceService.cancel();
}
public ReadOnlyDoubleProperty currentPriceProperty() {
return currentPrice.getReadOnlyProperty();
}
public final double getCurrentPrice() {
return currentPriceProperty().get();
}
private double getPriceData() {
// do actual retrieval work here...
}
}
(Code just typed in here without testing, but it should give you the idea.)
I cannot find any documentation on connecting a view model to a repository using Catel.
I have set up the Repository Pattern and my Models with EF6 Code First (all extending from ModelBase) but need to know how to use it with a ViewModel.
Do I need to create a service for the UnitOfWork? And if so, how? How will I use this in a ViewModel?
I am currently using the repository as a model in my viewmodel, but i do not think this is the correct way to do it? See my CompaniesViewModel below:
IUnitOfWork uow;
public CompaniesViewModel()
{
uow = new UnitOfWork<SoftwareSolutionsContext>();
CompanyRepository = uow.GetRepository<ICompanyRepository>();
}
public override string Title { get { return "Companies"; } }
protected override async Task Close()
{
uow.Dispose();
await base.Close();
}
protected override async Task Initialize()
{
Companies = new ObservableCollection<Company>(CompanyRepository.GetAll());
await base.Initialize();
}
public ObservableCollection<Company> Companies
{
get { return GetValue<ObservableCollection<Company>>(CompaniesProperty); }
set { SetValue(CompaniesProperty, value); }
}
public static readonly PropertyData CompaniesProperty = RegisterProperty("Companies", typeof(ObservableCollection<Company>), null);
[Model]
public ICompanyRepository CompanyRepository
{
get { return GetValue<ICompanyRepository>(CompanyRepositoryProperty); }
private set { SetValue(CompanyRepositoryProperty, value); }
}
public static readonly PropertyData CompanyRepositoryProperty = RegisterProperty("CompanyRepository", typeof(ICompanyRepository));
Essentially, I have 2 scenarios for working on the data:
getting all the data to display on a datagrid
selecting a record on the datagrid to open another view for editing a single record
Any guidance would be appreciated.
This is a very difficult subject, because there are basically a few options here:
Create abstractions in services (so the VM's only work with services, the services are your API into the db). The services work with the UoW
There are some people thinking that 1 is overcomplicated. In that case, you can simply use the UoW inside your VM's
Both have their pros and cons, just pick what you believe in most.
I cannot get Undo and Redo to behave correctly when using a dialog.
I have a simple model with a property indicating the state of the object(running, paused, stopped) which can be altered via a dialog. What happens is that I get actions that seems to do nothing in my undo queue or undo restores the object to an intermediate state.
The model object is registered with memento in the constructor. The dialog has three radio buttons each representing one of the three different states. Each radio button is bind to a command each. Each command performs a change of the property. I have tried two different approaches, either each command sets the property directly in the object or each command sets an instance variable for the view model when called and then I use the Saving event to modify the object.
If using the first approach each property change is put on the Undo queue if the user clicks on more than just one radiobutton before clicking Ok in the dialog. Tried to solve that by wrapping the whole dialog into a batch but that results in undoing the state change the object is restored to the state it had before the final one, i.e. if the property was set to stopped before the dialog opened and the user pressed the pause radiobutton, then start one and finally Ok, undo will set the property to paused instead of the expected stopped.
If using the second approach the user opens the dialog, change the state to paused, click Ok in the dialog the undo/redo behaves as expected but if the dialog is opened again and Cancel is chosen one more action is added to the Undo queue, i.e. the user has to click Undo twice to get back to the initial stopped-state.
So my question is how should this be correctly implemented to get the expected behaviour; that each dialog interaction can be undone and not every interaction in the dialog?
Here is the code for the ViewModel:
namespace UndoRedoTest.ViewModels
{
using Catel.Data;
using Catel.MVVM;
public class StartStopViewModel : ViewModelBase
{
Machine.MachineState _state;
public StartStopViewModel(Machine controlledMachine)
{
ControlledMachine = controlledMachine;
_state = controlledMachine.State;
StartMachine = new Command(OnStartMachineExecute);
PauseMachine = new Command(OnPauseMachineExecute);
StopMachine = new Command(OnStopMachineExecute);
Saving += StartStopViewModel_Saving;
}
void StartStopViewModel_Saving(object sender, SavingEventArgs e)
{
ControlledMachine.State = _state;
}
[Model]
public Machine ControlledMachine
{
get { return GetValue<Machine>(ControlledMachineProperty); }
private set { SetValue(ControlledMachineProperty, value); }
}
public static readonly PropertyData ControlledMachineProperty = RegisterProperty("ControlledMachine", typeof(Machine));
public override string Title { get { return "Set Machine state"; } }
public Command StartMachine { get; private set; }
public Command PauseMachine { get; private set; }
public Command StopMachine { get; private set; }
private void OnStartMachineExecute()
{
_state = Machine.MachineState.RUNNING;
//ControlledMachine.SecondState = Machine.MachineState.RUNNING;
}
private void OnPauseMachineExecute()
{
_state = Machine.MachineState.PAUSED;
//ControlledMachine.SecondState = Machine.MachineState.PAUSED;
}
private void OnStopMachineExecute()
{
_state = Machine.MachineState.STOPPED;
//ControlledMachine.SecondState = Machine.MachineState.STOPPED;
}
}
}
First of all, don't subscribe to the Saving event but simply override the Save() method. Note that Catel handles the model state for you when you decorate a model with the ModelAttribute. Therefore you need to get the prestate and poststate of the dialog and then push the result set into a batch.
For example, I would create extension methods for the object class (or model class) like this:
public static Dictionary<string, object> GetProperties(this IModel model)
{
// todo: return properties
}
Then you do this in the Initialize and in the Save method and you would have 2 sets of properties (pre state and post state). Now you have that, it's easy to calculate the differences:
public static Dictionary<string, object> GetChangedProperties(Dictionary<string, object> preState, Dictionary<string, object> postState)
{
// todo: calculate difference
}
Now you have the difference, you can create a memento batch and it would restore the exact state as you expected.
ps. it would be great if you could put this into a blog post once done or create a PR with this feature
I want to use a third party view controller that already inherits from UIViewController (https://bitbucket.org/thedillonb/monotouch.slideoutnavigation/src/f4e51488598b/MonoTouch.SlideoutNavigation?at=master), how would I integrate that with MVVMCross?
I could just take the source and change it to inherit from MvxViewController, but guessing I will run into this with other libraries.
Do I need to implement all the interfaces MvxViewController does? IMvxTouchView? IMvxEventSourceViewController?
For this particular case, where you don't actually want to do any data-binding so you can just use a custom presenter - e.g. see #Blounty's answer, or see this project demo - https://github.com/fcaico/MvxSlidingPanels.Touch
If you ever do need to convert third party ViewController base classes so that they support data-binding, then the easiest way is exactly what you guessed:
inherit from them to provide an EventSource-ViewController
inherit from the EventSource-ViewController to add the Mvx BindingContext
This technique is exactly how MvvmCross itself extends each of UIViewController, UITableViewController, UITabBarController, etc in order to provide data-binding.
For example, see:
extending UIViewController to provide an eventsource - MvxEventSourceViewController.cs
extending the event source ViewController to provide a binding context - MvxViewController.cs
Note that because C# doesn't have any Multiple-Inhertiance or any true Mixin support, this adaption of ViewControllers does involve a little cut-and-paste, but we have tried to minimise this through the use of event hooks and extension methods.
If it helps, this iOS technique for a previous MvvmCross version was discussed in Integrating Google Mobile Analytics with MVVMCross (obviously this is out of date now - but the general principles kind of remain the same - we adapt an existing viewcontroller via inheritance)
In Android, a similar process is also followed for Activity base classes - see ActionBarSherlock with latest MVVMCross
You can use a custom view presenter like below, This is pretty much straight out of my app using the SlideOutNavigation.
public class Presenter
: IMvxTouchViewPresenter
{
private readonly MvxApplicationDelegate applicationDelegate;
private readonly UIWindow window;
private SlideoutNavigationController slideNavigationController;
private IMvxTouchViewCreator viewCreator;
public Presenter(MvxApplicationDelegate applicationDelegate, UIWindow window)
{
this.applicationDelegate = applicationDelegate;
this.window = window;
this.slideNavigationController = new SlideoutNavigationController();
this.slideNavigationController.SlideWidth = 200f;
this.window.RootViewController = this.slideNavigationController;
}
public async void Show(MvxViewModelRequest request)
{
var creator = Mvx.Resolve<IMvxTouchViewCreator>();
if (this.slideNavigationController.MenuView == null)
{
// TODO: MAke this not be sucky
this.slideNavigationController.MenuView = (MenuView)creator.CreateView(new MenuViewModel());
((MenuView) this.slideNavigationController.MenuView).MenuItemSelectedAction = this.MenuItemSelected;
}
var view = creator.CreateView(request);
this.slideNavigationController.TopView = (UIViewController)view;
}
public void ChangePresentation(MvxPresentationHint hint)
{
Console.WriteLine("Change Presentation Requested");
}
public bool PresentModalViewController(UIViewController controller, bool animated)
{
Console.WriteLine("Present View Controller Requested");
return true;
}
public void NativeModalViewControllerDisappearedOnItsOwn()
{
Console.WriteLine("NativeModalViewControllerDisappearedOnItsOwn");
}
private void MenuItemSelected(string targetType, string objectId)
{
var type = Type.GetType(string.Format("App.Core.ViewModels.{0}ViewModel, AppCore", targetType));
var parameters = new Dictionary<string, string>();
parameters.Add("objectId", objectId);
this.Show(new MvxViewModelRequest { ViewModelType = type, ParameterValues = parameters });
}
}
I need a custom binding and I know when and where but I don't know how I can do it. This is the relation of the view in my custom binding. Think about the *Views like controls.
I have the connections from ViewModel->ContainerView->FirstView but I can't connect it with the TableView. To connect the ContainerView to FirstView I did a custom binding (in one direction for now). And in the setvalue method I call the firstview's method SetBinding (where I want to do the binding)
I tried a few option but nothing happens, the last one looks like this:
public GolferList CurrentGolferList { get; set; }
public void SetBinding(GolferList golferList){
this.CurrentGolferList = golferList;
TableSource = new TableSourcePlayers(TableViewPlayers);
var bindingDescription = new[]{
new MvxBindingDescription {TargetName = "ItemsSource",SourcePropertyPath = "CurrentGolferList"} ,
};
Binder.Bind(this,TableSource, bindingDescription);
TableViewPlayers.Source = TableSource;
TableViewPlayers.ReloadData();
}
I would be grateful if you could tell me another way to handle it.
Update:
I followed Stuart's link and now it works fine, thanks a lot Stuart!
Actually, in my scheme the TableView is a MvxSimpleBindableTableViewSource and I want to bind the data there. So in order to make it work, I used the code below (SetBinding needs some external refactor):
private List<IMvxUpdateableBinding> bindings;
private string BindingText = "{'ItemsSource':{'Path':'CurrentGolfers'}}";
public object DataContext {
get { return dataContext; }
set { dataContext = value;
if (bindings == null)
bindings = this.GetService<IMvxBinder>().Bind(dataContext, TableSource, BindingText).ToList();
else
bindings.ForEach(b => b.DataContext = dataContext);
}
}
public void SetBinding(GolferList golferList){
this.DataContext = PlayViewModel;
tableView.Source = TableSource;
tableView.ReloadData();
}
Note that BindingText points to the table, not to the view itself.
Update 2
Now in V3 it's a bit different. First, the view must implement IMvxBindable and this members:
public object DataContext
{
get { return BindingContext.DataContext; }
set { BindingContext.DataContext = value; }
}
public IMvxBindingContext BindingContext { get; set; }
(Don't forget dispose calling BindingContext.ClearAllBindings() and also call to CreateBindingContext() in the viewload )
And then you'll be able to bind in your class. In my case:
var set = this.CreateBindingSet<FirstPlayViewController, PlayViewModel>();
set.Bind(source).To(vm => vm.CurrentGolfers).Apply(); //I love the new fluent api :)
I think what you want to do is actual a data-bound View, rather than a custom binding.
This is covered in this question - Custom bindable control in a MvvmCross Touch project
Basically what you need to do is to add a collection of 'Bindings' and the 'DataContext' property to your FirstView.
If you do that then you should be able to databind (to DataContext) within FirstView just like you do within any normal MvvmCross view.
Note - this will be much easier to do in v3 as we've added a 'BindingContext' object to assist with exactly this type of operation