CollectionView SelectionChange event not working properly in Android (.Net MAUI) - xamarin

I'm trying to create custom tabs control in .Net MAUI, for that, I had first tried it with ScrollView and BindableStackLayout control but in that, I'm facing a problem.
Reported here Custom tabs with ScrollView bug
So, as an alternative approach or work-around, I have tried to develop the same Tabs control using CollectionView.
This alternative approach is working fine in iOS but not working properly in Android.
There is one problem that is common in both Android and iOS. I have taken BoxView control as an Indicator for the selected tab. That I'm going to show only for the Selected tab but this just shows in the first tab, when I click on other tabs the tabs get changed but it does not get hidden from the first tab and get visible in the other selected tab.
I have used the visual state manager with white color for the selected state because it gives looks like an indicator which I',m trying to create using BoxView. But this also shows Selected item for Android only when that view gets loads for iOS I have to select the tab first then only it shows the selected color there.
Here is what I have done:
MainPage.xaml
<ContentPage.Content>
<Grid RowDefinitions="50, *" RowSpacing="0">
<CollectionView x:Name="TabsView"
Grid.Row="0"
ItemsSource="{Binding Tabs,Mode=TwoWay}"
ItemsUpdatingScrollMode="KeepItemsInView"
ItemSizingStrategy="MeasureAllItems"
SelectedItem="{Binding SelectedTab}"
SelectionChangedCommand="{Binding Path=BindingContext.TabChangedCommand,Source={x:Reference TabsView}}"
SelectionChangedCommandParameter="{Binding SelectedTab}"
SelectionMode="Single">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Horizontal"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid RowSpacing="0" RowDefinitions="*, 3">
<Label Grid.Row="0"
Text="{Binding TabTitle}"
TextColor="White"
BackgroundColor="navy"
Padding="20,0"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
FontSize="12" />
<BoxView Grid.Row="1" Color="{Binding BoxColor}"
IsVisible="{Binding IsSelected}"/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal"/>
<VisualState Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="White" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<tabs:ParentRecordTabView Grid.Row="1"
IsVisible="{Binding IsParentRecordTabVisible}"
VerticalOptions="FillAndExpand"/>
<tabs:AdditionalInfoTabView Grid.Row="1"
IsVisible="{Binding IsAdditionalInfoTabVisible}"
VerticalOptions="FillAndExpand" />
</Grid>
</ContentPage.Content>
MainPageViewModel.cs
public class MainPageViewModel : BaseViewModel
{
public MainPageViewModel()
{
GetTabs();
}
private bool _isParentRecordTabVisible = true;
private bool _isAdditionalInfoTabVisible;
private ObservableCollection<TabViewModel> _tabs { get; set; }
private TabViewModel _selectedTab { get; set; }
public bool IsParentRecordTabVisible
{
get => _isParentRecordTabVisible;
set { _isParentRecordTabVisible = value; OnPropertyChanged(nameof(IsParentRecordTabVisible)); }
}
public bool IsAdditionalInfoTabVisible
{
get => _isAdditionalInfoTabVisible;
set { _isAdditionalInfoTabVisible = value; OnPropertyChanged(nameof(IsAdditionalInfoTabVisible)); }
}
public ObservableCollection<TabViewModel> Tabs
{
get => _tabs;
set { _tabs = value; OnPropertyChanged(nameof(Tabs)); }
}
public TabViewModel SelectedTab
{
get => _selectedTab;
set
{
_selectedTab = value;
OnPropertyChanged(nameof(SelectedTab));
}
}
public ICommand TabChangedCommand { get { return new Command<TabViewModel>(ChangeTabClick); } }
private void GetTabs()
{
Tabs = new ObservableCollection<TabViewModel>();
Tabs.Add(new TabViewModel { TabId = 1, IsSelected = true, TabTitle = "Parent record" });
Tabs.Add(new TabViewModel { TabId = 2, TabTitle = "Additional Info" });
Tabs.Add(new TabViewModel { TabId = 3, TabTitle = "Contacts" });
Tabs.Add(new TabViewModel { TabId = 4, TabTitle = "Previous inspections" });
Tabs.Add(new TabViewModel { TabId = 5, TabTitle = "Attachments" });
SelectedTab = Tabs.FirstOrDefault();
}
public void ChangeTabClick(TabViewModel tab)
{
Tabs.All((arg) =>
{
if (arg.TabId == tab.TabId)
{
arg.IsSelected = true;
}
else
{
arg.IsSelected = false;
}
return true;
});
SelectedTab = Tabs.Where(t => t.IsSelected == true).FirstOrDefault();
switch (SelectedTab.TabId)
{
case 1:
IsParentRecordTabVisible = true;
IsAdditionalInfoTabVisible = false;
break;
case 2:
IsParentRecordTabVisible = false;
IsAdditionalInfoTabVisible = true;
break;
}
}
}
TabViewModel.cs
public class TabViewModel : BaseViewModel
{
private bool _IsSelected;
public bool IsSelected
{
get { return _IsSelected; }
set
{
_IsSelected = value;
OnPropertyChanged(nameof(IsSelected));
}
}
private int _TabId;
public int TabId
{
get { return _TabId; }
set
{
_TabId = value;
OnPropertyChanged(nameof(TabId));
}
}
private string _TabTitle;
public string TabTitle
{
get { return _TabTitle; }
set
{
_TabTitle = value;
OnPropertyChanged(nameof(TabTitle));
}
}
}
Note: This same approach again works fine in Xamarin.Forms (Visual Studio 2019), this just not working in MAUI, so does anyone notice something like this?
How to Reproduce error: check github

Remove the BoxView default style in your project. Resource> Styles> Styles.xml
<Style TargetType="BoxView">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
You could use the IsVisible property to show the BoxView or not instead of binding color with BoxColor. Remove the SelectionChangedCommand, SelectionChangedCommandParameter and VisualStateManager.VisualStateGroups in CollectionView as well.
<BoxView Grid.Row="1"
Color="Yellow"
IsVisible="{Binding IsSelected}"/>
Set the SelectedTab property like below.
public TabViewModel SelectedTab
{
get => _selectedTab;
set
{
_selectedTab = value;
SetSelection();
OnPropertyChanged(nameof(SelectedTab));
}
}
private void SetSelection()
{
foreach (var item in Tabs)
{
item.IsSelected = false;
}
SelectedTab.IsSelected = true;
}

Related

Getting StateLayout with CustomState to show image thumbnail

In my Xamarin Forms 5 app, I have a form users will fill out to create a post -- similar to Facebook posts.
The effect I'm trying to create is this:
There's an "Add Image" button that allows user to upload an image. Once the image is uploaded, I want to no longer display the button but display a thumbnail version of the uploaded image.
Here's what my XAML looks like:
<StackLayout
xct:StateLayout.CurrentState="{Binding MainState.None}"
xct:StateLayout.CurrentCustomStateKey="{Binding PostImageState}">
<xct:StateLayout.StateViews>
<xct:StateView StateKey="Custom" CustomStateKey="Image set">
<Image
Grid.Row="0"
Grid.Column="0"
Source="{Binding PostImageUrl}"
WidthRequest="30"
HeightRequest="30"/>
</xct:StateView>
</xct:StateLayout.StateViews>
<Button
Text="Add Image"
Command="{Binding AddImageCommand}"
BackgroundColor="{StaticResource SecondaryBackground}"
WidthRequest="100"
HeightRequest="35"
HorizontalOptions="Start"
Margin="10,0,0,0"/>
</StackLayout>
Here's an abbreviated version of my view model:
public class MyViewModel : BaseViewModel
{
public LayoutState _mainState;
string postImageUrl { get; set; }
string postImageState { get; set; } = "No image";
public MyViewModel()
{
Title = string.Empty;
IsBusy = true;
MainState = LayoutState.None;
AddImageCommand = new AsyncCommand(Add_Image_Tapped);
}
public LayoutState MainState
{
get => _mainState;
set => SetProperty(ref _mainState, value);
}
public string PostImageUrl
{
get => postImageUrl;
set
{
if (postImageUrl == value)
return;
postImageUrl = value;
OnPropertyChanged();
}
}
public string PostImageState
{
get => postImageState;
set
{
if (postImageState == value)
return;
postImageState = value;
OnPropertyChanged();
}
}
async Task Add_Image_Tapped()
{
// Upload image
// Once upload is done
PostImageUrl = uploadedFileUrl;
PostImageState = "Image set";
}
}
I haven't been able to get this to work. Currently, it's not even showing the "Add Image" button. Where am I making a mistake?
There are several problems with your code.
1.Since you use Binding for xct:StateLayout.CurrentState, we should bind it to a variable in ViewModel, here we should use MainState not MainState.None:
xct:StateLayout.CurrentState="{Binding MainState}"
2.Based on your requirement, we can use the value from LayoutState enumeration(for example StateKey="Success"),, we don't need add custom states.
3.If we want to hidden the button once uploading the image, we can bind MainState to property IsVisible of button , but need use Converter StateToBooleanConverter to convert State to bool.
Based on your code ,I created a simple demo, and it works properly on my side.
You can refer to the following code:
MyPage.Xaml
<?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:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:viewmodel="clr-namespace:FormApp0314.ViewModel"
x:Class="FormApp0314.TestPage1">
<ContentPage.BindingContext>
<viewmodel:MyViewModel></viewmodel:MyViewModel>
</ContentPage.BindingContext>
<ContentPage.Resources>
<xct:StateToBooleanConverter x:Key="StateToBooleanConverter" />
</ContentPage.Resources>
<StackLayout
xct:StateLayout.CurrentState="{Binding MainState}">
<xct:StateLayout.StateViews>
<xct:StateView StateKey="Success" CustomStateKey="Image set">
<Image
Grid.Row="0"
Grid.Column="0"
Source="{ Binding PostImageUrl}"
WidthRequest="60"
HeightRequest="60"/>
</xct:StateView>
</xct:StateLayout.StateViews>
<Button
Text="Add Image"
Command="{Binding AddImageCommand}"
IsVisible="{Binding MainState, Converter={StaticResource StateToBooleanConverter}, ConverterParameter={x:Static xct:LayoutState.None}}"
WidthRequest="100"
HeightRequest="35"
HorizontalOptions="Start"
Margin="10,0,0,0" />
</StackLayout>
</ContentPage>
MyViewModel.cs
public class MyViewModel: BaseViewModel
{
public LayoutState _mainState;
string postImageUrl;
string postImageState = "No image";
public ICommand AddImageCommand { get; }
public MyViewModel()
{
MainState = LayoutState.None;
PostImageUrl = "bell.png";
AddImageCommand = CommandFactory.Create(Add_Image_Tapped);
}
async Task Add_Image_Tapped()
{
MainState = LayoutState.Success;
await Task.Delay(3000);
MainState = LayoutState.None;
}
public LayoutState MainState
{
get => _mainState;
set => SetProperty(ref _mainState, value);
}
public string PostImageUrl
{
get => postImageUrl;
set => SetProperty(ref postImageUrl, value);
}
public string PostImageState
{
get => postImageState;
set => SetProperty(ref postImageState, value);
}
}

how to refer to item in an enumeration within a ListView

I have a class MRU which contains an ObservableCollection of type string. I would like to display the items within the collection on a PopupPage in a ListView.
[DefaultBindingProperty("Items")]
public class MRU<T> :INotifyPropertyChanged
{
public ObservableCollection<T> Items { get; } = new ObservableCollection<T>();
// This is the maximum number of items that will be saved to persistent storage,
// in use the list can grow beyond this number
private int _MaxItems = 5;
public int MaxItems
{
get { return _MaxItems; }
set {
_MaxItems = value;
TrimItems();
OnPropertyChanged();
}
}
public void Add(T t) {
int idx = Items.IndexOf(t);
if (idx > 0)
{
Items.Move(idx, 0);
}
else if (idx == -1)
{
Items.Insert(0, t);
}
}
public T Pop(int i)
{
if (i > 0 && i < Items.Count)
{
Items.Move(i, 0);
}
return Items[0];
}
private void TrimItems()
{
while (Items.Count > _MaxItems)
{
Items.RemoveAt(Items.Count - 1);
}
}
public void Save()
{
App.Current.Properties["MRU"] = SaveToJSON();
App.Current.SavePropertiesAsync();
}
public string SaveToJSON()
{
TrimItems();
string jsonString = JsonSerializer.Serialize(Items);
return jsonString;
}
public int LoadFromJSON(string jsonString)
{
Items.Clear();
ObservableCollection<T> restore = JsonSerializer.Deserialize<ObservableCollection<T>>(jsonString);
foreach (var t in restore)
{
if (Items.Count == _MaxItems) break;
Items.Add(t);
}
return Items.Count;
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public event PropertyChangedEventHandler PropertyChanged;
}
I can display it in my page using the following XAML
<StackLayout
VerticalOptions="Center"
HorizontalOptions="Center"
Padding="0,20">
<Frame BackgroundColor="White" CornerRadius="15">
<StackLayout>
<Label Text="Most Recently Used" FontSize="Large" BackgroundColor="LightBlue" HorizontalOptions="CenterAndExpand" Padding="20,4"/>
<StackLayout >
<ListView ItemsSource="{Binding Items}" SelectionMode="Single">
<!--<ListView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Vertical">
<Label Text="{Binding XXXXXX}" VerticalOptions="Center" HorizontalTextAlignment="End" FontSize="Medium"/>
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate> -->
</ListView>
</StackLayout>
</StackLayout>
</Frame>
</StackLayout>
Note the commented out ListView.ItemTemplate section. I would like to display the items within an ItemTemplate so that I can add buttons for each object, however I don't know how to declare it . What should XXXXX be? I've tried a zillion unlikely things
In your xaml page, you created a ListView. I found that you forgot to add <ViewCell></ViewCell> tags.
You should use a structure like the following:
<ListView x:Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you have an ObservableCollection<string>, to access the string in your binding, you want to do:
Text="{Binding}"
or
Text="{Binding .}"
Both say that you want to bind to whole object. Since the "whole object" in this case is a string, that's what you'll get.

Best approach for show/hide password toggle functionality in Xamarin traditional approach

We are working on show/ hide password toggle functionality in Xamarin traditional approach. What is the best place to implement it? Is it in Xamarin.iOS &. Droid or in Xamarin.Core?
If it is in Xamarin.Core, can you let us know the process. Is it by value convertors?
Thanks in advance.
Recently, Microsoft MVP Charlin, wrote an article showing how to do this using Event Triggers in the Xamarin Forms code:
She was able to do it simply using a new ShowPasswordTriggerAction of type TriggerAction that implemented INotifyPropertyChanged.
Therein, she created a HidePassword bool property that Invoke a PropertyChanged event which changes the Source of the Icon image:
protected override void Invoke(ImageButton sender)
{
sender.Source = HidePassword ? ShowIcon : HideIcon;
HidePassword = !HidePassword;
}
Then place the Entry and ImageButton inside a layout (like a Frame or horizontally oriented LinearLayout) as shown:
<Entry Placeholder="Password"
IsPassword="{Binding Source={x:Reference ShowPasswordActualTrigger}, Path=HidePassword}"/>
<ImageButton VerticalOptions="Center"
HeightRequest="20"
HorizontalOptions="End"
Source="ic_eye_hide">
<ImageButton.Triggers>
<EventTrigger Event="Clicked">
<local:ShowPasswordTriggerAction ShowIcon="ic_eye"
HideIcon="ic_eye_hide"
x:Name="ShowPasswordActualTrigger"/>
</EventTrigger>
</ImageButton.Triggers>
</ImageButton>
We always use custom controls to show/hide password while entering the password using effects.
Android:
Create the control manually in ‘OnDrawableTouchListener’ method where, we are adding the ShowPass and HidePass icons to the entry control, changing them on the basis of user touch action and attaching it on effect invocation which will be fired when the effect is added to the control.
public class OnDrawableTouchListener : Java.Lang.Object, Android.Views.View.IOnTouchListener
{
public bool OnTouch(Android.Views.View v, MotionEvent e)
{
if (v is EditText && e.Action == MotionEventActions.Up)
{
EditText editText = (EditText)v;
if (e.RawX >= (editText.Right - editText.GetCompoundDrawables()[2].Bounds.Width()))
{
if (editText.TransformationMethod == null)
{
editText.TransformationMethod = PasswordTransformationMethod.Instance;
editText.SetCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, Resource.Drawable.ShowPass, 0);
}
else
{
editText.TransformationMethod = null;
editText.SetCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, Resource.Drawable.HidePass, 0);
}
return true;
}
}
return false;
}
}
Result:
IOS:
Create the control manually in 'ConfigureControl' method where we are adding the ShowPass and HidePassicons to the entry control, changing them on the basis of user touch action; and attaching it on effect invocation which will be fired when the effect will be added to the control.
private void ConfigureControl()
{
if (Control != null)
{
UITextField vUpdatedEntry = (UITextField)Control;
var buttonRect = UIButton.FromType(UIButtonType.Custom);
buttonRect.SetImage(new UIImage("ShowPass"), UIControlState.Normal);
buttonRect.TouchUpInside += (object sender, EventArgs e1) =>
{
if (vUpdatedEntry.SecureTextEntry)
{
vUpdatedEntry.SecureTextEntry = false;
buttonRect.SetImage(new UIImage("HidePass"), UIControlState.Normal);
}
else
{
vUpdatedEntry.SecureTextEntry = true;
buttonRect.SetImage(new UIImage("ShowPass"), UIControlState.Normal);
}
};
vUpdatedEntry.ShouldChangeCharacters += (textField, range, replacementString) =>
{
string text = vUpdatedEntry.Text;
var result = text.Substring(0, (int)range.Location) + replacementString + text.Substring((int)range.Location + (int)range.Length);
vUpdatedEntry.Text = result;
return false;
};
buttonRect.Frame = new CoreGraphics.CGRect(10.0f, 0.0f, 15.0f, 15.0f);
buttonRect.ContentMode = UIViewContentMode.Right;
UIView paddingViewRight = new UIView(new System.Drawing.RectangleF(5.0f, -5.0f, 30.0f, 18.0f));
paddingViewRight.Add(buttonRect);
paddingViewRight.ContentMode = UIViewContentMode.BottomRight;
vUpdatedEntry.LeftView = paddingViewRight;
vUpdatedEntry.LeftViewMode = UITextFieldViewMode.Always;
Control.Layer.CornerRadius = 4;
Control.Layer.BorderColor = new CoreGraphics.CGColor(255, 255, 255);
Control.Layer.MasksToBounds = true;
vUpdatedEntry.TextAlignment = UITextAlignment.Left;
}
}
Result:
For more details, please refer to the article below.
https://www.c-sharpcorner.com/article/xamarin-forms-tip-implement-show-hide-password-using-effects/
You could download the source file from GitHub for reference.
https://github.com/techierathore/ShowHidePassEx.git
You can use the PhantomLib library to do this. It has a control which allows you to have a show/hide icon for the password with examples. Just install the nuget. https://github.com/OSTUSA/PhantomLib
Your UI codes like this having a entry and image button
source to named accroding to your ui
<Frame CornerRadius="30" Background="white" Padding="0" HeightRequest="43" Margin="0,17,0,0">
<StackLayout Orientation="Horizontal">
<Entry x:Name="eLoginPassword"
Margin="15,-10,0,-15"
HorizontalOptions="FillAndExpand"
IsPassword="True"
Placeholder="Password"/>
<ImageButton
x:Name="ibToggleLoginPass"
Clicked="IbToggleLoginPass"
Source="eyeclosed"
Margin="0,0,13,0"
BackgroundColor="White"
HorizontalOptions="End"
/>
</StackLayout>
</Frame>
in C# code
// IbToggleLoginPass your defined method in xaml
//"eye" is drawable name for open eye and "eyeclosed" is drawable name for closed eye
private void IbToggleLoginPass(object sender, EventArgs e)
{
bool isPass = eLoginPassword.IsPassword;
ibToggleLoginPa`enter code here`ss.Source = isPass ? "eye" : "eyeclosed";
eLoginPassword.IsPassword = !isPass;
}
Trigger and a command
The trigger changes the icon, and the command changes the entry.
View xaml
<Grid>
<Entry Placeholder="Password" Text="{Binding Password, Mode=TwoWay}" IsPassword="{Binding IsPassword}" />
<ImageButton BackgroundColor="Transparent" WidthRequest="24" VerticalOptions="Center" TranslationY="-5" TranslationX="-10" HorizontalOptions="End"
Command="{Binding ToggleIsPassword}"
Source="eye" >
<ImageButton.Triggers>
<DataTrigger TargetType="ImageButton" Binding="{Binding IsPassword}" Value="True" >
<Setter Property="Source" Value="eye-slash" />
</DataTrigger>
</ImageButton.Triggers>
</ImageButton>
</Grid>
And in my ViewModel
private bool _IsPassword = true;
public bool IsPassword
{
get
{
return _IsPassword;
}
set
{
_IsPassword = value;
RaisePropertyChanged(() => IsPassword);
}
}
public ICommand ToggleIsPassword => new Command(() => IsPassword = !IsPassword);

make Xamarin DisplayActionSheet select, display value in Label

I have wired up my ContentPage to an instance of a class (g), and this case works fine:
open the page
enter values in the Entry boxes
make a selection from a DisplayActionSheet
click Save
OnSave all the values from the UI are in g, but the value from the DisplayActionSheet is not in the UI where I expect it.
After the DisplayActionSheet thing runs, I want a value for AisleDepthText to display in the UI.
Here is the class that I instantiate into a variable, g
public class GroceryItemForSaving
{
public GroceryItemForSaving() { }
public event PropertyChangedEventHandler PropertyChanged;
private string _AisleDepth;
public string AisleDepth
{
get
{
return _AisleDepth;
}
set
{
_AisleDepth = value;
OnPropertyChanged();
}
}
private string _AisleDepthText;
public string AisleDepthText
{
get
{
return _AisleDepthText;
}
set
{
_AisleDepthText = value;
OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I make g the BindingContext like this:
public NewGrocery()
{
InitializeComponent();
BindingContext = g;
}
Here is the relevant XAML.
<Label Text="GroceryName" Grid.Row="0" Grid.Column="1" ></Label>
<Entry Text="{Binding GroceryName}" Grid.Row="0" Grid.Column="2" ></Entry>
<Label Text="Aisle" Grid.Row="1" Grid.Column="1" ></Label>
<Entry Text="{Binding Aisle}" Grid.Row="1" Grid.Column="2"></Entry>
<Label Text="Aisle Depth" Grid.Row="2" Grid.Column="1" ></Label>
<Label Text="{Binding AisleDepthText, Mode=OneWay}" Grid.Row="2" Grid.Column="2" ></Label>
<Button Clicked="ShowAisleDepthChoices" Grid.Row="2" Grid.Column="3" Text="Aisle Depth" ></Button>
The button click handler ShowAisleDepthChoices, makes the ActionSheet display. In the code for that I set the values for AisleDepth and AisleDepthText like this:
public async void ShowAisleDepthChoices(object sender, EventArgs e)
{
var AisleDepth = 0;
var SelectedAisleDepth = await DisplayActionSheet("Aisle Depth", "Cancel", null, "Front", "Middle", "Back", "Back Wall");
switch (SelectedAisleDepth)
{
case "Front":
AisleDepth = 1;
break;
case "Middle":
AisleDepth = 2;
break;
case "Back":
AisleDepth = 3;
break;
case "Back Wall":
AisleDepth = 4;
break;
}
g.AisleDepthText = SelectedAisleDepth;
g.AisleDepth = AisleDepth.ToString();
}
Then after that no value appears in AisleDepthText Label, but when I click Save, the values are in g.AsileDepthText and g.AisleDept exactly where I expect them. NOTE: I can enter a GroceryName directly in the UI and it ends up in g.GroceryName on save.
What do I need to do to make the value for g.AisleDepthText appear in the UI after the DisplayActionSheet does its thing?
GroceryItemForSaving needs to implement INotifyPropertyChanged. You have to code to satisfy the implementation, but you're not declaring that the class uses the interface, so your binding isn't updating like it should.
public class GroceryItemForSaving : INotifyPropertyChanged

Windows Phone 7 Listbox not displaying

So I have a listbox I am trying to make for an "achievement page". Everything works good if I use a List but when I switch it to a List there is nothing displayed, not even from the xaml...
public partial class achievementPage : PhoneApplicationPage
{
public string Description { get; set; }
public string Type { get; set; }
public achievementPage()
{
InitializeComponent();
loadListbox();
}
public achievementPage(string achievementGet, string d1)
{
}
public void loadListbox()
{
achievementStoreage.loadData();
List<achievementPage> achievementList = new List<achievementPage>();
achievementList.Add(new achievementPage(achievementStoreage.achievement1, "This is a test"));
achievementList.Add(new achievementPage(achievementStoreage.achievement2, "This is another test"));
//List<string> achievementList = new List<string>();
//achievementList.Add("Sup");
achievementListBox.ItemsSource = achievementList;
}
}
<ListBox Name="achievementListBox" Margin="0,0,0,0" >
<ListBox.ItemTemplate>
<DataTemplate>
<Button Width="776" Height="120" BorderBrush="Black">
<Button.Content>
<StackPanel Orientation="Horizontal" Height="50">
<StackPanel Orientation="Horizontal" Height="40">
<TextBlock Width="150" Foreground="Black" FontSize="22" Text="Description:" Height="40"/>
</StackPanel>
</StackPanel>
</Button.Content>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
All I get is a blank page.. P.S don't worry about achievementStoreage, it is working properly.(just where I get stored data)
Frankly, it seems that you are simply not allowed to use ItemTemplates along with UIElements as the items' DataContexts. I've tried with:
achievementListBox.ItemsSource = new [] {"a","b"};
and both items were visible and printed dummy "description" texts, but none of the following lines I've tried has presented anything at all:
achievementListBox.ItemsSource = new [] { new TextBlock(), new TextBlock()};
achievementListBox.ItemsSource = new [] { new Rectangle(), new Rectangle()};
achievementListBox.ItemsSource = new [] { new Grid(), new Grid()};
Trying with your custom page - the same. No items shown.
This is very misleading. The items were shown, but look at the lines above: the controls were created empty, with no contents set!.
It turns out, that if the ListBox detects that the Item is an UIElement, then it does not use the ItemTemplate, but it presents that UIElement directly!
achievementListBox.ItemsSource = new[] { new TextBlock() { Text = "bbb" }, new TextBlock() { Text = "eee" } };
achievementListBox.ItemsSource = new[] { new Rectangle() { Fill = new SolidColorBrush(Colors.Red), Width = 30, Height = 10 }, new Rectangle() { Fill = new SolidColorBrush(Colors.Green), Width = 30, Height = 10 } };
var gridA = new Grid() { Width = 110, Height = 40 }; gridA.Children.Add(new Rectangle() { Fill = new SolidColorBrush(Colors.Red) });
var gridB = new Grid() { Width = 110, Height = 40 }; gridB.Children.Add(new Rectangle() { Fill = new SolidColorBrush(Colors.Green) });
achievementListBox.ItemsSource = new[] { gridA, gridB };
All of the three above examples completely ignore the ListBox.ItemTemplate, and instead, they display two items directly: two textboxes, two rectangles, two larger rectangles (in a Grid).
Getting back to your case: It means that with your original setup, the ListBox would try to display the items directly too - as your custom Page is a UIElement. And indeed it did that! But your pages were ... empty. In the overloaded constructor you omitted the InitializeComponent(); that constructs the View by reading the XAML code. Here is a corrected example that displays a "Hello" three times: Once just because it lies on the page, and next two times because the ListBox rows are set to the same page.
Please excuse me for renaming the classes, I simply started a new project instead of pasting your code.
Please note that I had to add to the XAML some other controls, because the Pages used as the data items would be display as empty, because they would have no items set
public partial class MainPage : PhoneApplicationPage
{
public string Description { get; set; }
public string Type { get; set; }
public MainPage()
{
InitializeComponent();
loadListbox();
}
public MainPage(string achievementGet, string d1)
{
InitializeComponent();
someText.Text = d1;
}
public void loadListbox()
{
achievementListBox.ItemsSource = new[] { new MainPage(null, "ddd"), new MainPage(null, "ccc") };
}
}
<StackPanel>
<TextBlock>
<Run Text="Hello" />
<Run Text=" " />
<Run x:Name="someText" />
</TextBlock>
<ListBox Name="achievementListBox" Margin="0,0,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Width="150" Foreground="White"
FontSize="22" Text="This DataTemplate is IGNORED because the Item is UIElement"
Height="40"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
I tried to shape the code in a similar manner to yours, just removed some lines not relevant to the problem. I hope this explains you everything now :)
Oh my. Why do create a list of achievementPages? On your achievementPage you will want to have a ListBox with items of type, like, AchievementItem, CompletedAchievement, NotCompletedAchievement, etc.
Currently, nothing shows up because your code probably throws StackoverflowException (not joking here:)). Look: your achievementPage constructor calls loadListBox that creates two achievementPages and adds them to the list. But creating two achievementPages causes again their constructors to be called two times, which invokes loadListBox two times, and so on..
-- edit: ok, no stackoverflow, I've just noticed the second constructor. You should stick to naming the classes with capital letters you know :) Anyways, putting a Page as a data item of a ListBox on a Page is a bad idea.
What you wanted to get should look more like:
public partial class AchievementPage : PhoneApplicationPage
{
public string Description { get; set; }
public string Type { get; set; }
public AchievementPage()
{
InitializeComponent();
loadListbox();
}
public void loadListbox()
{
var theList = new List<Achievement>();
theList.Add(new Achievement{ AchvCount=3, AchvName="medals" });
theList.Add(new Achievement{ AchvCount=2, AchvName="badges" });
theList.Add(new Achievement{ AchvCount=6, AchvName="zonks" });
achievementListBox.ItemsSource = achievementList;
}
}
public class Achievement : DependencyObject
{
public int AchvCount {get; set;}
public string AchvName {get; set;}
}
<ListBox Name="achievementListBox" Margin="0,0,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="You've got:" />
<TextBlock Grid.Column="0" Text="{Binding AchvCount}" />
<TextBlock Grid.Column="0" Text="{Binding AchvName}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Resources