How to pass textBlock control to a Class - windows-phone-7

say I have a textBlock control and I want to pass it to a class which controls the textBlock to display certain Message.
1) When I call a method in the class, I want textBlock to show message. Example " Checking connection...."
2) When the method complete the required task, the textBlock visibility become collapsed.
In the XAML : I have
a) textBlock name=textBlockMsg
b) a Button to call the class
Appreciate your help.
-- Update :
This class file inside project
public class GeoCalculation
{
public GeoCalculation() { }
public void CalculateDistance()
{
//- Begin -- want the textBlockMsg show : in progress......
--code
//-- when end-----, textBlockMsg visibility becom collapse
}
}

If you named you TextBox in the XAML with textBlockMsg, this will work
Edit
// I will not implement the whole INotifyPropertyChanged check how to do to it : implement
public class CalculationClass : INotifyPropertyChanged
{
public void CalculateDistance()
{
TextToBeBound = "in progress..."
--code
VisibilityToBeBound = Collapsed;
}
public string TextToBeBound
{ //... insert the implement of this property + NotifyPropertyChanged
get {...}
set {...}
}
public Visibility VisibilityToBeBound
{ //... insert the implement of this property + NotifyPropertyChanged
get {...}
set {...}
}
}
Then in the XAML add this :
<TextBlock x:Name="txtBlocMsg" Visibility={"Binding VisibilityToBeBound"} Text={Binding TextToBeBound"}/>
Don't forget to set the DataContext of the UI to your class (in my case CalculationClass
You should be good to go. If all this was new. I recommend you read about data Binding + MVVM pattern.
Edit
It's bad practice to pass UI element to model/business classes. You should use the MVVM pattern.
Hope this helps.

You can have a parameter to pass the TextBock:
public void CalculateDistance(TextBlock tb)
{
tb.Text = "in progress..."
--code
tb.Visibility = Visibility.Collapsed;
}
You coud use a the constructor of your class to inject the textblock it should handle
public class GeoCalculation
{
private TextBlock _tb;
public GeoCalculation(TextBlock tb)
{
_tb = tb;
}
public void CalculateDistance()
{
_tb.Text = "in progress..."
//code
_tb.Visibility = Visibility.Collapsed;
}
}
A ViewModel and using DataBinding would be better by the way!
There you could use our class (method) to provice the text for the ui (textbox)
But be aware:
There is a .net way to do this. The GeoCoordinate class contains a method "GetDistanceTo" to calculate the distance between two geo points. See http://msdn.microsoft.com/en-us/library/system.device.location.geocoordinate.getdistanceto.aspx .

Related

Create Behavior from Map Property in Control - Xamarin Forms MVVM Prism ArcGIS

I'm trying to show a callout when navigating from View A (form view) to View B (map view). I pass the record detail for the callout from View A to View B and it's bound to viewmodel of View B.
I would like to access the LoadStatusChanged event on the Map property of the Esri MapView control while adhering to MVVM architecture. Here is what my control looks like:
<esri:MapView x:Name="mapViewMain"
Map="{Binding MainMap}"
Grid.Row="0"
InteractionOptions="{Binding MapViewOptions}"
GraphicsOverlays="{Binding GraphicsOverlays}">
<esri:MapView.Behaviors>
<bh:ShowCalloutOnTapBehavior CalloutClickCommand="{Binding GoToDetailCommand}" />
<bh:ShowCalloutOnDataReceivedBehavior MeterMasterRequest="{Binding RequestParameters}" Map="{Binding MainMap}" />
</esri:MapView.Behaviors>
</esri:MapView>
I think I need to create a behavior that will take in the Map, wait for it to finish load, then show callout (results of MeterMasterRequest) on MapView (the ShowCallout method is on MapView control).
public class ShowCalloutOnDataReceivedBehavior : BehaviorBase<MapView>
{
public static readonly BindableProperty MeterMasterRequestProperty =
BindableProperty.Create(nameof(MeterMasterRequest), typeof(MeterMasterRequest), typeof(ShowCalloutOnDataReceivedBehavior));
public static readonly BindableProperty MapProperty =
BindableProperty.Create(nameof(Map), typeof(Map), typeof(ShowCalloutOnDataReceivedBehavior));
public MeterMasterRequest MeterMasterRequest
{
get { return (MeterMasterRequest)GetValue(MeterMasterRequestProperty); }
set { SetValue(MeterMasterRequestProperty, value); }
}
public Map Map
{
get { return (Map)GetValue(MapProperty); }
set { SetValue(MapProperty, value); }
}
How can I bind to the Map event from here? I don't know how I can get BehaviorBase to be of type Map. I seem to only be able to set behaviors at MapView level.
You can access the Map from the MapView.Map property and the MapView is passed in to the OnAttachedTo and OnDetachingFrom methods of the Behavior class. I see you have a BehaviorBase, which hopefully has the OnAttachedTo and OnDetachingFrom overrides still marked as protected virtual so you can override them in your ShowCalloutOnDataReceivedBehavior class
Override the OnAttachedTo method and then subscribe to the LoadStatusChanged event, override OnDetachingFrom so you can unsubscribe like so:
protected override void OnAttachedTo(MapView bindable)
{
base.OnAttachedTo(bindable);
bindable.Map.LoadStatusChanged += Map_LoadStatusChanged;
}
protected override void OnDetachingFrom(MapView bindable)
{
base.OnDetachingFrom(bindable);
bindable.Map.LoadStatusChanged -= Map_LoadStatusChanged;
}
private void Map_LoadStatusChanged(object sender, Esri.ArcGISRuntime.LoadStatusEventArgs e)
{
// Do stuff when LoadStatusChanged event is fired on the Map
}

Is it okay to include methods in a view model or should they be placed in another part of the code?

My Xamarin Forms ViewModel looks like this:
public class CFSPageViewModel : BaseViewModel
{
#region Constructor
public CFSPageViewModel()
{
PTBtnCmd = new Command<string>(PTBtn);
OnTappedCmd = new Command<string>(OnTapped);
}
#endregion
# region Commands
public ICommand PTBtnCmd { get; set; }
public ICommand OnTappedCmd { get; }
#endregion
#region Methods
private void OnTapped(string btnText)
{
Utils.SetState(btnText, CFS, SET.Cfs);
CFSMessage = Settings.cfs.TextLongDescription();
}
private void PTBtn(string btnText)
{
Utils.SetState(btnText, PT);
SetLangVisible(btnText);
SetLangSelected(btnText);
CFSMessage = Settings.cfs.TextLongDescription();
}
}
I was previously sending a message with MessageCenter to my C# back end code but now have removed MessageCenter so the methods are part of the ViewModel.
Is this a safe thing to do? I heard that MessageCenter messages passing around between ViewModels for everything was not the best of solutions.
Note that here is the way I had been doing it before:
MyPageViewModel.cs
PTBtnCmd = new Command<Templates.WideButton>((btn) =>
MessagingCenter.Send<CFSPageViewModel, Templates.WideButton>(
this, "PTBtn", btn));
MyPage.xaml.cs
MessagingCenter.Subscribe<CFSPageViewModel, Templates.WideButton>(
this, "PTBtn", (s, btn) =>
{
Utils.SetState(btn.Text, vm.PT);
SetLangVisible(btn.Text);
SetLangSelected(btn.Text);
vm.CFSMessage = Settings.cfs.TextLongDescription();
});
Note that methods such as SetLangVisible were also in MyPage.xaml.cs
To add an event handler to your Buttonsimply:
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyProject.Views.MyPage">
<ContentPage.Content>
<StackLayout>
<Button Text="PTBtn" Clicked="Handle_Clicked" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
In code behind:
namespace MyProject.Views
{
public partial class MyPage : ContentPage
{
public MyPage()
{
InitializeComponent();
}
void Handle_Clicked(object sender, EventArgs eventArgs)
{
((Button)sender).BackgroundColor = Color.Blue; // sender is the control the event occured on
// Here call your methods depending on what they do/if they are view related
/*
Utils.SetState(btn.Text, vm.PT);
SetLangVisible(btn.Text);
SetLangSelected(btn.Text);
vm.CFSMessage = Settings.cfs.TextLongDescription();
*/
}
}
}
All the events that can have a event handler assigned to it is listed in yellow with E:
The Command fires first and you can add CanExecute as a second parameter in the constructor - which will also stop both the command and the event handler from being executed.
I would also rename the Command to something like SelectLanguageCommand - to distinguish it from a ui action. That way you can disconnect the button from the command and connect the command to other ui - if you decide you want to change the view in the future. It would also be easier to understand when unit testing.
Is this a safe thing to do? I heard that MessageCenter messages passing around between ViewModels for everything was not the best of solutions.
You could register all your view models with DependencyService
public App()
{
InitializeComponent();
DependencyService.Register<AboutViewModel>();
DependencyService.Register<CFSPageViewModel>();
DependencyService.Register<MyPageViewModel>();
MainPage = new AppShell();
}
Set BindingContext of the views to the instances registered:
public AboutPage()
{
InitializeComponent();
BindingContext = DependencyService.Get<AboutViewModel>();
}
And get the ViewModel instance anywhere you need it. That way you don't have to deal with subscriptions as you need to when using MessagingCenter.
Wether it is safe or not - I am not sure.

ZK generate button with listener pointing to a Composer's method

I'm working with ZK Framework and I need to generate a <listbox> with an 'ListItemRenderer' I'm implementing. The problem is that I need to generate a button inside the renderer pointing to a Composer's method with the onClick event. Here is the code:
ZUL
<window id="mywin" apply="pkg.MyComposer">
<listbox id="mylbx"
model="#{mywin$MyComposer.action}"
itemRenderer="pkg.MyRenderer">
<listhead>
<listheader .../>
...
</listhead>
</listbox>
...
</window>
Composer
package pkg
public class MyComposer extends SelectorComposer<Window> {
#Wire("#mylbx")
private Listbox listbox;
public void action() {
// do some work, added a breakpoint in the first statement
}
Renderer
package pkg
public class MyRenderer implements ListitemRenderer<MyItem> {
#Override
public void render(Listitem item, MyItem data, int index) throws Exception {
// Some rendering...
Listcell actionCell = new Listcell();
this.addButton(actionCell, "Action 1", "btn_action1", index, "50%",
"onClick=mywin$MyComposer.action");
// another button (doesn't matter) ...
actionCell.setParent(item);
}
private void addButton(Listcell parent,
String label,
String id,
int index,
String width,
String forwardAction) {
Button btn = new Button(label);
btn.setId(id + "_" + index);
btn.setClass(id); // Second try
btn.setWidth(width);
ComponentsCtrl.applyForward(btn, forwardAction); // First try
btn.setParent(parent);
}
}
To test if action() is called, I added a breakpoint with my IDE in the first statement of the method, as I say in the comment.
My first try was to add a forward action in addButton(). I took that from another renderer where it was applied to a Span component, but action() was never called.
The second try was to define a class attribute for the button (so every button of the same type gets the same class) and to add an annotation to action() like this:
#Listen("onClick = .btn_action1")
public void actiion() {...}
but the result is the same.
I'm thinking that I need to add an EventListener to the button or to the composer but I don't know how to point to the correct method between the composer and the renderer.
Any help and/or guide is appreciated. Thanks in advance for your answers.
The best way to make this generic is to make a constructor who takes the composer as argument.
Of course, not every composer has that method so you need to create a interface what you will set on the composer.
public interface ButtonListboxRenderer {
void onClickListboxButton(); // of course with return type and arguments you need.
}
Then your renderer :
public class MyRenderer implements ListitemRenderer<MyItem> {
private final ButtonListboxRenderer composer;
public MyRenderer(ButtonListboxRenderer composer) {
this.composer = composer;
}
...
private void addButton(Listcell parent,
String label,
String id,
int index,
String width,
String forwardAction) {
Button btn = new Button(label);
btn.setId(id + "_" + index);
btn.setClass(id); // Second try
btn.setWidth(width);
// add eventlistener to the button and you can point to the method of the composer.
btn.setParent(parent);
}
}

Prism - moving data between viewmodels

I'm struggling to find the best implementation.
I'm using Prism and I have a View (ParentView), which has a small region within it. Depending on the item in a ddl, another smaller view (ChildView) gets injected into the region of the ParentView.
The ChildView will just have some properties which I would like to access from the ParentView.
So I realize I can use a Publish/Subscribe method to move data between viewmodels, but the issue is I have nothing to hang the Publish on. The view is made up of TextBoxes and no event triggers. The ChildView can be vastly different based on the selection of the ddl. I like the clean separation of each ChildView being it's own view injected inside the ParentView.
What is the best way to achieve this?
One solution can be to implement the interface INavigationAware in your viewmodels. After that you can use the methods onNavigatedFrom(), onNavigatedTo() and onNavigatingTo() to register your event.
EDIT:
If you want launch the event when a field in the child is changed you can do something like this:
private string _yourField;
public string YourField
{
get { return _yourField; }
set { SetProperty(ref _yourField, value);
//Here you can launch the event
}
}
In this case when YourField change the event is launched.
I tried a few implementations, but the one that worked was creating a singleton instance of the ChildView (childviewmodel) and then gaining access to the properties through the instance. It may not be pretty, but it works.
private static ChildViewModel _instance = new ChildViewModel ();
public static ChildViewModel Instance { get { return _instance; } }
#region Properties
private ChildModel _childModel= new ChildModel ();
public ChildModel _childModel
{
get { return _instance._childModel; }
set
{
SetProperty(ref _instance._childModel, value);
}
}
private string _childProperty1;
public string ChildProperty1
{
get { return _childProperty1; }
set
{
SetProperty(ref _childProperty1, value);
ChildModel.ChildProperty1= _childProperty1;
}
}
In reality - there were many childproperties. I only listed one for demo. And then I call it in ParentView
var _instance = ChildViewModel.Instance;
var _cm = _instance.ChildModel;
_parentModel = new ParentModel
{
Property1= ParentViewProperty1,
Property2= _cm.ChildProperty1,
};
Hope that helps someone else.

How to implement a custom presenter in a Windows UWP (Xamarin, MvvmCross)

I have the following code in my Android app, it basically uses one page (using a NavigationDrawer) and swaps fragments in/out of the central view. This allows the navigation to occur on one page instead of many pages:
Setup.cs:
protected override IMvxAndroidViewPresenter CreateViewPresenter()
{
var customPresenter = new MvxFragmentsPresenter();
Mvx.RegisterSingleton<IMvxFragmentsPresenter>(customPresenter);
return customPresenter;
}
ShellPage.cs
public class ShellPage : MvxCachingFragmentCompatActivity<ShellPageViewModel>, IMvxFragmentHost
{
.
.
.
public bool Show(MvxViewModelRequest request, Bundle bundle)
{
if (request.ViewModelType == typeof(MenuContentViewModel))
{
ShowFragment(request.ViewModelType.Name, Resource.Id.navigation_frame, bundle);
return true;
}
else
{
ShowFragment(request.ViewModelType.Name, Resource.Id.content_frame, bundle, true);
return true;
}
}
public bool Close(IMvxViewModel viewModel)
{
CloseFragment(viewModel.GetType().Name, Resource.Id.content_frame);
return true;
}
.
.
.
}
How can I achieve the same behavior in a Windows UWP app? Or rather, is there ANY example that exists for a Windows MvvmCross app which implements a CustomPresenter? That may at least give me a start as to how to implement it.
Thanks!
UPDATE:
I'm finally starting to figure out how to go about this with a customer presenter:
public class CustomPresenter : IMvxWindowsViewPresenter
{
IMvxWindowsFrame _rootFrame;
public CustomPresenter(IMvxWindowsFrame rootFrame)
{
_rootFrame = rootFrame;
}
public void AddPresentationHintHandler<THint>(Func<THint, bool> action) where THint : MvxPresentationHint
{
throw new NotImplementedException();
}
public void ChangePresentation(MvxPresentationHint hint)
{
throw new NotImplementedException();
}
public void Show(MvxViewModelRequest request)
{
if (request.ViewModelType == typeof(ShellPageViewModel))
{
//_rootFrame?.Navigate(typeof(ShellPage), null); // throws an exception
((Frame)_rootFrame.UnderlyingControl).Content = new ShellPage();
}
}
}
When I try to do a navigation to the ShellPage, it fails. So when I set the Content to the ShellPage it works, but the ShellPage's ViewModel is not initialized automatically when I do it that way. I'm guessing ViewModels are initialized in MvvmCross using OnNavigatedTo ???
I ran into the same issue, and built a custom presenter for UWP. It loans a couple of ideas from an Android sample I found somewhere, which uses fragments. The idea is as follows.
I have a container view which can contain multiple sub-views with their own ViewModels. So I want to be able to present multiple views within the container.
Note: I'm using MvvmCross 4.0.0-beta3
Presenter
using System;
using Cirrious.CrossCore;
using Cirrious.CrossCore.Exceptions;
using Cirrious.MvvmCross.ViewModels;
using Cirrious.MvvmCross.Views;
using Cirrious.MvvmCross.WindowsUWP.Views;
using xxxxx.WinUniversal.Extensions;
namespace xxxxx.WinUniversal.Presenters
{
public class MvxWindowsMultiRegionViewPresenter
: MvxWindowsViewPresenter
{
private readonly IMvxWindowsFrame _rootFrame;
public MvxWindowsMultiRegionViewPresenter(IMvxWindowsFrame rootFrame)
: base(rootFrame)
{
_rootFrame = rootFrame;
}
public override async void Show(MvxViewModelRequest request)
{
var host = _rootFrame.Content as IMvxMultiRegionHost;
var view = CreateView(request);
if (host != null && view.HasRegionAttribute())
{
host.Show(view as MvxWindowsPage);
}
else
{
base.Show(request);
}
}
private static IMvxWindowsView CreateView(MvxViewModelRequest request)
{
var viewFinder = Mvx.Resolve<IMvxViewsContainer>();
var viewType = viewFinder.GetViewType(request.ViewModelType);
if (viewType == null)
throw new MvxException("View Type not found for " + request.ViewModelType);
// Create instance of view
var viewObject = Activator.CreateInstance(viewType);
if (viewObject == null)
throw new MvxException("View not loaded for " + viewType);
var view = viewObject as IMvxWindowsView;
if (view == null)
throw new MvxException("Loaded View is not a IMvxWindowsView " + viewType);
view.ViewModel = LoadViewModel(request);
return view;
}
private static IMvxViewModel LoadViewModel(MvxViewModelRequest request)
{
// Load the viewModel
var viewModelLoader = Mvx.Resolve<IMvxViewModelLoader>();
return viewModelLoader.LoadViewModel(request, null);
}
}
}
IMvxMultiRegionHost
using Cirrious.MvvmCross.ViewModels;
using Cirrious.MvvmCross.WindowsUWP.Views;
namespace xxxxx.WinUniversal.Presenters
{
public interface IMvxMultiRegionHost
{
void Show(MvxWindowsPage view);
void CloseViewModel(IMvxViewModel viewModel);
void CloseAll();
}
}
RegionAttribute
using System;
namespace xxxxx.WinUniversal.Presenters
{
[AttributeUsage(AttributeTargets.Class)]
public sealed class RegionAttribute
: Attribute
{
public RegionAttribute(string regionName)
{
Name = regionName;
}
public string Name { get; private set; }
}
}
These are the three foundational classes you need. Next you'll need to implement the IMvxMultiRegionHost in a MvxWindowsPage derived class.
This is the one I'm using:
HomeView.xaml.cs
using System;
using System.Diagnostics;
using System.Linq;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using Cirrious.MvvmCross.ViewModels;
using Cirrious.MvvmCross.WindowsUWP.Views;
using xxxxx.Shared.Controls;
using xxxxx.WinUniversal.Extensions;
using xxxxx.WinUniversal.Presenters;
using xxxxx.Core.ViewModels;
namespace xxxxx.WinUniversal.Views
{
public partial class HomeView
: MvxWindowsPage
, IMvxMultiRegionHost
{
public HomeView()
{
InitializeComponent();
}
// ...
public void Show(MvxWindowsPage view)
{
if (!view.HasRegionAttribute())
throw new InvalidOperationException(
"View was expected to have a RegionAttribute, but none was specified.");
var regionName = view.GetRegionName();
RootSplitView.Content = view;
}
public void CloseViewModel(IMvxViewModel viewModel)
{
throw new NotImplementedException();
}
public void CloseAll()
{
throw new NotImplementedException();
}
}
}
The last piece to make this work is the way the actual xaml in the view is set-up. You'll notice that I'm using a SplitView control, and that I'm replacing the Content property with the new View that's coming in in the ShowView method on the HomeView class.
HomeView.xaml
<SplitView x:Name="RootSplitView"
DisplayMode="CompactInline"
IsPaneOpen="false"
CompactPaneLength="48"
OpenPaneLength="200">
<SplitView.Pane>
// Some ListView with menu items.
</SplitView.Pane>
<SplitView.Content>
// Initial content..
</SplitView.Content>
</SplitView>
EDIT:
Extension Methods
I forgot to post the two extension methods to determine if the view declares a [Region] attribute.
public static class RegionAttributeExtentionMethods
{
public static bool HasRegionAttribute(this IMvxWindowsView view)
{
var attributes = view
.GetType()
.GetCustomAttributes(typeof(RegionAttribute), true);
return attributes.Any();
}
public static string GetRegionName(this IMvxWindowsView view)
{
var attributes = view
.GetType()
.GetCustomAttributes(typeof(RegionAttribute), true);
if (!attributes.Any())
throw new InvalidOperationException("The IMvxView has no region attribute.");
return ((RegionAttribute)attributes.First()).Name;
}
}
Hope this helps.
As the link to the blog of #Stephanvs is no longer active I was able to pull the content off the Web Archive, i'll post it here for who ever is looking for it:
Implementing a Multi Region Presenter for Windows 10 UWP and MvvmCross
18 October 2015 on MvvmCross, Xamarin, UWP, Windows 10, Presenter > Universal Windows Platform
I'm upgrading a Windows Store app to the new Windows 10 Universal
Windows Platform. MvvmCross has added support for UWP in v4.0-beta2.
A new control in the UWP is the SplitView control. Basically it
functions as a container view which consist of two sub views, shown
side-by-side. Mostly it's used to implement the (in)famous hamburger
menu.
By default MvvmCross doesn't know how to deal with the SplitView, and
just replaces the entire screen contents with a new View when
navigating between ViewModels. If however we want to lay-out our views
differently and show multiple views within one window, we need a
different solution. Luckily we can plug-in a custom presenter, which
will take care of handling the lay-out per platform.
Registering the MultiRegionPresenter
In the Setup.cs file in your UWP project, you can override the
CreateViewPresenter method with the following implementation.
protected override IMvxWindowsViewPresenter CreateViewPresenter(IMvxWindowsFrame rootFrame)
{
return new MvxWindowsMultiRegionViewPresenter(rootFrame);
}
Using Regions
We can define a region by declaring a
element. At this point it has to be a Frame type because then we can
also show a nice transition animation when switching views.
<mvx:MvxWindowsPage ...>
<Grid>
<!-- ... -->
<SplitView>
<SplitView.Pane>
<!-- Menu Content as ListView or something similar -->
</SplitView.Pane>
<SplitView.Content>
<Frame x:Name="MainContent" />
</SplitView.Content>
</SplitView>
</Grid>
</mvx:MvxWindowsPage>
Now we want to be able when a ShowViewModel(...) occurs to swap out
the current view presented in the MainContent frame.
Showing Views in a Region
In the code-behind for a View we can now declare a MvxRegionAttribute,
defining in which region we want this View to be rendered. This name
has to match a Frame element in the view.
[MvxRegion("MainContent")]
public partial class PersonView
{
// ...
}
It's also possible to declare multiple regions within the same view.
This would allow you to split up your UI in more re-usable pieces.
Animating the Transition between Content Views
If you want a nice animation when transitioning between views in the
Frame, you can add the following snippet to the Frame declaration.
<Frame x:Name="MainContent">
<Frame.ContentTransitions>
<TransitionCollection>
<NavigationThemeTransition>
<NavigationThemeTransition.DefaultNavigationTransitionInfo>
<EntranceNavigationTransitionInfo />
</NavigationThemeTransition.DefaultNavigationTransitionInfo>
</NavigationThemeTransition>
</TransitionCollection>
</Frame.ContentTransitions>
</Frame>
The contents will now be nicely animated when navigating.
Hope this helps, Stephanvs

Resources