Can't get InputTransparent to work on custom PopOver Control - xamarin.forms

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

Related

Image with rounded corners

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"/>

Focus on Next Entry after max length of 1

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.

how to Add “View More” and view less at the end of label after 3 lines

I have label with 10 lines of data in it but need to show only 3 line after that need to add view more button and after expanding view less , how to add it in xamarin form
According to your description, you can use custom control to do this.
Firstly, you need to create contentview, name LargeTextLabel.xaml.
<ContentView.Content>
<StackLayout Orientation="Vertical">
<Label
x:Name="SmallLabel"
BackgroundColor="Beige"
HeightRequest="50"
IsVisible="true" />
<Label
x:Name="FullLabel"
BackgroundColor="BurlyWood"
HeightRequest="200"
IsVisible="false" />
<Button x:Name="ExpandContractButton" Text="See More" />
</StackLayout>
</ContentView.Content>
Then create BindableProperty by code behind.
public partial class LargeTextLabel : ContentView
{
public LargeTextLabel ()
{
InitializeComponent ();
}
public static readonly BindableProperty ExpandedProperty = BindableProperty.Create(
nameof(LargeTextLabel),
typeof(bool),
typeof(ContentView),
false,
BindingMode.TwoWay,
propertyChanged: (bindable, oldValue, newValue) =>
{
if (newValue != null && bindable is LargeTextLabel control)
{
var actualNewValue = (bool)newValue;
control.SmallLabel.IsVisible = !actualNewValue;
control.FullLabel.IsVisible = actualNewValue;
control.ExpandContractButton.Text = actualNewValue ? "See Less" : "See More";
}
});
public bool Expanded { get; set; }
public static readonly BindableProperty TextProperty = BindableProperty.Create(
nameof(LargeTextLabel),
typeof(string),
typeof(ContentView),
default(string),
BindingMode.TwoWay,
propertyChanged: (bindable, oldValue, newValue) =>
{
if (newValue != null && bindable is LargeTextLabel control)
{
var actualNewValue = (string)newValue;
control.SmallLabel.Text = actualNewValue;
control.FullLabel.Text = actualNewValue;
}
});
public string Text { get; set; }
public static readonly BindableProperty CommandProperty = BindableProperty.Create(
nameof(LargeTextLabel),
typeof(ICommand),
typeof(ContentView),
default(Command),
BindingMode.TwoWay,
propertyChanged: (bindable, oldValue, newValue) =>
{
if (newValue != null && bindable is LargeTextLabel control)
{
var actualNewValue = (ICommand)newValue;
control.ExpandContractButton.Command = actualNewValue;
}
});
public ICommand Command { get; set; }
}
Now, you can use this custom control in your page.
<ContentPage.Content>
<StackLayout>
<local:LargeTextLabel
Command="{Binding ExpandContractCommand}"
Expanded="{Binding TextExpanded}"
Text="{Binding LabelText}" />
</StackLayout>
</ContentPage.Content>
Create some property to bind this custom control, please implement INotifyPropertychanged to notify value update.
public partial class Page3 : ContentPage, INotifyPropertyChanged
{
private string _LabelText;
public string LabelText
{
get { return _LabelText; }
set
{
_LabelText = value;
RaisePropertyChanged("LabelText");
}
}
private ICommand _ExpandContractCommand;
private bool _TextExpanded;
public bool TextExpanded
{
get { return _TextExpanded; }
set
{
_TextExpanded = value;
RaisePropertyChanged("TextExpanded");
}
}
public Page3 ()
{
InitializeComponent ();
LabelText = "Can any one help me on this?\nI want set read more option for multiline text end of label when i click on that read more it will expand or navigate to the any other page. Please help me on this .\n\nThanks in advance.";
this.BindingContext = this;
}
public ICommand ExpandContractCommand
{
get
{
if (_ExpandContractCommand == null)
{
_ExpandContractCommand = new Command(() => {
TextExpanded = !TextExpanded;
});
}
return _ExpandContractCommand;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

program blows up on navigate using observable collections WP7

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...

Silverlight databound ComboBox not refreshing when SelectedValue set

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.

Resources