I'm using the MapControl for Windows 10 and binding it's Center property to my view model like so:
<Maps:MapControl x:Name="MapControl" Center="{Binding MapCenter, Mode=TwoWay}" />
This works, but I'd like to use a parabolic animation when transitioning the map from one center position to the next when my view model changes. I'd like it to behave the way it would if I had called TrySetViewAsync from my code behind passing a MapAnimationKind:
MapControl.TrySetViewAsync(geopoint, 13, null, null, Maps.MapAnimationKind.Bow)
How can I achieve this effect while maintaining separation of concerns?
With the help of MVVMLight Messenger, we can trigger it from the ViewModel easily.
About how to use MVVM Light in Windows 10 Universal App, see my answer in this one.
For example, the following method is placed in MainPage.xaml.cs:
private async void SetMapView(Geopoint point)
{
await MainMap.TrySetViewAsync(point, 13,0,0,Windows.UI.Xaml.Controls.Maps.MapAnimationKind.Bow);
}
Register the MainPage.xaml.cs to the default messenger provided by MVVMLight
Messenger.Default.Register<Geopoint>(this, Constants.SetMapViewToken, SetMapView);
Used a string Constants.SetMapViewToken as a token to identify the message and assigned SetMapView as an action
internal class Constants
{
public static string SetMapViewToken = "SetMapView";
}
Trigger SetMapView method in ViewModel:
Messenger.Default.Send<Geopoint>(MyLocation.Coordinate.Point, Constants.SetMapViewToken);
I've created a completed sample and uploaded to Github.
Enjoy it:)
Related
I'm trying to implement Login system like this:
public Command LoginCommand => new Command(async () =>
{
LoginModel model = new LoginModel("dnetTest", "dnetTest"); // (get value from entry instead of "dnetTest")
if (model.CheckInformation())
{
bool isSuccess = await LoginService.Login(model);
if (isSuccess)
{
await Application.Current.MainPage.DisplayAlert("Пријављивање", "Успешно сте се пријавили", "OK.");
Application.Current.MainPage = new MainPage();
}
}
My LogingPage also have:
<Label Text="Korisničko ime"/>
<Entry x:Name="Entry_Username" Placeholder="Korisničko ime"/>
<Label Text="Lozinka"/>
<Entry x:Name="Entry_Password" Placeholder="Lozinka"/>
<Button Text="Prijavi se" Command="{Binding LoginCommand}"/>
So, my question is how to bind Entry_Username and Entry_Password with LoginModel in LoginViewModel?
And is there any way to bind it without using x:Names?
To be fair; this is a very basic MVVM question that is easy to find, of which the solution is in multiple blogs and pages. But, let me help you get started.
And is there any way to bind it without using x:Names?
The whole point of data binding is that you do not have to make any hard references to these controls. We want to separate the logic from the UI so that we can easily replace one or both without having to touch the other. For instance, say that you want to implement a new design, if you use data binding, you can just show the current properties in the view model (or page model as it is referred to in the Xamarin space as well) if you reference those in your new UI.
If you would have all kind of x:Name references, you would not only have to touch your UI, but also the view model and go through all the references to those fields and replace them as well.
Using data binding promotes reusability and testability mostly.
As for your specific case. I can't see your full code, so there will be some assumptions made here. First, I'm going to assume that your LoginCommand lives in a view model of its own right now. You are already using data binding there, which is good. I don't directly see why you would need a separate model for the view model and the login, possibly your LoginModel is more like a service. Also, I'm assuming you're doing this manually, without the help of an MVVM framework. It's good to know what happens under the hood, but I would recommend looking at using an MVVM framework like FreshMvvm or Prism for example.
The login page that holds your XAML, I will call LoginPage.xaml which should have a LoginPage.xaml.cs code-behind file. In there, go into the constructor and specify this line:
public LoginPage()
{
InitializeComponents();
// This line is relevant here
BindingContext = new LoginViewModel();
}
Seeing that your LoginCommand is already using data binding, this is probably here already.
Now, in your LoginPage.xaml, change your XAML to this:
<Label Text="Korisničko ime"/>
<Entry Text="{Binding Username}" Placeholder="Korisničko ime"/>
<Label Text="Lozinka"/>
<Entry Text="{Binding Password}" Placeholder="Lozinka"/>
<Button Text="Prijavi se" Command="{Binding LoginCommand}"/>
Notice how I removed the x:Name attributes and added the Text attributes on the two Entry controls.
Next, go into your LoginViewModel.cs and add two properties, like this:
public string Username { get; set; }
public string Password { get; set; }
Whenever the text changes in your Entry controls, these properties should contain the value accordingly. Now, you can change the code you posted to something like this:
public Command LoginCommand => new Command(async () =>
{
// Notice how I changed this line
LoginModel model = new LoginModel(Username, Password);
if (model.CheckInformation())
{
bool isSuccess = await LoginService.Login(model);
if (isSuccess)
{
await Application.Current.MainPage.DisplayAlert("Пријављивање", "Успешно сте се пријавили", "OK.");
Application.Current.MainPage = new MainPage();
}
}
This should work for you!
As mentioned, I would recommend looking further into MVVM as a whole and also MVVM frameworks. Here is the official Docs page, a good writeup by Adam Pedley and something I wrote myself a while back.
I would like some feedback to see if I'm using SimpleIoc in the correct way.
The code below works, but I'm not sure if it's best practice.
I have an UWP XAML DocumentPage class on which I want to show an IRpcDocument.
I want to use the DocumentPage for both RpcDocumentA and RpcDocumentB. The user can navigate to both types of IRpcDocument. So the application should be able to switch between the two 'on the fly'.
So I wrote my DocumentPageViewModel
public class DocumentPageViewModel : ViewModelBase
{
public IRpcDocument RpcDocument;
public DocumentPageViewModel(IRpcDocument rpcDocument)
{
RpcDocument = rpcDocument;
}
}
And my ViewModelLocator
class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<DocumentPageViewModel>();
}
public DocumentPageViewModel SimpleIoc.Default.Register<DocumentPageViewModel>
{
get
{
return ServiceLocator.Current.GetInstance<SimpleIoc.Default.Register<DocumentPageViewModel>>(Guid.NewGuid().ToString());
}
}
}
When I'm navigating to the DocumentPage I call:
SimpleIoc.Default.Register<IRpcDocument , RpcDocumentA>();
await NavigationService.NavigateAsync(typeof(DocumentPage), DocumentIdParameter);
The app then navigates to the DocumentPage, constructs the RpcDocumentA, makes the necessary RPC calls to fetch the data and shows the document.
The first line tells the IoC framework it should expect an RpcDocumentA in its constructor, the second one triggers navigation. So in this case, im not registering the interface in the static ViewModelLocator().
So for each time I navigate I call SimpleIoc.Default.Register<IRpcDocument , RpcDocumentA> or SimpleIoc.Default.Register<IRpcDocument , RpcDocumentB>
This works, but is this the right way to do this? I suspect it's not.
I'd like to know what is the best solution to manipulate application settings in a cross-platform way.
In iOS we can change the settings outside the app in the settings screen, but we don't have that in windows phone and android.
So, my idea is to create a normal page/screen inside the app that shows all my application settings and have an interface with Save() and Get() methods that I can implement specific per device using DependencyServices.
Is this the right way to do it?
The Application subclass has a static Properties dictionary which can be used to store data. This can be accessed from anywhere in your Xamarin.Forms code using Application.Current.Properties.
Application.Current.Properties ["id"] = someClass.ID;
if (Application.Current.Properties.ContainsKey("id"))
{
var id = Application.Current.Properties ["id"] as int;
// do something with id
}
The Properties dictionary is saved to the device automatically. Data added to the dictionary will be available when the application returns from the background or even after it is restarted. Xamarin.Forms 1.4 introduced an additional method on the Application class - SavePropertiesAsync() - which can be called to proactively persist the Properties dictionary. This is to allow you to save properties after important updates rather than risk them not getting serialized out due to a crash or being killed by the OS.
https://developer.xamarin.com/guides/cross-platform/xamarin-forms/working-with/app-lifecycle/
Xamarin.Forms plugin which uses the native settings management.
Android: SharedPreferences
iOS: NSUserDefaults
Windows Phone: IsolatedStorageSettings
Windows Store / Windows Phone RT: ApplicationDataContainer
https://github.com/jamesmontemagno/Xamarin.Plugins/tree/master/Settings
I tried using the Application.Current.Properties Dictionary and had implementation problems.
A solution that worked with very little effort was James Montemagno's Xam.Plugin.Settings NuGet. GitHub Installing the NuGet automagically creates a Helpers folder with Settings.cs. To create a persisted setting you do:
private const string QuestionTableSizeKey = "QuestionTableSizeKey";
private static readonly long QuestionTableSizeDefault = 0;
and
public static long QuestionTableSize
{
get
{
return AppSettings.GetValueOrDefault<long>(QuestionTableSizeKey, QuestionTableSizeDefault);
}
set
{
AppSettings.AddOrUpdateValue<long>(QuestionTableSizeKey, value);
}
}
Access and setting in the app then looks like:
namespace XXX
{
class XXX
{
public XXX()
{
long myLong = 495;
...
Helpers.Settings.QuestionTableSize = myLong;
...
long oldsz = Helpers.Settings.QuestionTableSize;
}
}
}
I am currently developing an eclipse plugin which displays DOT-Graphs. For this purpose I make use of this plugin. However, I have no idea how to actually display the graph which I built. I want to display it in the middle of the eclipse window as an Editor.
To get this done I created a custom Editor class which needs some code in its createPartControl(Composite) code in order to make use of the DotGraphView which is provided by the plugin.
The question is, how can I display this DotGraphView?
The code of my Editor looks like this:
#Override
public void createPartControl(Composite container) {
DotImport importer = new DotImport(TEST_GRAPH);
Graph graph = importer.newGraphInstance();
DotGraphView dotGraphView = new DotGraphView();
dotGraphView.setGraph(graph);
// add dotGraphView as a child to container and display it
// What todo here?
}
To use the graph in your own custom view, check out the implementation of ZestFxUiView, the superclass of DotGraphView. You could probably subclass ZestFxUiView and call setGraph with your graph object.
My current set up:
Xamarin Forms, consisting of iOS, Android, WP app and shared PCL.
Using MVVM Light to keep a nice separation of concerns.
Brief intro into what I want to achieve. I want to have a Base page that has a Cancel and Next button. On pressing the Next button Content is loaded dynamically within that base page.
Xaml View:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="LogInPresenterView">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Cancel" Priority="0" Order="Primary" Command="{Binding Cancel}"></ToolbarItem>
<ToolbarItem Text="Next" Priority="1" Order="Primary" Command="{Binding Next}"></ToolbarItem>
</ContentPage.ToolbarItems>
</ContentPage>
ViewModel Code:
public class LogInPresenterViewModel : ViewModelBase
{
public LogInPresenterViewModel() {}
private RelayCommand _next;
public RelayCommand Next
{
get
{
return _next ?? (_next = new RelayCommand(async () => await DoNext()));
}
}
private async Task DoNext()
{
// IN HERE I WOULD LIKE TO DYNCAMICALLY LOAD CONTENT / VIEWS
}
}
Usually you would have a StackLayout etc before the element. However, on clicking the Next Toolbar Item I want to dynamically load content (that has a viewmodel).
So maybe my ICommand for my next button checked to see what the current content type was, and depending on that I would load another bit of content.
The scenario would be, the base page would load along with the first bit of content - Enter Email and Password. User enters that then clicks on next, if all ok, the content is replaced with the option to enter a security code, keeping the base Close and Next buttons at the top.
Hopefully this makes sense. I know what I want to do in my head, I just don't know how to translate that into Xamarin Forms...
Ok,
So first job is to create your region service in your PCL. This will look something like this:
using System;
using System.Collections.Generic;
namespace xxx
{
public class RegionService : IRegionService
{
private Dictionary<string, object> _regionDictionary;
public RegionService ()
{
_regionDictionary = new Dictionary<string, object> ();
}
#region IRegionService implementation
public bool RegisterRegion (string regionName, object regionObject)
{
object region = null;
_regionDictionary.TryGetValue (regionName, out region);
if (region != null)
_regionDictionary [regionName] = regionObject;
else
_regionDictionary.Add (regionName, regionObject);
return true;
}
public object ResolveRegion (string regionName)
{
object region = null;
_regionDictionary.TryGetValue (regionName, out region);
if (region == null)
throw new RegionServiceException ("Unable to resolve region with given name");
return region;
}
#endregion
}
}
This when you create your page with the dynamic content register your dynamic contentview in your code behind:
ContentView contentView = this.FindById<ContentView>("myContentView");
regionService.RegisterRegion("DynamicView", contentView);
You'll need to create an interface for your views and pages to use to indicate which region they wish to be presented in:
using System;
namespace xxx
{
public interface IRegionView
{
string GetRegionName ();
}
}
Then in your code behind for your view implement this interface to return the name of the region to display in.
You now need a custom presenter to use this region code. I use MVVMCross, so the details will vary for the MVVM implementation you are using, but essentially something like this is what you need:
public async static Task PresentPage(Page page)
{
if (typeof(IRegionView).GetTypeInfo().IsAssignableFrom(page.GetType().GetTypeInfo()))
{
IRegionService regionService = Mvx.Resolve<IRegionService>();
string regionName = (page as IRegionView).GetRegionName();
Page region = regionService.ResolveRegion(regionName) as Page;
if (typeof(IModalPage).GetTypeInfo().IsAssignableFrom(page.GetType().GetTypeInfo()))
await region.Navigation.PushModalAsync(page);
else if (typeof(IPopupPage).GetTypeInfo().IsAssignableFrom(page.GetType().GetTypeInfo()))
region.PushOverlayPage(page);
else if (typeof(NavigationPage).GetTypeInfo().IsAssignableFrom(region.GetType().GetTypeInfo()))
await (region as NavigationPage).PushAsync(page);
}
}
I hope this is useful for you :)
So if this was me. I would create a region service where the contentview registers a unique region name.
Content would then be marked to use that region, and a custom presenter can be used to show the view model's content in the appropriate region.
I'm on my phone whilst travelling at the moment but I can post some code later on if that helps :)
Tristan
You can dynamically load Xamarin Forms UI with XAML.
Old Answer:
This can be achieved with the use of the LoadFromXaml method. It works in the same was as XamlReader.Load in Silverlight/WPF. It is a hidden method that can be only accessed through reflection. There is an article on how to do it here:
http://www.cazzulino.com/dynamic-forms.html
But, I would like to ask to you go to this feature request at Xamarin and ask that the method be made public so that it becomes a fully supported feature:
https://forums.xamarin.com/discussion/comment/252626