Bing Maps WPF in MVVM pattern. Center not working? - c#

Hello Stackoverflow community!
I've started recently building a flight dashboard, including Bing Maps WPF Control in MVVM patters (at least as much, as possible) for Windows PC devices.
After seeking the web for a while I was able to deliver CredentialsProvider based on the key in app.config and moved to implement automatic centering of the Bing's control map based on the current device's position.
The XAML:
<m:Map ZoomLevel="16" Mode="Aerial" CredentialsProvider="{Binding BingMapsCredentials}" Grid.Row="1" Grid.ColumnSpan="2" Center="{Binding DevPosition}"/>
The ViewModel:
private readonly CredentialsProvider bingMapsCredentials = new ApplicationIdCredentialsProvider(ConfigurationSettings.AppSettings.Get("BingMapsKey"));
private readonly double nDefaultLatitude = double.Parse(ConfigurationSettings.AppSettings.Get("DefaultLongitude"), System.Globalization.CultureInfo.InvariantCulture);
private readonly double nDefaultLongitude = double.Parse(ConfigurationSettings.AppSettings.Get("DefaultLatitude"), System.Globalization.CultureInfo.InvariantCulture);
private GeoCoordinate nDevicePosition;
public CredentialsProvider BingMapsCredentials
{
get { return bingMapsCredentials; }
}
public Location DevPosition
{
get { return new Location(nDeviceLat, nDeviceLon); }
}
public double nDeviceLon
{
get
{
if (nDevicePosition.IsUnknown)
return nDefaultLongitude;
else
return nDevicePosition.Longitude; }
set { nDevicePosition.Longitude = value; }
}
public double nDeviceLat
{
get
{
if (nDevicePosition.IsUnknown)
return nDefaultLatitude;
else
return nDevicePosition.Latitude;
}
set { nDevicePosition.Latitude = value; }
}
While binding the CredentialsProvider works fine, setting the Center location does not at all. The map is displayed correctly but somewhere in the middle of nowhere. The debugger shows that there is no call on the Get property on location. There are also no WPF warning/error traces in the Output Window.
Am I missing something here?
Any help is appreciated.
P.

You haven't bound nDeviceLat and nDeviceLon to the DevPosition. As such this will only pull in the correct values when the get method is called. If the position is moved there is nothing to trigger the UI to update. Try doing something like this:
public Location DevPosition { get; set; }
public double nDeviceLon
{
get
{
if (nDevicePosition.IsUnknown)
return nDefaultLongitude;
else
return nDevicePosition.Longitude; }
set
{
nDevicePosition.Longitude = value;
DevPosition = new Location(nDeviceLat, nDeviceLon);
}
}
public double nDeviceLat
{
get
{
if (nDevicePosition.IsUnknown)
return nDefaultLatitude;
else
return nDevicePosition.Latitude;
}
set {
nDevicePosition.Latitude = value;
DevPosition = new Location(nDeviceLat, nDeviceLon);
}
}

This seems to be a common problem. There's a couple issues here, but basically you want to use setView() rather than setting to Center.
Here's two excellent articles on working with Bing maps in WPF, which also explains the center property more:
http://www.mobilemotion.eu/?p=1077&lang=en
http://visualstudiomagazine.com/Articles/2012/04/01/Map-Your-Apps.aspx?Page=2
Relates to/Possible Duplicate of:
Center and Zoom on Bing Maps WPF
Hope this helps!

Related

Windows 10 (Universal Windows App) data validation

I was trying to figure out how to do the data validation under UWP, but according to what I have found out, there is basically nothing I can implemented yet.
Due to that I tried to implement my custom validation logic. Problem I have now is, that I am showing error information on one TextBlock rather than directly under the specific TextBox which contains data error.
This is what I do at the moment:
public class Customer : ViewModel
{
private string _Name = default(string);
public string Name { get { return _Name; } set { SetProperty(ref _Name, value); OnPropertyChanged("IsValid"); } }
private string _Surname = default(string);
public string Surname { get { return _Surname; } set { SetProperty(ref _Surname, value); OnPropertyChanged("IsValid"); } }
private DateTime _DateOfBirth = default(DateTime);
public DateTime DateOfBirth { get { return _DateOfBirth; } set { SetProperty(ref _DateOfBirth, value); OnPropertyChanged("IsValid"); } }
public int ID { get; set; }
public bool IsValid
{
get
{
//restart error info
_ErrorInfo = default(string);
if (string.IsNullOrWhiteSpace(Name))
_ErrorInfo += "Name cannot be empty!" + Environment.NewLine;
if (string.IsNullOrWhiteSpace(Surname))
_ErrorInfo += "Surname cannot be empty!" + Environment.NewLine;
//raise property changed
OnPropertyChanged("ErrorInfo");
return !string.IsNullOrWhiteSpace(Name) &&
!string.IsNullOrWhiteSpace(Surname);
}
}
private string _ErrorInfo = default(string);
public string ErrorInfo { get { return _ErrorInfo; } set { SetProperty(ref _ErrorInfo, value); } }
}
Question:
How to adjust my code, so that rather than having one label with all error information, I can assign label under each textbox and display validation error there? Should I use Dictionary for this? If yes, how can I bind it to my View?
I have quickly become a fan of using Prism, see this wonderful demonstration User input validation with Prism and data annotations on the UWP.
Its better than anything I could type here.
You can make a flyout inside a textbox.
As soon as the textbox loses focus with wrong input, the flyout shows up .
You can set the placament of the flyout on top/bottom/side of the textbox.
Best of luck !
The problem with Prism is that it uses a string indexer. But Bind in uwp just will not allow string indexes... Integers only! There are also some key features lacking such as coordination between entity view models and between them and the context.
I've done some R&D and it seems that the following are key elements of a good validator in uwp
- use of strings as the binding target, to avoid dropping conversion exceptions
- tracking conversion errors separately from validation errors
- base class for the validating view model AND automatically generated derived classes specifying the property names
- events to tie multiple view models together so that multiple parts of the ui are kept consistent
- centralized error count and save / revert ability associated with the context
Anything out there that can do that? If so then I haven't found it yet.
sjb

MvvmCross binding between ViewModel and View doesn't work for buttons

I am using Xamarin to develop an App for Android in C# using MvvmCross.
The app compiles and runs fine when I am using one of my View Models which is bind to a seekbar, but doesn't with the View Models that I want to bind with a button. All my View models have a very similar structure (pretty much identical besides the names).
A sample of my setup is presented below:
My View Model looks something like:
public interface ISomething
{
int MethodSomething(int i);
}
public class ChangeSomethingViewModel : MvxViewModel
{
private readonly ISomething _somethign;
public ChangeSomethingViewModel(ISomething something)
{
_something = something;
}
public override void Start()
{
Terminate_Something = "hello";
base.Start();
}
.
.
.
public string terminate;
public string Terminate_Something
{
get { return terminate; }
set { terminate= "pressed"; RaisePropertyChanged(() => Terminate_Something); }
}
I then have another file with MethodSomething(int i)
public class ChangeSomething : ISystem
{
public int MethodSomething(int i)
{
.
.
.
}
}
In setup.cs
protected override IMvxApplication CreateApp()
{
Mvx.RegisterSingleton<IMvxAppStart>(new MvxAppStart<ChangeSomethingViewModel>());
return new App();
}
protected override void InitializeFirstChance()
{
Mvx.RegisterSingleton<ISomething>(new ChangeSomething());
base.InitializeFirstChance();
}
In my Main.axml file I have a button
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
local:MvxBind="Click Terminate_Something" />
When I run the app Terminate_Something is set and displayed in the button, as I would expect. However, if I place a breakpoint in Terminate_Something and click on the button no call to Terminate_Something is made.
I should probably mention that I can see in the log the following every time I touch the button.
04-17 12:57:14.070 D/GestureDetector( 5928): [Surface Touch Event] mSweepDown False, mLRSDCnt : -1 mTouchCnt : 7 mFalseSizeCnt:0
I can't quite understand what can be the problem as my other view model works great linking to a slider. When I press on the slider (seekbar) moves and the calls Set, the corresponding log is
04-17 12:57:45.060 D/ProgressBar( 5928): setProgress = 43, fromUser = true
04-17 12:57:45.060 D/ProgressBar( 5928): mProgress = 50mIndeterminate = false, mMin = 0, mMax = 70
Thank you in advance
It looks like the problem is because you are not binding to an ICommand.
Also, I might be missing it somewhere, but you should also specify in Terminate_Something MethodSomething, no?
This should fix it: In your View Model change your Terminate_Something property to
private MvxCommand terminate;
public System.Windows.Input.ICommand Terminate_Something
{
get
{
terminate = terminate ?? new MvxCommand(MethodSomething);
return terminate;
}
}
Since you mentioned that the SeekBar is working didn't you do something like?
set { terminate= "pressed"; RaisePropertyChanged(() => Terminate_Something); MethodSomething();}
In any case Stuart has a video pretty much on this very thing in N=5
EDIT:
Obviously, you can't set terminate to "hello" in start()... depending on what you are trying to do with the button you can bind the text "hello" of the button to a different property.

Displaying and updating table thats boxed in a class in my view

Im having issues getting data to appear in a datagrid in my view.
public class ComputerViewModel: ObservableObject
{
private ObservableCollection<ComputerModel> _ComputerInformation;
public ObservableCollection<ComputerModel> ComputerInformation {
get { return _ComputerInformation; }
set {
_ComputerInformation = value;
NotifyPropertyChanged("ComputerInformation");
}
}
public ComputerViewModel()
{
ComputerInformation = new ObservableCollection<ComputerModel>();
ComputerModel computer = new ComputerModel();
computer.start("DA2968");
ComputerInformation.Add(computer);
}
}
public class ComputerModel: ObservableObject
{
private DataTable _ProcessHistory;
public DataTable ProcessHistory {
get { return _ProcessHistory; }
set { _ProcessHistory = value; NotifyPropertyChanged("ProcessHistory"); }
}
}
View:
<DataGrid DataContext="{Binding Source=ComputerInformation}" ItemsSource="{Binding ProcessHistory}"/>
im not sure its even possible.
Basically i have code that monitors process creation and deletion on multiple computers.
I intitate a new model for each computer and would like to store them in the ViewModel.
which is working fine. im just having difficulty showing the information on the screen. can anyone please help.
ObservableObject is where my INotifyPropertyChanged and DelegateCommand is located.
ComputerModel is where the code for monitoring the creation and deletion of process is located. this is all working fine and storing in the DataTable fine. the data is there i just cannot display it.
Thanks

How do you correctly store a custom object in isolated storage using C#?

Working on a sideproject with WP8, but having trouble getting IsolatedStorage working. I have looked at dozens of posts seemingly asking the same question, but I haven't been able to get any of the solutions to work. The application is a simple task organizer where I have created my own Task Objects, one being a Summary Task and each SummaryTask containing a list of BasicTasks. I have tried using XMLSerializing only to run into problems because I was using an ObservableCollection. Thought I could change the collection to a Subclass of INotifyPropertyChanged but that didn't work either. Quite frankly, I'm still getting the hang of the different between the two anyways. So anyways, my latest attempt involves trying to use IsolatedStorage Settings and that didn't work either. Here is my class definition:
class SummaryTask : TaskItem
{
public List<BasicTask> children = new List<BasicTask>();
private string sumTaskName;
private int sumTaskId;
public SummaryTask()
{
}
public SummaryTask(string name, int id)
{
sumTaskName = name;
sumTaskId = id;
}
public string SumTaskName
{
get { return sumTaskName; }
set { sumTaskName = value; }
}
public int SumTaskId
{
get { return sumTaskId; }
set { sumTaskId = value; }
}
public void addTask(string taskName, string taskText, int taskId){
children.Add(new BasicTask(taskName, taskText, taskId));
}
public List<BasicTask> CHILDREN
{
get { return children; }
}
}
}
I create a list of this SummaryTask in a Global variable and use it throughout my pages for easy access. Here is what the beginning of my MainPage.xaml.cs file looks UPDATED:
public MainPage()
{
InitializeComponent();
BackKeyPress += OnBackKeyPressed;
if (Global.settings.Contains("list"))
{
Global.list = (List<SummaryTask>)Global.settings["list"];
}
else
{
Global.list = new List<SummaryTask>();
}
}
Guidance on the poor quality of my code and how to improve it is also accepted. Thank you.
Edit: The exception indicates that an item with the same key has already been created. The stacktrace doesn't show anything of importance in this case. I should also note that the exception is thrown after adding an object to the list and trying to save it, not while compiling.
The piece of code I am using to try to save to the Isolated Storage is here, it triggers when I navigate to MainPage.xaml:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
resultList.SelectedItem = null;
Global.settings["list"] = Global.list;
Global.settings.Save();
}
No exceptions anymore, but exiting the app and reentering isn't pulling up any saved data.
The problem with Add is very simple to fix - just use the indexer instead, which allows you to overwrite an entry with the same name:
settings["list"] = Global.list;
That won't fix the Save call... but you'd need to give more details about what exception (not just "it tells me", the full exception details) to help us help you more.

Passing on variables from ViewModel to another View (MVVMCross)

For the past couple of weeks I've been working on developing a cross platform app (IOS/Android/WP7) using the MVVMCross framework. Today I ran into a problem I don't really know how to solve, so hopefully you can push me in the right direction.
In the IOS I have the following construction for navigating to another page (the code below is located in a ViewModel):
KeyValuePair<string,string> kvpAct1 = new KeyValuePair<string, string>("short", ".countertest5");
public IMvxCommand BeckhoffActuator1
{
get
{
return new MvxRelayCommand<Type>((type) => this.RequestNavigate<Beckhoff.BeckhoffActuatorViewModel>(kvpAct1));
}
}
When this IMvxCommand is fired (button pressed) the next View is loaded, in this case the BeckhoffActuatorViewModel. In the code of the BeckhoffActuatorView I use the keyvaluepair from above:
public class BeckhoffActuatorView : MvxTouchDialogViewController<BeckhoffActuatorViewModel>
{
ICollection<string> icol;
public BeckhoffActuatorView(MvxShowViewModelRequest request) : base(request, UITableViewStyle.Grouped, null, true)
{
icol = request.ParameterValues.Values;
}
public override void ViewDidLoad()
{
//Code
}
}
This construction is working fine in IOS, but I would like to use the same construction in my android App.
The code in the ViewModel hasn't changed since that's the whole idea of MVVM. But the code of the BackhoffActuatorView is different for Android:
public class BeckhoffActuatorView : MvxBindingActivityView<BeckhoffSensorViewModel>
{
public ICollection<string> icol;
public BeckhoffActuatorView()
{
Debug.WriteLine("Standard");
}
public BeckhoffActuatorView(MvxShowViewModelRequest request)
{
Debug.WriteLine("Custom");
icol = request.ParameterValues.Values;
}
protected override void OnViewModelSet()
{
SetContentView(Resource.Layout.BeckhoffActuatorView);
}
}
The code above isn't working, the MvxBindingActivityView doesn't seem to implement something similar to the ViewController I use in IOS. The code only come in the standard constructor, and when I leave that one out completely it won't compile/run.
Does anyone know know I can access the keyvaluepair I send with the RequestNavigate? Thank you!
MVVMCross is very convention based - and it works on the idea of passing messages between ViewModels wherever possible.
If you navigate to a ViewModel using:
KeyValuePair<string,string> kvpAct1 = new KeyValuePair<string, string>("short", ".countertest5");
public IMvxCommand BeckhoffActuator1
{
get
{
return new MvxRelayCommand<Type>((type) => this.RequestNavigate<Beckhoff.BeckhoffActuatorViewModel>(kvpAct1));
}
}
then you should be able to pick that up in the BeckhoffActuatorViewModel using the constructor:
public class BeckhoffActuatorViewModel : MvxViewModel
{
public BeckhoffActuatorViewModel(string short)
{
ShortValue = short;
}
private string _shortValue;
public string ShortValue
{
get
{
return _shortValue;
}
set
{
_shortValue = value;
FirePropertyChanged("ShortValue");
}
}
}
And your views can then access ViewModel.ShortValue (for iOS this can be done after base.ViewDidLoad(), for Android after OnCreate() and for WP7 after OnNavigatedTo)
For an example of this, take a look at the TwitterSearch example:
https://github.com/slodge/MvvmCrossTwitterSearch
This has a HomeViewModel which calls navigate using:
private void DoSearch()
{
RequestNavigate<TwitterViewModel>(new { searchTerm = SearchText });
}
and a TwitterViewModel which receives the searchTerm using the constructor:
public TwitterViewModel(string searchTerm)
{
StartSearch(searchTerm);
}
Please note that only strings are allowed in this message passing at present - but you can always serialise your own objects using JSON.Net - or you can extend the framework - it's open source.
Please note that only strings, ints, doubles and bools are allowed in this constructor parameter passing at present - this is due to serialisation requirements for Xaml Urls and for Android Intents. If you want to experiment with navigation using your own custom serialised objects, then please see http://slodge.blogspot.co.uk/2013/01/navigating-between-viewmodels-by-more.html.
Also, note that if you want to use the anonymous object navigation (RequestNavigate<TwitterViewModel>(new { searchTerm = SearchText });) then you will need to make sure that an InternalsVisibleTo attribute is set - see https://github.com/slodge/MvvmCrossTwitterSearch/blob/master/TwitterSearch.Core/Properties/AssemblyInfo.cs:
[assembly: InternalsVisibleTo("Cirrious.MvvmCross")]
Further... not for the faint-hearted... and this isn't "good mvvm code"... but if you really want/need to access the MvxShowViewModelRequest data inside an Android activity, then you can extract it from the incoming Intent - there's an Extras string containing the request (see the deserialisation in CreateViewModelFromIntent in https://github.com/slodge/MvvmCross/blob/master/Cirrious/Cirrious.MvvmCross/Android/Views/MvxAndroidViewsContainer.cs)

Categories