Animate labels for 20 seconds - xamarin

I have 3 labels binded like following
<StackLayout>
<Label Padding="1" Text="VRN" FontSize="25" TextColor="Black" VerticalOptions="Center" HorizontalTextAlignment="Center"/>
<Label Padding="1" Text="{Binding VRN}" TextColor="#ba000d" FontSize="100" FontAttributes="Bold" VerticalOptions="Center" HorizontalTextAlignment="Center"/>
<Label Padding="1" Text="Make" FontSize="25" TextColor="Black" VerticalOptions="Center" HorizontalTextAlignment="Center"/>
<Label Text="{Binding Make}" TextColor="#ba000d" FontSize="100" FontAttributes="Bold" VerticalOptions="Center" HorizontalTextAlignment="Center"/>
<Label Text="{Binding Model}" TextColor="#ba000d" FontSize="30" FontAttributes="Bold" VerticalOptions="Center" HorizontalTextAlignment="Center"/>
</StackLayout>
Binding is done in ItemModel like following
string _vrn = "";
public string VRN
{
protected set
{
if (_vrn != value)
{
_vrn = value;
OnPropertyChanged("VRN");
}
}
get { return _vrn ; }
}
string _make= "";
public string Make
{
protected set
{
if (_make!= value)
{
_make= value;
OnPropertyChanged("Make");
}
}
get { return _make; }
}
string _model = "";
public string Model
{
protected set
{
if (_model != value)
{
_model = value;
OnPropertyChanged("Model");
}
}
get { return _model ; }
}
hubConnection.On<string>("NewItem", (item) =>
{
MainThread.BeginInvokeOnMainThread(async () =>
{
Item newItem = JsonConvert.DeserializeObject<Item>(item);
VRN= newItem.VRN;
Make= newItem.Make;
Model= newItem.Model;
}
}
Above code will set the labels with value of last item inserted. Up to here done.
I want to animate(Blink) the 3 labels for 20 seconds whenever new item is added to Item ObservableCollection(I am using SignalR to insert data)
I already have a class which i am using it with CollectionView for another scenario.Is there any way i can use the following class to animate Labels or is there any other easy way?
public class BlinkTriggerAction : TriggerAction<VisualElement>
{
protected async override void Invoke(VisualElement sender)
{
var parentAnimation = new Animation();
var fadeOutAnimation = new Animation(d => sender.Opacity = d, 1, 0, Easing.Linear);
var fadeInAnimation = new Animation(d => sender.Opacity = d, 0, 1, Easing.Linear);
parentAnimation.Add(0, 0.5, fadeOutAnimation);
parentAnimation.Add(0.5, 1, fadeInAnimation);
parentAnimation.Add(0, 0.5, fadeOutAnimation);
parentAnimation.Add(0.5, 1, fadeInAnimation);
parentAnimation.Add(0, 0.5, fadeOutAnimation);
parentAnimation.Add(0.5, 1, fadeInAnimation);
parentAnimation.Add(0, 0.5, fadeOutAnimation);
parentAnimation.Add(0.5, 1, fadeInAnimation);
parentAnimation.Commit(sender, "BlinkingVisualElement", 16, 800, repeat: () => true);
await Task.Delay(20000);
parentAnimation.Add(0, 0.5, fadeOutAnimation);
parentAnimation.Add(0.5, 1, fadeInAnimation);
sender.AbortAnimation("BlinkingVisualElement");
}
}

If you are looking for reusability I would suggest using behaviours instead of Triggers, as it lets you add it under the styles which Triggers don't.
Your BlinkTriggerBehavior would be something like this:
Note: I am using VisualElement here, you can use anything in future e.g Button, Label, Entry etc.
public class BlinkTriggerBehavior: Behavior<VisualElement>
{
protected override void OnAttachedTo(VisualElement bindable)
{
base.OnAttachedTo(bindable);
bindable.PropertyChanged += Animate; // binding property changed event
}
private async void Animate(object obj, PropertyChangedEventArgs e)
{
// your animation code gets fired everytime any of your Label
// property changes or any VisualElement which uses this behavior.
// If you wish you can use whatever property name you wish here or keep your
// animation code as is.
if (e.PropertyName == "Text")
{
//Your animation code
}
}
protected override void OnDetachingFrom(VisualElement bindable)
{
base.OnDetachingFrom(bindable);
bindable.PropertyChanged -= Animate;
}
}
In your MainPage.xaml, contentpage resource you can add the following:
<Style TargetType="Label">
<Style.Behaviors>
<views:BlinkTriggerBehavior />
</Style.Behaviors>
</Style>
... any other Target type Like you mentioned CollectionView ViewCell

If you want to make the animation for the label, you could download the source file from the link below:
https://learn.microsoft.com/en-us/samples/xamarin/xamarin-forms-samples/userinterface-animation-basic/
Change the xaml in RotateAnimationPage like below.
<StackLayout Margin="0,20,0,0">
<!--<Image x:Name="image" Source="monkey.png" VerticalOptions="CenterAndExpand" />-->
<Label
x:Name="image"
BackgroundColor="Accent"
HorizontalOptions="Center"
Text="Hello"
VerticalOptions="CenterAndExpand" />
<Button
x:Name="startButton"
Clicked="OnStartAnimationButtonClicked"
Text="Start Animation"
VerticalOptions="EndAndExpand" />
<Button
x:Name="cancelButton"
Clicked="OnCancelAnimationButtonClicked"
IsEnabled="false"
Text="Cancel Animation"
VerticalOptions="EndAndExpand" />
</StackLayout>
Screenshot:
And if you want to animate labels for 20 seconds, you could set the RotateTo length which to animate the transition to 20000. The screenshot set with 2000.
await image.RotateTo(360, 20000);

Related

How to go to a new page with selectedItem in Picker In Xamarin

I have a Picker in Xamarin and I want to go to the page which is selected in Picker. So here is my view in Picker:
<ContentPage.Content>
<ScrollView>
<StackLayout Margin="20">
<Label Text="List of tables" FontAttributes="Bold" HorizontalOptions="Center"/>
<Picker Title="Select table" ItemsSource="{Binding TablesFromViewModelCollector}" ItemDisplayBinding="{Binding Name}" SelectedItem="{Binding SelectedTable}" />
<Label Text="{Binding SelectedTable.Name}" HorizontalOptions="Center" Style="{DynamicResource TitleStyle}"/>
<Button Text="Select Table" HorizontalOptions="Center" Style="{DynamicResource TitleStyle}" Clicked="Selected_Button"/>
</StackLayout>
</ScrollView>
</ContentPage.Content>
Here is it's contoller:
public TablePage()
{
InitializeComponent();
BindingContext = new TableViewModel();
}
async void Selected_Button(object sender, System.EventArgs e)
{
await Navigation.PushAsync(new TableDetails());
}
And here is TableViewModel where I can select the table from Picker:
class TableViewModel : INotifyPropertyChanged
{
public IList<Table> TablesFromViewModelCollector { get { return TableData.Tables; } }
Table selectedTable;
public Table SelectedTable
{
get { return selectedTable; }
set
{
if(SelectedTable != value)
{
selectedTable = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
So I added button under the Picker as you see. And what I am trying to achieve, when the Picker is selected and button is clicked, goes to the page of TableDetails. In this page, I want to show the data of selectedTable. Basically, here is my view for TableDetails:
<ContentPage.Content>
<ScrollView>
<StackLayout Margin="20">
<Label Text="{Binding SelectedTable.Name}" HorizontalOptions="Center" Style="{DynamicResource TitleStyle}"/>
</StackLayout>
</ScrollView>
</ContentPage.Content>
And here is my controller:
public partial class TableDetails : ContentPage
{
public TableDetails()
{
InitializeComponent();
BindingContext = new TableViewModel();
}
}
But I dont get the value of the selectedTable.
just pass the value on the constructor
await Navigation.PushAsync(new TableDetails(VM.SelectedTable));
then modify the page constructor
public TableDetails(Table selected)
{
InitializeComponent();
BindingContext = new TableViewModel();
}

Xamarin CarouselView throws OutOfRangeException when ItemsSource changes and Positon is not 0

I have a Xamarin Forms App with a Carousel View and my plan is to filter the result by the Priorities shown in a Bottom Tap Bar.
When the View is on the first card, everything works fine, but if you hit one of the filters from a different card, then the first one, a System.ArgumentOutOfRangeException exception got fired.
I tried already a lot of things to set the position of the Carousel View to 0, but in vain.
using the CarouselView_PositionChanged event => not working
using the TabHost_SelectedTabIndexChanged event => not working
setting the property of the CarouselView by the selectedIndex changed from the Tab View from the ViewModel => not working
turn it into a CollectionView and everything is working => just looks ugly 🤮
Here the page
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
xmlns:viewModels="clr-namespace:AmsXamarin.ViewModels"
xmlns:fontAwesome="clr-namespace:FontAwesome"
xmlns:cells="clr-namespace:AmsXamarin.Cells"
xmlns:tabs="http://sharpnado.com"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
x:Class="AmsXamarin.Views.SnagListX.SnagListPage"
x:Name="mySnagListPage">
<ContentPage.BindingContext>
<viewModels:SnagListViewModel />
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<xct:TabSelectionChangedEventArgs x:Key="TabSelectionChangedEventArgs" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.ToolbarItems>
<ToolbarItem Command="{Binding ShowMyJobsCommand}"
Order="Primary"
Priority="0"
Text="{Binding MyJobsTitle,Mode=TwoWay}">
</ToolbarItem>
<ToolbarItem Command="{Binding AddFaultCommand}"
Order="Primary"
Priority="0">
<ToolbarItem.IconImageSource>
<FontImageSource FontFamily="FAS"
Glyph="{x:Static fontAwesome:FontAwesomeIcons.Plus}"
Color="{AppThemeBinding Dark={StaticResource Third},Light={StaticResource Primary}}"
Size="Large" />
</ToolbarItem.IconImageSource>
</ToolbarItem>
<ToolbarItem Command="{Binding AcceptCommand}"
Order="Primary"
Priority="0">
<ToolbarItem.IconImageSource>
<FontImageSource FontFamily="FAS"
Glyph="{x:Static fontAwesome:FontAwesomeIcons.UserCheck}"
Color="{AppThemeBinding Dark={StaticResource Third},Light={StaticResource Primary}}"
Size="Large" />
</ToolbarItem.IconImageSource>
</ToolbarItem>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout>
<Label Style="{StaticResource LabelLarge}"
Text="Snag List"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
<StackLayout>
<IndicatorView x:Name="snagListIndicator"
Margin="0,10,0,0"
IndicatorColor="LightGray"
SelectedIndicatorColor="LightGray"
IndicatorSize="6" />
<RefreshView Command="{Binding RefreshCommand}"
IsRefreshing="{Binding IsBusy, Mode=OneWay}"
Style="{StaticResource BaseRefreshView}">
<CarouselView x:Name="snagListCV"
Margin="10"
IndicatorView="snagListIndicator"
ItemsLayout="HorizontalList"
ItemsSource="{Binding SnagListPriorities.SnagListApps}"
CurrentItem="{Binding SnagListItem}"
Position="{Binding PositionIdx,Mode=OneWay}"
PositionChanged="CarouselView_PositionChanged"
Loop="False"
IsEnabled="{Binding IsNotBusy}">
<CarouselView.EmptyView>
<StackLayout Padding="12">
<Label Style="{StaticResource LabelMedium}"
HorizontalOptions="Center"
Text="{Binding EmptyMessage,Mode=TwoWay}" />
</StackLayout>
</CarouselView.EmptyView>
<CarouselView.ItemTemplate>
<DataTemplate>
<cells:SnagListCardX />
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
</RefreshView>
</StackLayout>
<tabs:TabHostView x:Name="TabHost"
Margin="13,0,13,10"
BackgroundColor="{AppThemeBinding Dark={StaticResource Third},Light={StaticResource Fourth}}"
CornerRadius="30"
IsSegmented="True"
Orientation="Horizontal"
TabType="Fixed"
SelectedIndex="{Binding SelectedIndex,Mode=TwoWay}"
Shades="{StaticResource LightBottomShadow}">
<tabs:TabHostView.Behaviors>
<xct:EventToCommandBehavior EventName="SelectedTabIndexChanged"
Command="{Binding SelectedCommand}"
EventArgsConverter="{StaticResource TabSelectionChangedEventArgs}" />
</tabs:TabHostView.Behaviors>
<tabs:TabHostView.Tabs>
<tabs:BottomTabItem Style="{StaticResource BottomTabsMedium}"
Label="All">
<tabs:BottomTabItem.Badge>
<tabs:BadgeView Style="{StaticResource BadgeViewBase}"
BackgroundColor="{StaticResource All}"
TextColor="White"
Text="{Binding SnagListPriorities.All,Mode=TwoWay}" />
</tabs:BottomTabItem.Badge>
</tabs:BottomTabItem>
<tabs:BottomTabItem Style="{StaticResource BottomTabsMedium}"
Label="High"
StyleClass="">
<tabs:BottomTabItem.Badge>
<tabs:BadgeView Style="{StaticResource BadgeViewBase}"
BackgroundColor="{StaticResource High}"
TextColor="White"
Text="{Binding SnagListPriorities.High,Mode=OneWay}" />
</tabs:BottomTabItem.Badge>
</tabs:BottomTabItem>
<tabs:BottomTabItem Style="{StaticResource BottomTabsMedium}"
Label="Medium">
<tabs:BottomTabItem.Badge>
<tabs:BadgeView Style="{StaticResource BadgeViewBase}"
BackgroundColor="{StaticResource Medium}"
TextColor="Black"
Text="{Binding SnagListPriorities.Medium,Mode=TwoWay}" />
</tabs:BottomTabItem.Badge>
</tabs:BottomTabItem>
<tabs:BottomTabItem Style="{StaticResource BottomTabsMedium}"
Label="Low">
<tabs:BottomTabItem.Badge>
<tabs:BadgeView Style="{StaticResource BadgeViewBase}"
BackgroundColor="{StaticResource Low}"
TextColor="Black"
Text="{Binding SnagListPriorities.Low,Mode=TwoWay}" />
</tabs:BottomTabItem.Badge>
</tabs:BottomTabItem>
</tabs:TabHostView.Tabs>
</tabs:TabHostView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
And the view model
using AmsXamarin.Helpers;
using AmsXamarin.Models;
using AmsXamarin.Services;
using MvvmHelpers;
using MvvmHelpers.Commands;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using Command = MvvmHelpers.Commands.Command;
namespace AmsXamarin.ViewModels
{
public class SnagListViewModel : BaseViewModel
{
readonly ISnagListService snagListService;
List<SnagListAppX> SnagListAppXs;
SnagListAppX snagListItem;
public SnagListAppX SnagListItem
{
get { return snagListItem; }
set
{
snagListItem = value;
OnPropertyChanged();
}
}
SnagListPriorities snagListPriorities;
public SnagListPriorities SnagListPriorities
{
get { return snagListPriorities; }
set
{
snagListPriorities = value;
OnPropertyChanged();
}
}
public AsyncCommand AddFaultCommand { get; }
public AsyncCommand AcceptCommand { get; }
public Command RefreshCommand { get; }
public Command SelectedCommand { get; }
public Command ShowMyJobsCommand { get; }
string emptyMessage;
public string EmptyMessage
{
get { return emptyMessage; }
set
{
emptyMessage = value;
OnPropertyChanged();
}
}
int positionIdx;
public int PositionIdx
{
get { return positionIdx; }
set
{
positionIdx = value;
OnPropertyChanged();
}
}
int selectedIndex;
public int SelectedIndex
{
get => selectedIndex;
set => SetProperty(ref selectedIndex, value);
}
string myJobsTitle = "My Jobs";
int staffId;
public string MyJobsTitle
{
get { return myJobsTitle; }
set
{
myJobsTitle = value;
OnPropertyChanged();
}
}
bool myJobsEnabled = true;
bool updateProperty = false;
public bool AcceptToolBar { get; set; }
public SnagListViewModel()
{
SnagListItem = new SnagListAppX();
SnagListPriorities = new SnagListPriorities()
{
SnagListApps = new ObservableRangeCollection<SnagListAppX>()
};
SnagListAppXs = new List<SnagListAppX>();
AddFaultCommand = new AsyncCommand(ShowAddSnagListItem);
AcceptCommand = new AsyncCommand(ShowModalAccept);
SelectedCommand = new Command(Selected);
RefreshCommand = new Command(Refresh);
staffId = 0;
ShowMyJobsCommand = new Command(ShowModalMyJobs);
snagListService = DependencyService.Get<ISnagListService>();
Device.BeginInvokeOnMainThread(async () =>
{
await GetSnagListItems();
});
}
async Task GetSnagListItems()
{
try
{
IsBusy = true;
EmptyMessage = "Loading...";
SnagListPriorities slp = await snagListService.GetSnagLists(false, true);
SnagListPriorities.SnagListApps.Clear();
SnagListPriorities = slp;
SnagListAppXs.Clear();
SnagListAppXs.AddRange(slp.SnagListApps);
EmptyMessage = SnagListAppXs.Count == 0 ? "Good Job! \r\n\r\nCurrently nothing to fix" : "";
IsBusy = false;
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Error", ex.Message, "Ok");
}
}
void Selected()
{
//PositionIdx = 0;
//OnPropertyChanged(nameof(PositionIdx));
//FilterSnagList();
Refresh();
}
void FilterSnagList()
{
var allJobs = SnagListAppXs;
if (staffId != 0) allJobs = allJobs.Where(x => x.StaffId == staffId).ToList();
if (updateProperty) OnPropertyChanged(nameof(SnagListPriorities));
if (selectedIndex > 0) allJobs = allJobs.Where(sli => sli.Priority == selectedIndex).ToList();
SnagListPriorities.SnagListApps.Clear();
//SnagListPriorities.SnagListApps = new ObservableRangeCollection<SnagListAppX>(allJobs);
SnagListPriorities.SnagListApps.AddRange(allJobs);
//OnPropertyChanged(nameof(SnagListPriorities.SnagListApps));
updateProperty = false;
}
void ShowModalMyJobs()
{
staffId = myJobsEnabled ? int.Parse(Settings.StaffIdSetting) : 0;
MyJobsTitle = myJobsEnabled ? "All Jobs" : "My Jobs";
AcceptToolBar = !myJobsEnabled;
myJobsEnabled = !myJobsEnabled;
SelectedIndex = 0;
updateProperty = true;
}
void Refresh()
{
IsBusy = true;
var allJobs = SnagListAppXs.Where(s => s.StaffId == 115);
SnagListPriorities.SnagListApps.Clear();
SnagListPriorities.SnagListApps.AddRange(allJobs);
IsBusy = false;
}
}
}
looks a bit messy, but wanted to show, that I tried already a lot of things.
Last try was a simple RefreshView.
It works as long as the itemsSource is not changing. Once it changes and the first card is not shown, it crashes.
Any ideas? Many thanks

Xamarin: Binding to ImageSource property is not working

I am trying to display images dynamically through binding:
I have ObservableCollection of TabItem to which I load a png into the Icon Property:
TabItems.Add(new TabItem()
{
Title = AppResources.Home,
IsSelected = true,
Icon = ImageSource.FromResource("Home.png")
});
Where:
public class TabItem : BindableObject
{
private bool _isSelected;
public ImageSource Icon { get; set; }
public string Title { get; set; }
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
if (_isSelected != value)
{
_isSelected = value;
OnPropertyChanged();
}
}
}
}
And I have a DataTemplate of TabItem in a CollectionView that displays these items and uses the Icon Property to bind to Image:
<DataTemplate x:DataType="local:TabItem" x:Key="TabItem_DataTemplate">
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
WidthRequest="{Binding Title, Converter={StaticResource ProportionateConverter}}"
>
<Image Source="{Binding Icon}" InputTransparent="True" HeightRequest="50" />
<Label HorizontalOptions="Center" VerticalOptions="End" FontSize="Small"
Text="{Binding Title}"
InputTransparent="True"
TextColor="{Binding IsSelected, Converter={StaticResource IsSelectedToTextColorConverter}}"/>
</StackLayout>
</DataTemplate>
I get an Error in runtime:
ImageLoaderSourceHandler: Image data was invalid: Xamarin.Forms.StreamImageSource
The images are in the Resources folder and are marked as Embedded resource
Updated solution:
(Thanks Jason)
The solution was using my markup extension when initializing the Icon property (I'll be glad to hear of a more elegant way)
var homeExt = new ImageResourceExtension()
{
Source = "TimeManager.Resources.Icons.Home.png"
};
TabItems.Add(new TabItem()
{
Title = AppResources.Home,
IsSelected = true,
Icon = (ImageSource)(homeExt.ProvideValue(null))
});

How to check the check box dynamically in listbox in windows phone 7?

I have the listbox with four checkbox items. If I give the input as 2 then first two check boxes should select,this should happen programatically.
Please help me out..
Thanks,
.xaml page
<StackPanel Background="White" Margin="5,0,5,0">
<ListBox x:Name="listBox2" Foreground="Black" Height="300" SelectionMode="Multiple" SelectionChanged="listBox2_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Name="stackpanel1" Orientation="Horizontal" >
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding Name}" Foreground="Black" Height="68" Margin="0,-15,0,-10" BorderThickness="0" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked">
</CheckBox>
</StackPanel>
<Rectangle Fill="Gray" Height=".5" HorizontalAlignment="Stretch" Width="440" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
.cs code:
List<Items> source = new List<Items>();
source.Add(new Items() { Name = "Item 1" });
source.Add(new Items() { Name = "Item 2" });
source.Add(new Items() { Name = "Item 3" });
source.Add(new Items() { Name = "Item 4" });
source.Add(new Items() { Name = "Item 5" });
source.Add(new Items() { Name = "Item 6" });
`` listBox2.ItemsSource = source;
Items class
public class Items
{
public string Name
{
get;
set;
}
}
Here's a really crude way of doing what I think you've described. It doesn't use much of your code directly but should get you moving in the right direction.
XAML
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<toolkit:ListPicker SelectionChanged="ListPicker_OnSelectionChanged">
<toolkit:ListPickerItem>0</toolkit:ListPickerItem>
<toolkit:ListPickerItem>1</toolkit:ListPickerItem>
<toolkit:ListPickerItem>2</toolkit:ListPickerItem>
<toolkit:ListPickerItem>3</toolkit:ListPickerItem>
<toolkit:ListPickerItem>4</toolkit:ListPickerItem>
</toolkit:ListPicker>
<ItemsControl x:Name="TheListBox" Grid.Row="1" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Note that I'm using the Windows Phone Toolkit (get it from http://phone.codeplex.com/ or nuget)
C#
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Controls;
using Microsoft.Phone.Controls;
public partial class MainPage : PhoneApplicationPage
{
ObservableCollection<Items> source = new ObservableCollection<Items>();
public MainPage()
{
InitializeComponent();
source.Add(new Items { Id = 1 });
source.Add(new Items { Id = 2 });
source.Add(new Items { Id = 3 });
source.Add(new Items { Id = 4 });
TheListBox.ItemsSource = source;
}
private void ListPicker_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (source.Count > 0 && e.AddedItems.Count > 0)
{
for (int i = 0; i < source.Count; i++)
{
this.source[i].IsChecked = int.Parse(((ContentControl)(e.AddedItems[0])).Content.ToString()) >= this.source[i].Id;
}
}
}
}
public class Items : INotifyPropertyChanged
{
private bool isChecked;
public int Id { get; set; }
public string Name
{
get
{
return "Item " + this.Id;
}
}
public bool IsChecked
{
get
{
return this.isChecked;
}
set
{
this.isChecked = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Removing Item from observable collection causes textbox to focus

I have a textbox that is on the same screen as a listbox. When I remove an item from the listbox, which is bound to an observable collection, the textbox gains focus. Is there a way to stop this from happening?
I am using MVVM but am open to putting some code in the code behind if it fixes this problem.
EDIT:
CodeBehind View:
namespace Offload.WinPhone.Views
{
using System.Windows.Controls;
using Microsoft.Phone.Controls;
public partial class MainPageView : PhoneApplicationPage
{
public MainPageView()
{
InitializeComponent();
}
private void QuickNoteBodyEditor_TextChanged(object sender, TextChangedEventArgs e)
{
var senderAsTextbox = (TextBox)sender;
if (senderAsTextbox.Text.Length == 0)
this.Focus();
}
}
}
View:
<Grid x:Name="LayoutRoot" Background="Transparent" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="OFFLOAD" FontFamily="Segoe WP Bold" Grid.Row="0"/>
<ListBox x:Name="QuickNotes" Grid.Row="1" TabIndex="1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,5,0,5" Orientation="Vertical">
<TextBlock Style="{StaticResource PhoneTextExtraLargeStyle}" TextWrapping="Wrap" Text="{Binding Body}"/>
<TextBlock Style="{StaticResource PhoneTextAccentStyle}" Text="{Binding Timestamp, StringFormat='{}{0:dd MMM HH:mm}'}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox x:Name="QuickNoteBodyEditor" AcceptsReturn="True" Grid.Row="2" InputScope="Chat" TextChanged="QuickNoteBodyEditor_TextChanged" IsTabStop="True" />
</Grid>
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBar.Buttons>
<cal:AppBarButton IconUri="/AppFramework/Resources/Icons/Add.png" Text="Add" Message="AddQuickNote" />
<cal:AppBarButton IconUri="/AppFramework/Resources/Icons/Delete.png" Text="Delete" Message="DeleteSelectedQuickNote" />
</shell:ApplicationBar.Buttons>
<shell:ApplicationBar.MenuItems>
<cal:AppBarMenuItem Text="About" Message="NavigateToAddAccountView"/>
<cal:AppBarMenuItem Text="Review" Message="NavigateToAddAccountView"/>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
ViewModel:
public class MainPageViewModel : PropertyChangedBase
{
private ObservableCollection<QuickNote> quickNotes;
private string quickNoteBodyEditor;
private QuickNote selectedQuickNote;
// Databound Properties
public string QuickNoteBodyEditor
{
get { return quickNoteBodyEditor; }
set { quickNoteBodyEditor = value; NotifyOfPropertyChange(() => QuickNoteBodyEditor); NotifyOfPropertyChange(() => CanAddQuickNote);}
}
public QuickNote SelectedQuickNote
{
get { return selectedQuickNote; }
set { selectedQuickNote = value; NotifyOfPropertyChange(() => SelectedQuickNote); NotifyOfPropertyChange(() => CanDeleteSelectedQuickNote); }
}
public ObservableCollection<QuickNote> QuickNotes
{
get { return quickNotes; }
set { quickNotes = value; NotifyOfPropertyChange(() => QuickNotes); }
}
// Guard Clauses
public bool CanAddQuickNote
{
get { return !string.IsNullOrWhiteSpace(quickNoteBodyEditor); }
}
public bool CanDeleteSelectedQuickNote
{
get{ return selectedQuickNote == null ? false : true; }
}
// Constructors
public MainPageViewModel()
{
GetQuickNotesFromIsolatedStorage();
WatchWhatsGotFocus.StartWatching();
}
// Public Methods
public void AddQuickNote()
{
if (CanAddQuickNote)
{
quickNotes.Add(new QuickNote(quickNoteBodyEditor, DateTime.Now));
AddQuickNotesToIsolatedStorage();
quickNoteBodyEditor = string.Empty;
NotifyOfPropertyChange(() => QuickNoteBodyEditor);
NotifyOfPropertyChange(() => QuickNotes);
}
}
public void DeleteSelectedQuickNote()
{
if (CanDeleteSelectedQuickNote)
{
quickNotes.Remove(selectedQuickNote);
AddQuickNotesToIsolatedStorage();
selectedQuickNote = null;
NotifyOfPropertyChange(() => SelectedQuickNote);
NotifyOfPropertyChange(() => QuickNotes);
}
}
private void GetQuickNotesFromIsolatedStorage()
{
quickNotes = IsolatedStorage.Get<ObservableCollection<QuickNote>>("QuickNoteList");
}
private void AddQuickNotesToIsolatedStorage()
{
IsolatedStorage.Add("QuickNoteList", quickNotes);
}
}
I had the same issue, a possible solution is to set the TextBox to disabled before modifying your collection and enable it afterwards again:
TextBox.IsEnabled = false;
ObservableCollection.Remove(object);
TextBox.IsEnabled = true;
I wouldn't call it a clean approach, but it worked for me :).

Resources