i am developing a windows phone 8 app, in which i have to display a pushpin to current location.
while running the code it is showing an exception:
"An exception of type 'System.NullReferenceException' occurred in MapTestApp.DLL but was not handled in user code"
public MainPage()
{
InitializeComponent();
geolocator = new Geolocator();
geolocator.DesiredAccuracy = PositionAccuracy.High;
geolocator.MovementThreshold = 100; // The units are meters.
geolocator.StatusChanged += geolocator_StatusChanged;
geolocator.PositionChanged += geolocator_PositionChanged;
}
void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
Dispatcher.BeginInvoke(() =>
{
pushpin1.DataContext = args.Position.Coordinate;
});
}
.xaml code:
<maps:Map x:Name="MyMap" Center="{Binding}" ZoomLevel="15">
<toolkit:MapExtensions.Children>
<toolkit:Pushpin x:Name="pushpin1" GeoCoordinate="{Binding}">
<toolkit:Pushpin.Template>
<ControlTemplate TargetType="toolkit:Pushpin">
<StackPanel>
<ContentPresenter x:Name="content" Content="{TemplateBinding Content}" HorizontalAlignment="Left"></ContentPresenter>
<Path Data="M0,0L1,1L2,0L2,0L1,0L0,0Z"
Fill="#00AAFF"
Stretch="Fill"
Margin="-2,0"
Height="120"
Width="30"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content.Visibility, Mode=TwoWay}"
HorizontalAlignment="Left"
/>
</StackPanel>
</ControlTemplate>
</toolkit:Pushpin.Template>
</toolkit:Pushpin>
</toolkit:MapExtensions.Children>
</maps:Map>
Here is the link to the exception screen, it shows pushpin as null object: https://docs.google.com/file/d/0By0Y-Dca1cKjYXp4T3ctV1hLUEk/edit?usp=sharing
Please try this attached property
public static class PushPinExtension
{
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.RegisterAttached("ItemsSource",
typeof(IEnumerable), typeof(PushPinExtension),
new PropertyMetadata(OnPushPinPropertyChanged));
private static void OnPushPinPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement)d;
var pushpin = MapExtensions.GetChildren((Map)uie).OfType<MapItemsControl>().FirstOrDefault();
if (pushpin != null) pushpin.ItemsSource = (IEnumerable)e.NewValue;
}
public static IEnumerable GetItemsSource(DependencyObject obj)
{
return (IEnumerable)obj.GetValue(ItemsSourceProperty);
}
public static void SetItemsSource(DependencyObject obj, IEnumerable value)
{
obj.SetValue(ItemsSourceProperty, value);
}
}
dp:PushPinExtension.ItemsSource="{Binding Path=PushpinCollection}"
Related
My problem is column row is not getting selected/highlighted on window when user presses any letter key on Keyboard.
I tried following code but when I use DataGridTextSearchBehaviour then SelectionChanged="DataGrid_SelectionChanged" is not working.
<DataGrid ItemsSource="{Binding DrugItems}" SelectedItem="{Binding SelectedDrugItem}"
SelectionChanged="DataGrid_SelectionChanged" Grid.Row="1" Grid.RowSpan="2" Grid.Column="0"
Grid.ColumnSpan="2" HorizontalScrollBarVisibility="Visible">
<i:Interaction.Behaviors>
<b:DataGridTextSearchBehavior />
</i:Interaction.Behaviors>
<DataGrid.Columns>
<DataGridTextColumn Header="{DynamicResource lang.ui.DrugName}" Binding="{Binding Brand_Name}" Width="Auto"
ElementStyle="{StaticResource verticalCenter}">
</DataGridTextColumn>
<DataGridTextColumn Header="{DynamicResource lang.ui.DoseForm}" Binding="{Binding Dose_Form}"
ElementStyle="{StaticResource verticalCenter}"/>
<DataGridTextColumn Header="{DynamicResource lang.ui.UOM}" Binding="{Binding UOM}"
ElementStyle="{StaticResource verticalCenter}"/>
</DataGrid.Columns>
</DataGrid>
I have created DataGridTextSearchBehavior this seperate class ,and if I set AssociatedObject.SelectionUnit = DataGridSelectionUnit.FullRow; then problem gets resolved but instead of selecting column full row gets selected which I dont need. I need to select/Highlight column wise data.
using System;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Interactivity;
namespace eIVFRes.Behaviors
{
public class DataGridTextSearchBehavior : Behavior<DataGrid>
{
private DataGridSelectionUnit _oldUnit;
public string TextPath
{
get => TextSearch.GetTextPath(AssociatedObject);
set => TextSearch.SetTextPath(AssociatedObject, value ?? string.Empty);
}
protected override void OnAttached()
{
// <Setter Property="IsTextSearchEnabled" Value="True" />
//<Setter Property="IsTextSearchCaseSensitive" Value="False" />
base.OnAttached();
AssociatedObject.IsTextSearchEnabled = true;
AssociatedObject.IsTextSearchCaseSensitive = false;
_oldUnit = AssociatedObject.SelectionUnit;
AssociatedObject.SelectionUnit = DataGridSelectionUnit.Cell;
AssociatedObject.SelectedCellsChanged += DataGrid_OnSelectedCellsChanged;
}
protected override void OnDetaching()
{
AssociatedObject.IsTextSearchEnabled = false;
AssociatedObject.SelectionUnit = _oldUnit;
AssociatedObject.SelectedCellsChanged -= DataGrid_OnSelectedCellsChanged;
base.OnDetaching();
}
private void DataGrid_OnSelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{column row is not getting selected/highlighted on window when user presses any letter key on Keyboard.
if (!ReferenceEquals(sender, AssociatedObject))
return;
if (!AssociatedObject.SelectedCells.Any())
return;
var c = AssociatedObject.SelectedCells.First();
if (!(c.Column is DataGridTextColumn ct))
return;
var path = (ct.Binding as Binding)?.Path.Path ?? string.Empty;
if (string.IsNullOrWhiteSpace(path))
{
if (!string.IsNullOrWhiteSpace(TextPath))
{
TextPath = string.Empty;
TextSearch.SetText(AssociatedObject, string.Empty);
}
}
else
{
if (!path.Equals(TextPath, StringComparison.CurrentCulture))
{
TextPath = path;
TextSearch.SetText(AssociatedObject, string.Empty);
}
}
}
}
}
Need a custom picker in which the Title reduces in size once an Item is selected from the Picker as in image. Same happens for a MaterialisedEntry but need the same for Picker Control
Xamarin.Forms does not allow you to inject a custom view (like a ContentView with a stacklayout) into the Picker control). What you could do is to use a Grid where you have a stacklayout overlaid on a Picker. Based on the SelectedItem property of the picker you would need to update the Text of the State Label.
<Grid HorizontalOptions="Center" VerticalOptions="Center">
<Picker x:Name="picker"
Title="select a state">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Colorado</x:String>
<x:String>California</x:String>
<x:String>Ohio</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>
<StackLayout WidthRequest="300" InputTransparent="True" BackgroundColor="White">
<Label Text="State" FontSize="10"/>
<!--Here you would need to bind the Label Text to a property
that changes according to the SelectedItem of the picker-->
<Label Text="Colorado" FontSize="14"/>
</StackLayout>
</Grid>
According to your description, I suggest you can use custom render to add arrow for Picker control, like this:
Create class name CustomPicker in PLC.
public class CustomPicker : Picker
{
public static readonly BindableProperty ImageProperty =
BindableProperty.Create(nameof(Image), typeof(string), typeof(CustomPicker), string.Empty);
public string Image
{
get { return (string)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
}
Create class name CustomPickerRenderer in Android or ios.
[assembly: ExportRenderer(typeof(CustomPicker), typeof(CustomPickerRenderer))]
namespace demo3.Droid
{
public class CustomPickerRenderer : PickerRenderer
{
CustomPicker element;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
element = (CustomPicker)this.Element;
if (Control != null && this.Element != null && !string.IsNullOrEmpty(element.Image))
{
Control.Background = AddPickerStyles(element.Image);
}
}
public LayerDrawable AddPickerStyles(string imagePath)
{
//ShapeDrawable border = new ShapeDrawable();
// border.Paint.Color = Android.Graphics.Color.Gray;
// border.SetPadding(10,10,10,10);
// border.Paint.SetStyle(Paint.Style.Stroke);
//Drawable[] layers = { border , GetDrawable(imagePath) };
Drawable[] layers = { GetDrawable(imagePath) };
LayerDrawable layerDrawable = new LayerDrawable(layers);
layerDrawable.SetLayerInset(0, 0, 0, 0, 0);
return layerDrawable;
}
private BitmapDrawable GetDrawable(string imagePath)
{
int resID = Resources.GetIdentifier(imagePath, "drawable", this.Context.PackageName);
var drawable = ContextCompat.GetDrawable(this.Context, resID);
var bitmap = ((BitmapDrawable)drawable).Bitmap;
var result = new BitmapDrawable(Resources, Bitmap.CreateScaledBitmap(bitmap, 70, 70, true));
result.Gravity = Android.Views.GravityFlags.Right;
return result;
}
}
}
3.Reference this customPicker in PLC.
<Frame
Padding="8"
BorderColor="Gray"
CornerRadius="20"
HasShadow="True"
IsClippedToBounds="True">
<StackLayout>
<Label
x:Name="label"
FontSize="20"
Text="state" />
<picker:CustomPicker
x:Name="picker1"
Title="select one item"
Image="ic_arrow_drop_down"
SelectedIndexChanged="Picker1_SelectedIndexChanged">
<picker:CustomPicker.Items>
<x:String>1</x:String>
<x:String>2</x:String>
</picker:CustomPicker.Items>
</picker:CustomPicker>
</StackLayout>
</Frame>
private void Picker1_SelectedIndexChanged(object sender, EventArgs e)
{
var picker = sender as CustomPicker;
if(picker.SelectedIndex>-1)
{
label.FontSize = 10;
}
}
This is the screenshot:
Hello guys :D I am having a problem with the android part of the Xamarin.Forms
When I Navigate from a AbsoluteLayout with a Map and TabbleView (enter image description here) to a Grid with only a Map, the Map from the previous page stays static on top of the second one(enter image description here). This problem does not manifest in iOS, only in Android. If any of you guys know the problem please tell me so I can quickly fix :D
Page with First Map
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SGPI.Intervencao.CriarIntervencao"
xmlns:local="clr-namespace:SGPI.Shared"
xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Xamarin.Forms;assembly=Esri.ArcGISRuntime.Xamarin.Forms"
Padding="5,5">
<ContentPage.Resources>
<ResourceDictionary>
<local:MapViewModel x:Key="MapViewModel" />
</ResourceDictionary>
</ContentPage.Resources>
<AbsoluteLayout>
<esriUI:MapView Map="{Binding Map, Source={StaticResource MapViewModel}}" x:Name="map" AbsoluteLayout.LayoutBounds="0,0, 1, 0.6" AbsoluteLayout.LayoutFlags="All"/>
<TableView Intent="Form" HasUnevenRows="True" AbsoluteLayout.LayoutBounds="0,1, 1, 0.4" AbsoluteLayout.LayoutFlags="All">
<TableRoot>
<TableSection Title="Information">
<EntryCell Label="Nome" Text="{Binding Name}" Placeholder="Nome"/>
<EntryCell Label="Codigo" Text="{Binding Code}" Placeholder="Codigo"/>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="Status" HorizontalOptions="Start" VerticalOptions="Center"/>
<Picker x:Name="pick" SelectedIndex="{Binding Index}" SelectedItem="{Binding Status}" Title="Status" HorizontalOptions="FillAndExpand">
<Picker.Items>
<x:String>Em Construção</x:String>
<x:String>Construido</x:String>
</Picker.Items>
</Picker>
</StackLayout>
</ViewCell>
<ViewCell>
<Button Image="editMap.png" Clicked="Button_Clicked" />
</ViewCell>
</TableSection>
</TableRoot>
</TableView>
</AbsoluteLayout>
<ContentPage.ToolbarItems>
<ToolbarItem Icon="dan.png" Order="Primary" x:Name="done" Clicked="Done"/>
</ContentPage.ToolbarItems>
Code Behind
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace SGPI.Intervencao
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CriarIntervencao : ContentPage
{
public event EventHandler<IntervencaoClass> IntervencaoAdded;
public event EventHandler<IntervencaoClass> IntervencaoUpdated;
public CriarIntervencao(IntervencaoClass intervencao)
{
if(intervencao == null)
throw new ArgumentNullException(nameof(intervencao));
InitializeComponent();
BindingContext = new IntervencaoClass
{
Code = intervencao.Code,
Name = intervencao.Name,
Status = intervencao.Status,
Index = intervencao.Index,
Id = intervencao.Id,
Polygons = intervencao.Polygons
};
if(intervencao.Polygons != null)
map.GraphicsOverlays.Add(intervencao.Polygons);
}
private async void Done(object sender, EventArgs e)
{
var intervencao = BindingContext as IntervencaoClass;
if (filled())
{
await DisplayAlert("Erro", "Preenche tudo", "OK");
return;
}
map.GraphicsOverlays.Clear();
if (!intervencao.Id.HasValue)
{
intervencao.Id = 1;
IntervencaoAdded?.Invoke(this, intervencao);
}
else
{
IntervencaoUpdated?.Invoke(this, intervencao);
}
await Navigation.PopAsync();
}
public bool filled()
{
var intervencao = BindingContext as IntervencaoClass;
return String.IsNullOrEmpty(intervencao.Name) || String.IsNullOrEmpty(intervencao.Code) || pick.SelectedIndex == -1;
}
private async void Button_Clicked(object sender, EventArgs e)
{
map.GraphicsOverlays.Clear();
var page = new MapPages.MapPage((BindingContext as IntervencaoClass).Polygons);
page.AcceptedMap += (send, graphics) => {
var intervencao = BindingContext as IntervencaoClass;
intervencao.Polygons = graphics;
map.GraphicsOverlays.Add(graphics);
Navigation.PopAsync();
};
await Navigation.PushAsync(page);
}
}
}
Second Page
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:SGPI.Shared"
xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Xamarin.Forms;assembly=Esri.ArcGISRuntime.Xamarin.Forms"
x:Class="SGPI.MapPages.MapPage">
<ContentPage.Resources>
<ResourceDictionary>
<local:MapViewModel x:Key="MapViewModel" />
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<esriUI:MapView Map="{Binding Map, Source={StaticResource MapViewModel}}" x:Name="map" GeoViewTapped="Map_GeoViewTapped"/>
</Grid>
<ContentPage.ToolbarItems>
<ToolbarItem Icon="dan.png" Clicked="Done"/>
</ContentPage.ToolbarItems>
</ContentPage>
Second Page Code Behind
using Esri.ArcGISRuntime.Data;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Symbology;
using System.Collections.Generic;
using Xamarin.Forms;
using Esri.ArcGISRuntime.UI;
using System;
using System.Threading;
namespace SGPI.MapPages
{
public partial class MapPage : ContentPage
{
public event EventHandler<GraphicsOverlay> AcceptedMap;
List<MapPoint> points;
static SimpleLineSymbol symbol = new SimpleLineSymbol()
{
Style = SimpleLineSymbolStyle.Dash,
Color = System.Drawing.Color.Black,
Width = 1
};
static SimpleMarkerSymbol marker = new SimpleMarkerSymbol()
{
Color = System.Drawing.Color.Pink,
Outline = symbol,
Style = SimpleMarkerSymbolStyle.Diamond,
Size = 10
};
static SimpleLineSymbol line = new SimpleLineSymbol(SimpleLineSymbolStyle.Solid, System.Drawing.Color.CadetBlue, 1);
static SimpleFillSymbol fill = new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.Color.Black, line);
public MapPage(GraphicsOverlay graphic)
{
InitializeComponent();
map.Map = new SGPI.Shared.MapViewModel().Map;
if(graphic == null)
graphic = new GraphicsOverlay();
map.GraphicsOverlays.Add(graphic);
points = new List<MapPoint>();
}
private Graphic AddPolygonInMap(MapPoint[] points)
{
var pointCollection = new PointCollection(points[0].SpatialReference);
foreach (MapPoint p in points)
pointCollection.Add(p);
var sPolygon = new Polygon(pointCollection);
return new Graphic(sPolygon, fill);
}
private void AddPointinMap(MapPoint point)
{
Graphic graphic = new Graphic(point, marker);
map.GraphicsOverlays[0].Graphics.Add(graphic);
}
private async void Map_GeoViewTapped(object sender, Esri.ArcGISRuntime.Xamarin.Forms.GeoViewInputEventArgs e)
{
var tolerance = 10d; // Use larger tolerance for touch
var maximumResults = 1; // Only return one graphic
var onlyReturnPopups = false; // Don't return only popups
// Use the following method to identify graphics in a specific graphics overlay
IdentifyGraphicsOverlayResult identifyResults = await map.IdentifyGraphicsOverlayAsync(
map.GraphicsOverlays[0],
e.Position,
tolerance,
onlyReturnPopups,
maximumResults);
// Check if we got results
if (identifyResults.Graphics.Count > 0)
{
// Make sure that the UI changes are done in the UI thread
Device.BeginInvokeOnMainThread(async () => {
await DisplayAlert("", "Tapped on graphic", "OK");
});
} else
{
points.Add(e.Location);
AddPointinMap(e.Location);
}
}
private async void Done(object sender, EventArgs e)
{
if(points.Count > 2) {
GraphicsOverlay graphics = new GraphicsOverlay();
graphics.Graphics.Add(AddPolygonInMap(points.ToArray()));
map.GraphicsOverlays.Add(graphics);
Thread.Sleep(500);
var accepted = await DisplayAlert("Aviso", "Este é o polígono certo?", "Sim", "Não");
if (accepted)
{
GraphicsOverlay graph = graphics;
map.GraphicsOverlays.Clear();
AcceptedMap?.Invoke(this, graph);
}
else
{
for(int i = 1; i < map.GraphicsOverlays.Count; i++)
map.GraphicsOverlays[i] = new GraphicsOverlay();
}
}
}
}
}
I am running out of hair in frustration.
I have made a somewhat minimal example where the same viewmodel/view acts as main window and dialog. Hope this does not cause confusion.
class DialogViewModel : Screen
{
private readonly IWindowManager _windowManager;
public DialogViewModel(IWindowManager windowManager)
{
_windowManager = windowManager;
}
public void ShowDialog()
{
_windowManager.ShowDialog(new DialogViewModel(_windowManager));
}
//----------
public DialogViewModel()
{
}
public void Close()
{
TryClose();
//TryClose(true);
//TryClose(false);
}
}
<Window x:Class="Views.DialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro.Platform"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="200">
<WrapPanel>
<Button>
Close
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="Close" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button>
Show Dialog
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="ShowDialog" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</WrapPanel>
</Window>
public sealed class AppBootstrapper : BootstrapperBase
{
private SimpleContainer _container;
public AppBootstrapper() : base(true)
{
StartRuntime();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<DialogViewModel>();
}
protected override void Configure()
{
_container = new SimpleContainer();
_container.Singleton<IWindowManager, WindowManager>();
_container.Singleton<IEventAggregator, EventAggregator>();
_container.PerRequest<DialogViewModel>();
}
protected override object GetInstance(Type service, string key)
{
var instance = _container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException("Could not locate any instances.");
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
The call TryClose() does not initiate OnDeactivate.
According to https://caliburnmicro.codeplex.com/wikipage?title=Screens,%20Conductors%20and%20Composition and the several posts here on stackoverflow the above should be enough. But nothing happens when I call TryClose().
Please note that code does run and show the dialogs on my machine. If something is missing, please let me know.
Any ideas why?
Thank you!
StartRuntime should be Initialize(), codeplex code is really really outdated with release of v2. Reference github.com/bluespire/Caliburn.Micro or CaliburnMicro.com. I assume this is a WPF App. Also your Button's don't appear to be correct.
<Button x:Name="ShowDialog" Content="ShowDialog" />
<Button x:Name="Close" Content="Close" />
I am assuming this is a WPF application.
I have a Windows Phone 8 app and I have a RelayCommand Instance called DiscoverExpansionModulesCommand. I have a button with the Command property bound to DiscoverExpansionModulesCommand. When the app first loads, the button is enabled or disabled properly. However, when on the page and I want to change whether the command can execute, the method CanExecuteDiscoverExpansionModulesCommand() properly fires and it returns the proper true or false value, but the button does not reflect it. Why isn't button updating it's UI? I found another article on this issue here http://social.msdn.microsoft.com/Forums/en-US/silverlightarchieve/thread/48a341e4-f512-4c33-befd-b614404b4920/
My ViewModel:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using MAL.Portable.Commands;
using MAL.Portable.Message;
using MAL.Portable.Model;
using System;
using System.Collections.Generic;
using System.Windows.Input;
namespace MAL.Portable.ViewModel
{
public class SettingsViewModel : ViewModelBase
{
// Define an observable collection property that controls can bind to.
private List<Setting> settings;
private String controllerUrl;
private String controllerPort;
private String temperature;
private Wifi wifi;
private Boolean connected;
private Boolean checkingConnection;
public SettingsViewModel()
{
DiscoverExpansionModulesCommand = new RelayCommand(OnDiscoverExpansionModules, CanExecuteDiscoverExpansionModulesCommand);
Messenger.Default.Register<RetrieveSettingsMessage>
(
this, (action) => RetrievedListsMessage(action)
);
Messenger.Default.Send<GetSettingsMessage>(new GetSettingsMessage());
}
public ICommand DiscoverExpansionModulesCommand
{
get;
private set;
}
public String ConnectionStatus
{
get
{
if (checkingConnection)
return "checking";
else
return connected ? "connected" : "not connnected";
}
}
private Boolean CanExecuteDiscoverExpansionModulesCommand()
{
return connected;
}
private void OnDiscoverExpansionModules()
{
}
private void CheckConnection()
{
wifi = null;
if (!String.IsNullOrWhiteSpace(ControllerUrl) && !String.IsNullOrWhiteSpace(ControllerPort) && !checkingConnection)
{
checkingConnection = true;
wifi = new ReefAngelWifi(controllerUrl, controllerPort);
wifi.TestConnectionComplete += wifi_TestConnectionComplete;
wifi.RequestFail += wifi_RequestFail;
wifi.BeginTestConnection();
}
}
private void wifi_RequestFail(object sender, RequestExceptionEventArgs e)
{
connected = false;
checkingConnection = false;
RaisePropertyChanged("ConnectionStatus");
}
private void wifi_TestConnectionComplete(object sender, TestConnectionEventArgs e)
{
connected = e.TestSuccessful;
checkingConnection = false;
DiscoverExpansionModulesCommand.CanExecute(null);
RaisePropertyChanged("ConnectionStatus");
RaisePropertyChanged("DiscoverExpansionModulesCommand");
}
private object RetrievedListsMessage(RetrieveSettingsMessage action)
{
settings = action.Settings;
CheckConnection();
return null;
}
private String GetStringValue(String key)
{
if (settings == null) return String.Empty;
var item = settings.Find(x => x.Key == key);
if (item == null) return String.Empty;
else return item.Value;
}
private Boolean GetBooleanValue(String key)
{
if (settings == null) return false;
var item = settings.Find(x => x.Key == key);
if (item == null) return false;
else return Boolean.Parse(item.Value);
}
}
}
And the XAML
<phone:PhoneApplicationPage
xmlns:ReefAngel="clr-namespace:MAL.WindowsPhone8"
xmlns:Controls="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Input"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Platform.WP8"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:telerikPrimitives="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Primitives"
x:Class="MAL.WindowsPhone8.ReefAngel.SettingsPage"
xmlns:converter="clr-namespace:MAL.WindowsPhone8.Converters"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
DataContext="{Binding Settings, Source={StaticResource Locator}}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True">
<phone:PhoneApplicationPage.Resources>
<converter:BooleanToStringConverter x:Key="temperatureConverter" TrueString="Celsius" FalseString="Fahrenheit" />
<converter:BooleanToStringConverter x:Key="timeFormatConverter" TrueString="24 hour" FalseString="12 hour" />
<converter:BooleanToStringConverter x:Key="dateFormatConverter" TrueString="dd/mm/yyyy" FalseString="mm/dd/yyyy" />
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" >
<phone:Pivot Title="{Binding LocalizedResources.ApplicationTitle, Source={StaticResource LocalizedStrings}, StringFormat='\{0\} Settings'}">
<phone:PivotItem Header="connection">
<Grid>
<StackPanel Margin="12,0,0,0">
<TextBlock Margin="0,20,0,0" TextWrapping="Wrap" Text="Reef Angel Wifi Address"/>
<TextBox Height="72" TextWrapping="Wrap" Text="{Binding ControllerUrl, Mode=TwoWay}"/>
<TextBlock Margin="0,20,0,0" TextWrapping="Wrap" Text="Reef Angel Wifi Port"/>
<TextBox Height="72" TextWrapping="Wrap" Text="{Binding ControllerPort, Mode=TwoWay}"/>
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0,20,0,0" TextWrapping="Wrap" Text="Reef Angel Wifi Status : "/>
<TextBlock Margin="0,20,0,0" TextWrapping="Wrap" Text="{Binding ConnectionStatus, Mode=OneWay}"/>
</StackPanel>
</StackPanel>
</Grid>
</phone:PivotItem>
<phone:PivotItem Header="expansion">
<Grid>
<Button Content="Discover Expansion Modules" x:Name="DiscoverButton" Command="{Binding DiscoverExpansionModulesCommand, Mode=OneWay}" />
</Grid>
</phone:PivotItem>
</phone:Pivot>
</Grid>
</phone:PhoneApplicationPage>
I am using the MVVM Light Portable Class Libraries.
You need to call RelayCommand.RaiseCanExecuteChanged() when the conditions you evaluate inside your CanExecute method change.
Edit
private void wifi_RequestFail(object sender, RequestExceptionEventArgs e)
{
connected = false;
checkingConnection = false;
RaisePropertyChanged("ConnectionStatus");
DiscoverExpansionModulesCommand.RaiseCanExecuteChanged();
}
private void wifi_TestConnectionComplete(object sender, TestConnectionEventArgs e)
{
connected = e.TestSuccessful;
checkingConnection = false;
DiscoverExpansionModulesCommand.CanExecute(null);
RaisePropertyChanged("ConnectionStatus");
RaisePropertyChanged("DiscoverExpansionModulesCommand");
DiscoverExpansionModulesCommand.RaiseCanExecuteChanged();
}
This will not cause a loop as it only tells the RelayCommand to re-execute the specified CanExecute method. In your case this only means that the property CanExecuteDiscoverExpansionModulesCommand is read.
It appears to be a cross threading issue. And figuring out how to call a Dispatcher in the PCL was tricky, but I found it here: Update UI thread from portable class library