column row is not getting selected/highlighted on window when user presses any letter key on Keyboard - selectionchanged

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);
}
}
}
}
}

Related

Set Placeholder as Title on Picker Item Selection in Xamarin Forms

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:

Xamarin Forms -> Activity Indicator not working if Commands of statements to be executed

Using Visual Studio 2017 Community 15.8.1
This is after going through all options of stackoverflow regarding ActivityIndicator. So though it may be a duplication but nothing is helping me out.
So finally decided to post my workouts and get best help from here.
What I have tried till now:-
1. {Binding IsLoading} + INotifyPropertyChanged + public void RaisePropertyChanged(string propName) + IsLoading = true; concept.
2. ActivityIndicator_Busy.IsVisible = false; (Direct control accessed)
These two approaches were mostly recommended and I went into depth of each since lot of hours in last few weeks. But nothing got crack.
What I achieved?:-
ActivityIndicator_Busy.IsVisible = false; concept is working smooth only when I put return before executing the statements (for testing purpose); statement on Button Clicked event. (Attached Image)
But as soon as I remove the return; On Pressing Button, directly after some pause, the HomePage Opens.
MY Questions:-
1. This is particular with the current scenario how to get the ActivityIndicator run Immediately when user clicks the Welcome Button.
2. Pertaining to same, When app starts there is also a blank white screen coming for few seconds almost 30 seconds which I also I want to show ActivityIndicator. But dont know how to impose that logic at which instance.
My Inputs
My MainPage.xaml File:-
(Edited 06-Sept-2018 09.11 pm)
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Name="page_main_page"
NavigationPage.HasBackButton="False"
NavigationPage.HasNavigationBar="False"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:appNutri"
BindingContext="{x:Reference page_main_page}"
x:Class="appNutri.MainPage">
<ContentPage.Content>
<StackLayout BackgroundColor="White"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<StackLayout>
<Image x:Name="Image_Welcome"
Source="welcome.png"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
WidthRequest="300"
HeightRequest="300" />
<Button x:Name="Button_Welcome"
Clicked="Button_Welcome_Clicked"
Text="Welcome!"
BackgroundColor="DeepSkyBlue"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
TextColor="White"
HeightRequest="60" />
</StackLayout>
<StackLayout BackgroundColor="White"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<ActivityIndicator
x:Name="ActivityIndicator_Busy"
Color="Black"
IsEnabled="True"
HorizontalOptions="Center"
VerticalOptions="Center"
IsRunning="{Binding Source={x:Reference page_main_page}, Path=IsLoading}"
IsVisible="{Binding Source={x:Reference page_main_page}, Path=IsLoading}" />
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
My MainPage.cs Code:-
(Edited on 06-Sept-2018 09.13 pm)
using appNutri.Model;
using SQLite;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace appNutri
{
public partial class MainPage : Xamarin.Forms.ContentPage, INotifyPropertyChanged
{
private bool isLoading;
public bool IsLoading
{
get
{
return isLoading;
}
set
{
isLoading = value;
RaisePropertyChanged("IsLoading");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public MainPage()
{
InitializeComponent();
BindingContext = this;
}
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = this;
}
protected async void Button_Welcome_Clicked(object sender, EventArgs e)
{
IsLoading = true;
await Select_Local_User_Information();
IsLoading = false;
}
private async Task Select_Local_User_Information()
{
IsLoading = true;
string where_clause = "";
try
{
Sql_Common.Database_Folder_Path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
string Database_Full_Path = Path.Combine(Sql_Common.Database_Folder_Path, Sql_Common.Database_Name);
SQLiteConnection connection = new SQLiteConnection(Database_Full_Path);
//connection.DropTable<User_Master>();
//connection.Delete(connection.Table<User_Master>());
//connection.CreateTable<User_Master>(CreateFlags.ImplicitPK | CreateFlags.AutoIncPK);
connection.CreateTable<User_Master>();
int count = connection.ExecuteScalar<int>("Select count(*) from User_Master");
if (count == 0)
{
connection.DropTable<User_Master>();
connection.CreateTable<User_Master>();
//IsLoading = false;
//IsBusy = false;
await Navigation.PushAsync(new User_Register_Page());
}
else
{
Sql_Common.User_Logged = true;
var Local_User_Data = connection.Table<User_Master>().ToList();
User_Master.Logged_User_Details_Container.First_Name = Local_User_Data[0].First_Name;
User_Master.Logged_User_Details_Container.Cell1 = Local_User_Data[0].Cell1;
where_clause = " Upper ( First_Name ) = " + "'" + User_Master.Logged_User_Details_Container.First_Name.ToUpper().Trim() + "'" + " and " +
" Cell1 = " + "'" + User_Master.Logged_User_Details_Container.Cell1.Trim() + "'";
int records = Sql_Common.Get_Number_Of_Rows_Count("User_Master", where_clause);
if (records == 0)
{
connection.DropTable<User_Master>();
connection.CreateTable<User_Master>();
IsLoading = false;
await Navigation.PushAsync(new User_Register_Page());
}
else
{
User_Master.User_Master_Table(where_clause, User_Master.Logged_User_Details_Container);
IsLoading = false;
await Navigation.PushAsync(new User_Home_Page());
}
}
connection.Close();
}
catch (SQLiteException ex)
{
string ex_msg = ex.Message;
}
IsLoading = false;
}
}
}
04-Oct-2018
Finally resolved with This Answer
Update 2018-09-10
You think that you have implemented INotifyPropertyChanged by adding INotifyPropertyChanged to your class definition and adding the event
public event PropertyChangedEventHandler PropertyChanged;
along with its event invocator
public void RaisePropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
Anyway, since ContentPage already implements INotifyPropertyChanged, adding those did not implement INotifyPropertyChanged. ContentPage already defines the event (or rather BindableObjectfrom which ContentPage indirectly inherits). Any object that relies on being informed about property changes in your page will subscribe to the PropertyChanged event of the ancestor and not the PropertyChanged event you defined, hence the ActivityIndicator will not update.
Just remove the event you defined and call OnPropertyChanged instead of RaisePropertyChanged() and you should be fine.
private bool isLoading;
public bool IsLoading
{
get
{
return isLoading;
}
set
{
isLoading = value;
OnPropertyChanged();
}
}
Since OnPropertyChanged is declared as
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
you don't have to pass the property name by hand. The compiler will do that for you beacsue of the CallerMemberNameAttribute.
End Update
The XAML extension {Binding IsLoading} binds the ActivityIndicator to the BindingContext of your page. By default the BindingContext is null, hence there is nothing to bind to and all your efforts are to no avail.
With a viewmodel
The preferred solution would be to use a viewmodel and assign it to MainPage.BindingContext, e.g.
var page = new MainPage()
{
BindingContext = new MainPageViewModel()
}
but if you take that road, you should move all of your UI logic to that viewmodel and encapsulate your SQL access and business logic in other classes, to keep the viewmodel clean from resource accesses and business logic. Having the resource accesses and logic in code behind may work for that small example, but is likely to become an unmaintainable mess.
Without a viewmodel
Anyway, you don't have to use a viewmodel to use bindings. You can set the BindingContext for the page (or some children) or use the Source of the BindingExtension
Setting the BindingContext
The BindingContext is passed from any page or view to it's children. You first have to give your page a name with x:Name="Page" (don't have to use Page, anyway, you can't use the class name of your page) and set the BindingContext to that page
<ContentPage ...
x:Name="Page"
BindingContext="{x:Reference Page}"
...>
now binding to IsLoading should work.
Using Source in the Binding
If you want to reference something else than the BindingContext of a view, BindingExtension has a property Source. You have to give a name to your page, too (see above)
<ContentPage ...
x:Name="Page"
...>
and now you can reference this in your binding
<ActivityIndicator
...
IsRunning="{Binding Path=IsLoading, Source={x:Reference Page}}"
IsVisible="{Binding Path=IsLoading, Source={x:Reference Page}}"/>

ArcGis Map does not disapear in navigation Xamarin.Forms

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();
}
}
}
}
}

Xaamrin Forms BoxView Width Too Long when using to Underline

I am using a BoxView to accomplish underlining in my app. I have a couple of labels that are very short - Text such as Yes or No etc. Here is the XAML for one of the labels with the BoxView for underlining:
<StackLayout Orientation="Vertical" Grid.Row="5" Grid.Column="1" Margin="0,4,0,4" HorizontalOptions="Start" BackgroundColor="Purple" MinimumWidthRequest="1">
<Label x:Name="txtUseMetric" TextColor="Blue" FontSize="Small" Text="{Binding UseMetricText}" BackgroundColor="Yellow">
<Label.GestureRecognizers>
<TapGestureRecognizer Tapped="Value_Tapped" CommandParameter="usemetric" />
</Label.GestureRecognizers>
</Label>
<BoxView BackgroundColor="Green" HeightRequest="1" MinimumWidthRequest="1" />
</StackLayout>
My problem is that the width of the BoxView is always extending past my text I have tried overriding the MinWidthRequest in my App.Xaml file as seen below:
<Style TargetType="BoxView">
<Setter Property="MinimumWidthRequest" Value="3" />
</Style>
But this has not effect. I have included screen shots for you to see.
FYI - The yellow is the width of the Label. You don't see any purple (the background color of the StackLayout) because the StackLayout and Label are the same width. The second screen shot shows what the screen looks like if I remove the BoxView - i.e. the Label and StackLayout are sized correctly.
Any suggestions on how to fix this?
Screen shot with BoxView Too Long making label and StackLayout too long
Screen shot with BoxView removed and Label and Stack Layout sizing correctly
Please note the default HorizontalOptions and that Label derives from View:
Default value is LayoutOptions.Fill unless otherwise documented.
Add HorizontalOptions="Start" on the "Use Metric" Label:
<Label x:Name="txtUseMetric" TextColor="Blue" FontSize="Small"
Text="{Binding UseMetricText}" BackgroundColor="Yellow"
HorizontalOptions="Start">
<BoxView BackgroundColor="Green" HeightRequest="1"
WidthRequest="{Binding Path=Width, Source={x:Reference txtUseMetric}"
HorizontalOptions="Start"/>
One option is to replace the label/box underline with a custom renderer that adds an underline capability to the label.
Here is how to do it:
User Control
public class CustomLabel : Label
{
public static readonly BindableProperty IsUnderlinedProperty =
BindableProperty.Create(nameof(IsUnderlined), typeof(bool), typeof(CustomLabel), false);
public bool IsUnderlined
{
get { return (bool)GetValue(IsUnderlinedProperty); }
set
{
SetValue(IsUnderlinedProperty, value);
}
}
}
Android renderer
[assembly: ExportRenderer(typeof(CustomLabel), typeof(CustomLabelRenderer))]
namespace Incident.Droid.CustomRenderers
{
public class CustomLabelRenderer : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
var view = (CustomLabel)Element;
var control = Control;
UpdateUi(view, control);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var view = (CustomLabel)Element;
if (e.PropertyName == CustomLabel.IsUnderlinedProperty.PropertyName)
{
Control.PaintFlags = view.IsUnderlined ? Control.PaintFlags | PaintFlags.UnderlineText : Control.PaintFlags &= ~PaintFlags.UnderlineText;
}
}
static void UpdateUi(CustomLabel view, TextView control)
{
if (view.FontSize > 0)
{
control.TextSize = (float)view.FontSize;
}
if (view.IsUnderlined)
{
control.PaintFlags = control.PaintFlags | PaintFlags.UnderlineText;
}
}
}
}
iOS Renderer
[assembly: ExportRenderer(typeof(CustomLabel), typeof(CustomLabelRenderer))]
namespace Incident.iOS.CustomRenderers
{
public class CustomLabelRenderer : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
var view = (CustomLabel)Element;
UpdateUi(view, Control);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var view = (CustomLabel)Element;
if (e.PropertyName == CustomLabel.IsUnderlinedProperty.PropertyName)
{
UpdateUi(view, Control);
}
}
private static void UpdateUi(CustomLabel view, UILabel control)
{
var attrString = new NSMutableAttributedString(control.Text);
if (view != null && view.IsUnderlined)
{
attrString.AddAttribute(UIStringAttributeKey.UnderlineStyle,
NSNumber.FromInt32((int)NSUnderlineStyle.Single),
new NSRange(0, attrString.Length));
}
control.AttributedText = attrString;
}
}
}

RelayCommand.CanExecute not updating IsEnabled in UI

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

Resources