Image with rounded corners - xamarin.forms

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

Related

Changing Property in code behind does not update view

When I change my property which is a model object, The view does not update unless I reassign the binding context. I am not using mvvm, so no view model.
public partial class MainPage : ContentPage
{
private MySource _myCurrentSource = new MySource("yolor");
public MySource MyCurrentSource {
get { return _myCurrentSource; }
set {_myCurrentSource = value; }
}
public MainPage()
{
InitializeComponent();
MyCurrentSource = _myCurrentSource;
MainStack.BindingContext = MyCurrentSource;
label.SetBinding(Label.TextProperty, new Binding("SourceString"));
}
private void Button_Clicked(object sender, EventArgs e)
{
MyCurrentSource = new MySource("new string");
//property changed
MainStack.BindingContext = MyCurrentSource;
}
}
I want to get rid of : MainStack.BindingContext = MyCurrentSource;
This is what my xaml looks like
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataBindingPlayGround.MainPage">
<StackLayout Padding="10, 0" x:Name="MainStack" HorizontalOptions="Center"
VerticalOptions="CenterAndExpand">
<Label x:Name="label" Text="TEXT" FontSize="48" />
<Button Text="Change" Clicked="Button_Clicked"/>
</StackLayout>
</ContentPage>
Model class:
public class MySource
{
public MySource(string str)
{
SourceString = str;
}
public string SourceString { get; set; }
}
Modify MySource class as follows to have a try:
public class MySource : INotifyPropertyChanged
{
public MySource(string str)
{
sourceString = str;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string sourceString;
public double SourceString
{
set
{
if (sourceString != value)
{
sourceString = value;
OnPropertyChanged("SourceString");
}
}
get
{
return sourceString;
}
}
}
=============================Update=================================
Although not understanding the logic of your application, if you want to make MyCurrentSource works. You will also need to use INotifyPropertyChanged:
public partial class MainPage : ContentPage ,INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private MySource _myCurrentSource;
public MySource MyCurrentSource
{
set
{
if (_myCurrentSource != value)
{
_myCurrentSource = value;
OnPropertyChanged("MyCurrentSource");
}
}
get
{
return _myCurrentSource;
}
}
public MainPage()
{
InitializeComponent();
_myCurrentSource = new MySource("yolor");
//MyCurrentSource = _myCurrentSource;
MainStack.BindingContext = _myCurrentSource ;
label.SetBinding(Label.TextProperty, new Binding("SourceString"));
}
private void Button_Clicked(object sender, EventArgs e)
{
_myCurrentSource = new MySource("new string");
//property changed
MainStack.BindingContext = _myCurrentSource;
}
}
Or you can directly set new Model when BindingContext.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
MainStack.BindingContext = new MySource("yolor");
label.SetBinding(Label.TextProperty, new Binding("SourceString"));
}
private void Button_Clicked(object sender, EventArgs e)
{
//property changed
MainStack.BindingContext = new MySource("new string");
}
}
This is finally what worked for me, in case anyone has similar situation:
Source classes below:
public class FirstSource
{
public string sourceString;
public string SourceString { get; set; }
public int SourceOneProp2 { get; set; }
public FirstSource(string str, int num)
{
SourceString = str;
SourceOneProp2 = num;
}
}
public class SecondSource
{
public string ExampleField { get; set; }
public int SourceTwoProp2 { get; set; }
public SecondSource(string exampleField, int num)
{
ExampleField = exampleField;
SourceTwoProp2 = num;
}
}
ViewModel (Decided to use to make task easier)
class MainPageViewModel : BaseViewModel
{
private FirstSource _sourceOne;
private SecondSource _sourceTwo;
public FirstSource SourceOne
{
get { return _sourceOne; }
set { SetValue(ref _sourceOne, value); }
}
public SecondSource SourceTwo
{
get { return _sourceTwo; }
set { SetValue(ref _sourceTwo, value); }
}
}
Code Behind:
public partial class MainPage : ContentPage
{
int counter = 0;
private MainPageViewModel ViewModel
{
get { return BindingContext as MainPageViewModel; }
set { BindingContext = value; }
}
public MainPage()
{
InitializeComponent();
ViewModel = new MainPageViewModel();
ViewModel.SourceOne = new FirstSource("init1", 10);
ViewModel.SourceTwo = new SecondSource("init2", 20);
}
private void Button_Clicked(object sender, EventArgs e)
{
counter += 1;
ViewModel.SourceOne = new FirstSource("Changed1", 100+counter);
}
private void Button_Clicked_1(object sender, EventArgs e)
{
counter += 1;
ViewModel.SourceTwo = new SecondSource("Changed2", 200+counter);
}
}
XAML/UI
<StackLayout Padding="10, 0" x:Name="MainStack" HorizontalOptions="Center" VerticalOptions="Start">
<Label x:Name="label" Text="{Binding Path=SourceOne.SourceString}" FontSize="48" />
<Label Text="{Binding Path=SourceOne.SourceOneProp2}" />
<Button Text="Change" Clicked="Button_Clicked"/>
<StackLayout Padding="10, 0" x:Name="SecondStack">
<Label x:Name="secondLabel" Text="{Binding Path=SourceTwo.ExampleField}" FontSize="48" />
<Label Text="{Binding Path=SourceTwo.SourceTwoProp2}" />
<Button Text="Change" Clicked="Button_Clicked_1"/>
</StackLayout>
</StackLayout>

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.

Always keep on keyboard in android on focus to next 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;
}
}
}

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