I'm experiencing a refresh problem with my databound ComboBox.
The selection is not refreshed when I change the SelectedValue from the code-behind.
Here is a sample I made which illustrates the problem. Am I doing something wrong ?
Thanks !
<navigation:Page x:Class="Views.TestCB"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="TestCB Page">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ComboBox ItemsSource="{Binding AllItems, Mode=TwoWay}" Grid.Row="0" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" DisplayMemberPath="Key" SelectedValuePath="Value" SelectedValue="{Binding SelectedValue, Mode=TwoWay}"/>
<Button Content="Change" Click="Button_Click" Grid.Row="1" />
<Button Content="Display" Click="Button_Click_1" Grid.Row="2" />
</Grid>
</navigation:Page>
public partial class TestCB : Page, INotifyPropertyChanged
{
public TestCB()
{
InitializeComponent();
}
private Random r = new Random();
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
private ObservableCollection<MyClass> allItemsField = new ObservableCollection<MyClass>();
public ObservableCollection<MyClass> AllItems
{
get
{
return this.allItemsField;
}
set
{
if (this.allItemsField != value)
{
this.allItemsField = value;
this.OnPropertyChanged("AllItems");
}
}
}
private MyClass selectedItemField = null;
public MyClass SelectedItem
{
get
{
return this.selectedItemField;
}
set
{
if (this.selectedItemField != value)
{
this.selectedItemField = value;
this.OnPropertyChanged("SelectedItem");
}
}
}
private int selectedValueField;
public int SelectedValue
{
get
{
return this.selectedValueField;
}
set
{
if (this.selectedValueField != value)
{
this.selectedValueField = value;
this.OnPropertyChanged("SelectedValue");
}
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.SetCombo();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.SetCombo();
this.SelectRandom();
}
private void SetCombo()
{
this.AllItems.Clear();
int generated = 0;
while (generated < 10)
{
int val = r.Next(100);
string key = string.Format("Key #{0}", val);
if (!this.AllItems.Any(e => e.Key == key))
{
this.AllItems.Add(new MyClass { Key = key, Value = val });
generated++;
}
}
}
private void SelectRandom()
{
var tmp = this.AllItems[r.Next(this.AllItems.Count)];
this.SelectedValue = tmp.Value;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
MessageBox.Show(this.SelectedItem.Key);
}
}
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
private int valueField;
public int Value
{
get
{
return this.valueField;
}
set
{
if (this.valueField != value)
{
this.valueField = value;
this.OnPropertyChanged("Value");
}
}
}
private string keyField = string.Empty;
public string Key
{
get
{
return this.keyField;
}
set
{
if (this.keyField != value)
{
this.keyField = value;
this.OnPropertyChanged("Key");
}
}
}
}
EDIT: Please note that in my program, the ComboBox is part of a ListBoxItem (the ListBox being also databound), so I can't access it directly to reset the binding/force a reload.
I finally used the combobox found here : http://forums.lhotka.net/forums/p/9786/45971.aspx
It corrected my SelectedValue binding problem.
Have you tried setting SelectedItem instead? SelectedValue has some know issues.
Related
I am trying to get an image in the shape of rectangle with rounded Cornes. I have tried to use nugget fftransformations and frame. So far I am not getting the result I want. Using the nugget turns the image into square no matter what size I give to it. Using frame for some reason doesn't actually round the corners. enter image description here
<StackLayout VerticalOptions="CenterAndExpand">
<ffimageloading:CachedImage VerticalOptions="CenterAndExpand"
WidthRequest="530" HeightRequest="334"
DownsampleToViewSize="true"
Source = "http://loremflickr.com/530/334/nature?filename=simple.jpg">
<ffimageloading:CachedImage.Transformations>
<fftransformations:RoundedTransformation Radius="10"/>
</ffimageloading:CachedImage.Transformations>
</ffimageloading:CachedImage>
<ffimageloading:CachedImage VerticalOptions="CenterAndExpand"
WidthRequest="530" HeightRequest="334"
DownsampleToViewSize="true"
Source = "http://loremflickr.com/530/334/nature?filename=simple.jpg">
<ffimageloading:CachedImage.Transformations>
<fftransformations:CornersTransformation CornersTransformType="AllRounded"/>
</ffimageloading:CachedImage.Transformations>
</ffimageloading:CachedImage>
<Grid VerticalOptions="CenterAndExpand">
<Frame Padding="0" WidthRequest="530" HeightRequest="334"
HorizontalOptions="Center"
VerticalOptions="Center" CornerRadius="20" >
<Image Source = "http://loremflickr.com/530/330/nature?filename=simple.jpg" Aspect="AspectFill" />
</Frame>
</Grid>
</StackLayout>
Some time ago I needed corner effects on other controls. And it applies to image too.
All you need is to create Effect:
public class RoundCornersEffect : RoutingEffect
{
public RoundCornersEffect() : base($"MySuperApp.{nameof(RoundCornersEffect)}")
{
}
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.CreateAttached(
"CornerRadius",
typeof(int),
typeof(RoundCornersEffect),
0,
propertyChanged: OnCornerRadiusChanged);
public static int GetCornerRadius(BindableObject view) =>
(int)view.GetValue(CornerRadiusProperty);
public static void SetCornerRadius(BindableObject view, int value) =>
view.SetValue(CornerRadiusProperty, value);
private static void OnCornerRadiusChanged(BindableObject bindable, object oldValue, object newValue)
{
if (!(bindable is View view))
return;
var cornerRadius = (int)newValue;
var effect = view.Effects.OfType<RoundCornersEffect>().FirstOrDefault();
if (cornerRadius > 0 && effect == null)
view.Effects.Add(new RoundCornersEffect());
if (cornerRadius == 0 && effect != null)
view.Effects.Remove(effect);
}
}
And implement it on both platforms:
IOS:
public class RoundCornersEffectIOS : PlatformEffect
{
protected override void OnAttached()
{
try
{
PrepareContainer();
SetCornerRadius();
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}
protected override void OnDetached()
{
try
{
Container.Layer.CornerRadius = new nfloat(0);
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}
protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
{
if (args.PropertyName == RoundCornersEffect.CornerRadiusProperty.PropertyName)
SetCornerRadius();
}
private void PrepareContainer()
{
Container.ClipsToBounds = true;
Container.Layer.AllowsEdgeAntialiasing = true;
Container.Layer.EdgeAntialiasingMask = CAEdgeAntialiasingMask.All;
}
private void SetCornerRadius()
{
var cornerRadius = RoundCornersEffect.GetCornerRadius(Element);
Container.Layer.CornerRadius = new nfloat(cornerRadius);
}
}
Droid:
public class RoundCornersEffectDroid : PlatformEffect
{
private Android.Views.View View => Control ?? Container;
protected override void OnAttached()
{
try
{
PrepareContainer();
SetCornerRadius();
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}
protected override void OnDetached()
{
try
{
View.OutlineProvider = ViewOutlineProvider.Background;
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}
protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
{
if (args.PropertyName == RoundCornersEffect.CornerRadiusProperty.PropertyName)
SetCornerRadius();
}
private void PrepareContainer()
{
View.ClipToOutline = true;
}
private void SetCornerRadius()
{
var cornerRadius = RoundCornersEffect.GetCornerRadius(Element) * GetDensity();
View.OutlineProvider = new RoundedOutlineProvider(cornerRadius);
}
private static double GetDensity() =>
DeviceDisplay.MainDisplayInfo.Density;
private class RoundedOutlineProvider : ViewOutlineProvider
{
private readonly double _radius;
public RoundedOutlineProvider(double radius)
{
_radius = radius;
}
public override void GetOutline(Android.Views.View view, Outline outline)
{
outline?.SetRoundRect(0, 0, view.Width, view.Height, (float)_radius);
}
}
}
Then you can use it in control:
<Image Source="mylogo.png" VerticalOptions="Center" Aspect="AspectFit" myeffects:RoundCornersEffect.CornerRadius="5"/>
I have created dynamic 20 entries and want to focus on next entry after user enter a digit and max length of entry is 1. The focus should be automatically move on next entry.I am sharing my code.Thanks in advance for help.
//model
public class CrossingUIModel
{
public int Id { get; set; }
public string FieldValue { get; set; }
}
//on change property
private ObservableCollection<CrossingUIModel> bindCrossingUIModel;
public ObservableCollection<CrossingUIModel> BindCrossingUIModel
{
get { return bindCrossingUIModel; }
set
{
bindCrossingUIModel = value;
OnPropertyChanged(nameof(BindCrossingUIModel));
}
}
//creating ui
public void CreateUI()
{
UserDialogs.Instance.ShowLoading();
BindCrossingUIModel = new ObservableCollection<CrossingUIModel>();
for (int i = 1; i < 21; i++)
{
CrossingUIModel model = new CrossingUIModel();
model.Id = i;
BindCrossingUIModel.Add(model);
}
UserDialogs.Instance.HideLoading();
}
//xml file
<CollectionView x:Name="CrossingView" ItemsSource="{Binding BindCrossingUIModel, Mode=TwoWay}" SelectionMode="Multiple">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="10" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout HorizontalOptions="FillAndExpand">
<Entry x:Name="Fields" Text="{Binding FieldValue, Mode=TwoWay}"
ReturnType="Next" MaxLength="1" Keyboard="Numeric"
TextChanged="Fields_TextChanged" ></Entry>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Since you had used Data-Binding , it would be better to handle all the logic in ViewModel .
in code behind
Define a custom Entry
public class MyEntry:Entry
{
public static readonly BindableProperty IsFocusProperty =BindableProperty.Create("IsFocus", typeof(bool), typeof(MyEntry), false,propertyChanged: OnChanged);
static void OnChanged(BindableObject bindable, object oldValue, object newValue)
{
var entry = bindable as MyEntry;
var focus = (bool)newValue;
if(focus)
{
entry.Focus();
}
else
{
entry.Unfocus();
}
}
public bool IsFocus
{
get { return (bool)GetValue(IsFocusProperty); }
set {
SetValue(IsFocusProperty, value);
}
}
public MyEntry()
{
this.Focused += MyEntry_Focused;
this.Unfocused += MyEntry_Unfocused;
}
private void MyEntry_Unfocused(object sender, FocusEventArgs e)
{
this.IsFocus = false;
}
private void MyEntry_Focused(object sender, FocusEventArgs e)
{
this.IsFocus = true;
}
}
in Model
public class CrossingUIModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Id { get; set; }
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
string fieldValue;
public string FieldValue
{
get
{
return fieldValue;
}
set
{
if (fieldValue != value)
{
fieldValue = value;
OnPropertyChanged("FieldValue");
}
}
}
bool isFocus = false;
public bool IsFocus
{
get
{
return isFocus;
}
set
{
if (isFocus != value)
{
isFocus = value;
OnPropertyChanged("IsFocus");
}
}
}
}
in ViewModel
public class MyViewModel
{
public ObservableCollection<CrossingUIModel> BindCrossingUIModel { get; set; }
public MyViewModel()
{
BindCrossingUIModel = new ObservableCollection<CrossingUIModel>();
for (int i = 1; i < 21; i++)
{
CrossingUIModel model = new CrossingUIModel();
model.Id = i;
BindCrossingUIModel.Add(model);
}
foreach (CrossingUIModel model in BindCrossingUIModel)
{
model.PropertyChanged += Model_PropertyChanged;
}
}
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName== "FieldValue")
{
var model = sender as CrossingUIModel;
if(model.FieldValue.Length==1)
{
model.FieldValue = model.FieldValue.Substring(0, 1);
model.IsFocus = false;
int id = model.Id;
BindCrossingUIModel[id].IsFocus = true;
}
}
}
}
in xaml
Now we don't need to set MaxLength and TextChanged any more .
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<CollectionView x:Name="CrossingView" ItemsSource="{Binding BindCrossingUIModel, Mode=TwoWay}" SelectionMode="Multiple">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="10" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout WidthRequest="100" HeightRequest="30" HorizontalOptions="FillAndExpand">
<local:MyEntry WidthRequest="80" BackgroundColor="LightBlue" HeightRequest="30" x:Name="Fields" Text="{Binding FieldValue, Mode=TwoWay}"
IsFocus="{Binding IsFocus, Mode=TwoWay}"
ReturnType="Next" Keyboard="Numeric"
></local:MyEntry>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
By the way , you could use Grid instead of StackLayout as the Parent Layout of the Entry.
I am having 20 entries and these are created dynamically through loop. and on textchanges i have set focus to the next entry ,having focus to next entry keyboard hides and than show again and it happen on every text change.I want that keyboard should not hide every time after textchange it show always show till the last 20th entry..
----My Custom Entry
public class MyEntry : Entry
{
public static readonly BindableProperty IsFocusProperty = BindableProperty.Create("IsFocus", typeof(bool), typeof(MyEntry), false, propertyChanged: OnChanged);
static void OnChanged(BindableObject bindable, object oldValue, object newValue)
{
var entry = bindable as MyEntry;
var focus = (bool)newValue;
if (focus)
{
entry.Focus();
}
else
{
entry.Unfocus();
}
}
public bool IsFocus
{
get { return (bool)GetValue(IsFocusProperty); }
set
{
SetValue(IsFocusProperty, value);
}
}
public MyEntry()
{
this.Focused += MyEntry_Focused;
this.Unfocused += MyEntry_Unfocused;
}
private void MyEntry_Unfocused(object sender, FocusEventArgs e)
{
this.IsFocus = false;
}
private void MyEntry_Focused(object sender, FocusEventArgs e)
{
this.IsFocus = true;
}
}
--My model
public class CrossingUIModel : BaseViewModel
{
public int Id { get; set; }
private string fieldValue;
public string FieldValue
{
get { return fieldValue; }
set
{
if (fieldValue != value)
{
fieldValue = value;
OnPropertyChanged(nameof(FieldValue));
}
}
}
private bool isFocus = false;
public bool IsFocus
{
get { return isFocus; }
set
{
if (isFocus != value)
{
isFocus = value;
OnPropertyChanged(nameof(IsFocus));
}
}
}
}
--my View Model
public void CreateUI()
{
UserDialogs.Instance.ShowLoading();
BindCrossingUIModel = new ObservableCollection<CrossingUIModel>();
for (int i = 1; i < 21; i++)
{
CrossingUIModel model = new CrossingUIModel();
model.Id = i;
BindCrossingUIModel.Add(model);
}
foreach (CrossingUIModel model in BindCrossingUIModel)
{
model.PropertyChanged += Model_PropertyChanged;
}
UserDialogs.Instance.HideLoading();
}
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "FieldValue")
{
var model = sender as CrossingUIModel;
if (model.FieldValue.Length == 1)
{
model.FieldValue = model.FieldValue.Substring(0, 1);
model.IsFocus = false;
int id = model.Id;
if (id == 20)
{
var FocusControl = _page.FindByName<Entry>("Amt");
if (FocusControl != null)
{
FocusControl.Focus();
}
}
else
{
BindCrossingUIModel[id].IsFocus = true;
}
}
}
}
--xaml file
<CollectionView.ItemTemplate>
<DataTemplate>
<!--<StackLayout HorizontalOptions="FillAndExpand">
<Entry x:Name="Fields" Text="{Binding FieldValue, Mode=TwoWay}"
ReturnType="Next" MaxLength="1" Keyboard="Numeric"
TextChanged="Fields_TextChanged" ></Entry>
</StackLayout>-->
<StackLayout WidthRequest="100" HorizontalOptions="FillAndExpand" >
<local:MyEntry WidthRequest="80" BackgroundColor="White" HeightRequest="50"
x:Name="Fields" Text="{Binding FieldValue, Mode=TwoWay}"
IsFocus="{Binding IsFocus, Mode=TwoWay}"
ReturnType="Next" Keyboard="Numeric" MaxLength="1"
></local:MyEntry>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
You could modify the code by removing the line model.IsFocus = false;
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "FieldValue")
{
var model = sender as CrossingUIModel;
if (model.FieldValue.Length == 1)
{
model.FieldValue = model.FieldValue.Substring(0, 1);
int id = model.Id;
if (id == 20)
{
var FocusControl = _page.FindByName<Entry>("Amt");
if (FocusControl != null)
{
FocusControl.Focus();
}
}
else
{
BindCrossingUIModel[id].IsFocus = true;
}
}
}
I've got the following Xamarin.Forms TemplatedView:
using System;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
namespace STM.Framework.Controls
{
[ContentProperty(nameof(Content))]
public class PopOverLayout : TemplatedView
{
public PopOverLayout()
{
}
#region BoxViewTapCommand
public static BindableProperty BoxViewTapCommandProperty = BindableProperty.Create(nameof(BoxViewTapCommand), typeof(ICommand), typeof(PopOverLayout), default(ICommand), defaultValueCreator: BoxViewTapCommandCreator);
private static object BoxViewTapCommandCreator(BindableObject bindable)
{
return new Command(p => BoxViewTappedExecute(bindable as PopOverLayout, p));
}
private static void BoxViewTappedExecute(PopOverLayout bindable, object parameter)
{
bindable.IsOverlayVisible = !bindable.IsOverlayVisible;
if (!bindable.IsOverlayVisible)
{
bindable.OverlayContent.InputTransparent = true;
}
}
public ICommand BoxViewTapCommand
{
get { return (ICommand) GetValue(BoxViewTapCommandProperty); }
set { SetValue(BoxViewTapCommandProperty, value); }
}
#endregion BoxViewTapCommand
#region OverlayContent
public static BindableProperty OverlayContentProperty = BindableProperty.Create(nameof(OverlayContent), typeof(VisualElement), typeof(PopOverLayout), default(VisualElement));
public VisualElement OverlayContent
{
get { return (VisualElement) GetValue(OverlayContentProperty); }
set { SetValue(OverlayContentProperty, value); }
}
#endregion OverlayContent
#region Content
public static BindableProperty ContentProperty = BindableProperty.Create(nameof(Content), typeof(VisualElement), typeof(PopOverLayout), default(VisualElement));
public VisualElement Content
{
get { return (VisualElement) GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
#endregion Content
#region IsOverlayVisible
public static BindableProperty IsOverlayVisibleProperty = BindableProperty.Create(nameof(IsOverlayVisible), typeof(bool), typeof(PopOverLayout), default(bool));
public bool IsOverlayVisible
{
get { return (bool) GetValue(IsOverlayVisibleProperty); }
set { SetValue(IsOverlayVisibleProperty, value); }
}
#endregion IsOverlayVisible
}
}
App.xaml:
<Style TargetType="{x:Type controls:PopOverLayout}">
<Setter Property="BackgroundColor" Value="#88000000" />
<Setter Property="IsOverlayVisible" Value="False" />
<Setter Property="ControlTemplate">
<Setter.Value>
<ControlTemplate>
<Grid BackgroundColor="{TemplateBinding BackgroundColor}" >
<Grid.RowDefinitions>
<RowDefinition>
<RowDefinition.Height>
<OnIdiom x:TypeArguments="GridLength" Phone="0" Tablet="50" />
</RowDefinition.Height>
</RowDefinition>
<RowDefinition Height="*" />
<RowDefinition>
<RowDefinition.Height>
<OnIdiom x:TypeArguments="GridLength" Phone="0" Tablet="50" />
</RowDefinition.Height>
</RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition>
<ColumnDefinition.Width>
<OnIdiom x:TypeArguments="GridLength" Phone="0" Tablet="50" />
</ColumnDefinition.Width>
</ColumnDefinition>
<ColumnDefinition Width="*" />
<ColumnDefinition>
<ColumnDefinition.Width>
<OnIdiom x:TypeArguments="GridLength" Phone="0" Tablet="50" />
</ColumnDefinition.Width>
</ColumnDefinition>
</Grid.ColumnDefinitions>
<controls:EnhancedContentPresenter Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" Grid.ColumnSpan="3"
Content="{TemplateBinding Content}" />
<BoxView Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" Grid.ColumnSpan="3"
InputTransparent="{TemplateBinding IsOverlayVisible, Converter={StaticResource BooleanInversionConverter}, Mode=OneWay}"
IsVisible="{TemplateBinding IsOverlayVisible}"
BackgroundColor="{TemplateBinding BackgroundColor}">
<BoxView.GestureRecognizers>
<TapGestureRecognizer Command="{TemplateBinding BoxViewTapCommand}"></TapGestureRecognizer>
</BoxView.GestureRecognizers>
</BoxView>
<controls:EnhancedContentPresenter Grid.Row="1" Grid.Column="1" Content="{TemplateBinding OverlayContent}"
InputTransparent="{TemplateBinding IsOverlayVisible, Converter={StaticResource BooleanInversionConverter}, Mode=OneWay}"
IsVisible="{TemplateBinding IsOverlayVisible}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Converter:
public class BooleanInversionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(bool))
throw new NotSupportedException();
if (value.GetType() != typeof(bool))
throw new NotSupportedException();
var casted = (bool)value;
return !casted;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(bool))
throw new NotSupportedException();
if (value.GetType() != typeof(bool))
throw new NotSupportedException();
var casted = (bool)value;
return !casted;
}
}
ContentPresenter:
public class EnhancedContentPresenter : ContentPresenter
{
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (BindingContext != null && this.Content != null)
SetInheritedBindingContext(this.Content, BindingContext);
}
}
Due to the nature of the control it is pretty obvious what i am trying to do i guess: If the overlay is hidden i don't want the overlay to capture tap (or other) events and instead behave like it didn't even exist.
However i can't seem to get this working because something keeps capturing touch events, even if the overlay is hidden and InputTransparent is set to true.
Does this problem ring a bell to anyone?
I'm not sure whether something is still wrong with templatedView (because they sure lack a lot of wpf features), but doing it like this worked just fine:
[ContentProperty(nameof(Content))]
public class PopOverLayout : RelativeLayout
{
#region OverlayContent
public static BindableProperty OverlayContentProperty = BindableProperty.Create(nameof(OverlayContent), typeof(View), typeof(PopOverLayout), default(View), propertyChanged: OverlayContentChanged);
private static void OverlayContentChanged(BindableObject bindable, object oldValue, object newValue)
{
var layout = bindable as PopOverLayout;
if (layout == null)
return;
layout.UpdateOverlayContent();
}
public View OverlayContent
{
get { return (View) GetValue(OverlayContentProperty); }
set { SetValue(OverlayContentProperty, value); }
}
#endregion OverlayContent
#region Content
public static BindableProperty ContentProperty = BindableProperty.Create(nameof(Content), typeof(View), typeof(PopOverLayout), default(View), propertyChanged: ContentChanged);
private static void ContentChanged(BindableObject bindable, object oldvalue, object newvalue)
{
var layout = bindable as PopOverLayout;
if (layout == null)
return;
layout.UpdateDisplayContent();
}
public View Content
{
get { return (View) GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
#endregion Content
#region IsOverlayVisible
public static BindableProperty IsOverlayVisibleProperty = BindableProperty.Create(nameof(IsOverlayVisible), typeof(bool), typeof(PopOverLayout), default(bool), propertyChanged: IsOverlayVisibleChanged);
private static void IsOverlayVisibleChanged(BindableObject bindable, object oldvalue, object newvalue)
{
var layout = bindable as PopOverLayout;
if (layout == null)
return;
bool newState = (bool) newvalue;
if (newState)
{
layout.ShowPopup();
}
else
{
layout.HidePopup();
}
}
public bool IsOverlayVisible
{
get { return (bool) GetValue(IsOverlayVisibleProperty); }
set { SetValue(IsOverlayVisibleProperty, value); }
}
#endregion IsOverlayVisible
protected override void OnParentSet()
{
base.OnParentSet();
UpdateDisplayContent();
UpdateOverlayContent();
}
private View content;
private RelativeLayout popup;
private void UpdateDisplayContent()
{
if (content != null)
this.Children.Remove(content);
if (Content != null)
{
content = Content;
this.Children.Add(content,
Constraint.Constant(0),
Constraint.Constant(0),
Constraint.RelativeToParent(layout => layout.Width),
Constraint.RelativeToParent(layout => layout.Height));
Content.InputTransparent = IsOverlayVisible;
}
}
private void UpdateOverlayContent()
{
if (popup != null)
this.Children.Remove(popup);
if (OverlayContent != null && IsOverlayVisible)
{
ShowPopup();
}
}
private void HidePopup()
{
if (popup != null)
this.Children.Remove(popup);
OverlayContent.InputTransparent = true;
Content.InputTransparent = false;
}
private void ShowPopup()
{
if (popup != null)
this.Children.Remove(popup);
popup = new RelativeLayout();
popup.GestureRecognizers.Add(new TapGestureRecognizer() {Command = new Command(PopUpTapped)});
popup.BackgroundColor = BackgroundColor;
this.Children.Add(popup,
Constraint.Constant(0),
Constraint.Constant(0),
Constraint.RelativeToParent(layout => layout.Width),
Constraint.RelativeToParent(layout => layout.Height));
popup.Children.Add(OverlayContent,
Constraint.RelativeToParent(layout => layout.X + 20),
Constraint.RelativeToParent(layout => layout.Y + 20),
Constraint.RelativeToParent(layout => layout.Width - 40),
Constraint.RelativeToParent(layout => layout.Height - 40));
OverlayContent.InputTransparent = false;
Content.InputTransparent = true;
}
private void PopUpTapped(object o)
{
this.IsOverlayVisible = !IsOverlayVisible;
}
}
In maine, on my button click event handler I do:
private void addIconButton_Click(object sender, EventArgs e)
{
if (test)
{
MessageBox.Show("enters addIcon Main");
Note note = new Note();
note.Modified = DateTimeOffset.Now;
if (note != null)
{
Settings.NotesList.Add(note); //this causes the issue.
//Settings.NotesList[0] = note;
}
Settings.CurrentNoteIndex = 0;
test = false;
MessageBox.Show("right before navigate");
this.NavigationService.Navigate(new Uri("/DetailsPage.XAML", UriKind.Relative));
MessageBox.Show("after navigate");
}
//DetailsPage mynewPage = new DetailsPage();
//this.Content = mynewPage;
}
and on my on navigatedTo I do:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
MessageBox.Show("enters onNav Main");
DataContext = null;
DataContext = Settings.NotesList;
Settings.CurrentNoteIndex = -1;
Listbox.SelectedIndex = -1;
if (Settings.NotesList != null)
{
if (Settings.NotesList.Count == 0)
{
Notes.Text = "No Notes";
}
else
{
Notes.Text = "";
}
}
}
Inside my front end code I do:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="Listbox" SelectionChanged="listbox_SelectionChanged" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Width="800" MinHeight="60">
<StackPanel>
<TextBlock x:Name="Title" VerticalAlignment="Center" FontSize="{Binding TextSize}" Text="{Binding Name}"/>
<TextBlock x:Name="Date" VerticalAlignment="Center" FontSize="{Binding TextSize}" Text="{Binding Modified,
Mode=TwoWay, Converter={StaticResource dateConverter}}"/>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
I have an observable collections in a static class where in the constructor I set it to:
public static class Settings
{
public static ObservableCollection<Note> NotesList;
static IsolatedStorageSettings settings;
private static int currentNoteIndex;
static Settings()
{
NotesList = new ObservableCollection<Note>();
settings = IsolatedStorageSettings.ApplicationSettings;
MessageBox.Show("enters constructor settings");
}
and then inside the Notes class it just looks like this:
public class Note
{
public DateTimeOffset Modified { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int TextSize { get; set; }
}
when I click the app button and it calls the invent handler right after the navigationService is called I get:
// Code to execute on Unhandled Exceptions
private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
System.Diagnostics.Debugger.Break();
}
}
this only happens when Settings.NotesList.Add(note); is added in the addIconButton_click method..
Any suggestions???
I fixed it by setting default values to the instance variables in the Notes class inside the constructor...