Image Source not binding properly in DataTemplate in Windows 8 Mobile - image

I have this list of objects that needs to be separated into two columns. Then I use this template for every object of the list:
<DataTemplate x:Key="UnderlyingRealTimeExchangeRatesLongListSelector">
<Grid Background="{Binding PriceChanged, Converter={StaticResource PriceChangedToBackgroundConverter}}"
Margin="0,2.5,5,2.5" Tap="RealTimeElement_Tapped">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="txtUnderlyingName" Text="{Binding Name}" Foreground="White"
Style="{StaticResource NormalFontStyle}" HorizontalAlignment="Left"
Margin="5" FontSize="25" Padding="10" TextWrapping="Wrap"/>
<Image Grid.Column="1"
Source="{Binding Path= Image, Converter={StaticResource ImageToFlagConverter}}"
Height="30"></Image>
<TextBlock x:Name="txtUnderlyingPrice" Text="{Binding Price, StringFormat='0:N2'}"
Grid.Row="1" Grid.ColumnSpan="2" Foreground="#FFD300"
Style="{StaticResource LightFontStyle}" FontSize="40"
HorizontalAlignment="Right" VerticalAlignment="Center" Padding="10"/>
</Grid>
</DataTemplate>
<phone:LongListSelector x:Name="llsRealTimeCurrencies1" Grid.Column="0" Margin="0,15,0,32"
ItemTemplate="{StaticResource UnderlyingRealTimeExchangeRatesLongListSelector}"
Visibility="Collapsed"/>
<phone:LongListSelector x:Name="llsRealTimeCurrencies2" Grid.Column="1" Margin="0,15,0,32"
ItemTemplate="{StaticResource UnderlyingRealTimeExchangeRatesLongListSelector}"
Visibility="Collapsed"/>
Here is the converter:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//Returns The flag needed
if (value != null)
{
string image = value.ToString().ToLower();
string flag = "Assets\\flags\\" + image + "_flag.png";
return Path.GetFullPath(flag);
}
else
{
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
Here is the code behind the previous part:
private void SetAndShowCorrectRealTimeList<T>(LongListSelector[] list, IList<T> collection)
{
//and make sure the grid holding the lists is visible
grdRealtimeLists.Visibility = System.Windows.Visibility.Visible;
List<LongListSelector> lists =
new List<LongListSelector>()
{
llsRealTimeCommodities1,
llsRealTimeCommodities2,
llsRealTimeCurrencies1,
llsRealTimeCurrencies2,
llsRealTimeIndecies1,
llsRealTimeIndecies2,
llsRealTimeWatchList1,
llsRealTimeWatchList2
};
foreach (var item in lists.Except(list))
{
item.Visibility = System.Windows.Visibility.Collapsed;
}
for (int i = 0; i < list.Length; i++)
{
list[i].Visibility = Visibility.Visible;
List<T> result = collection.Where((item, index) => index % 2 == i).ToList();
list[i].ItemsSource = result;
}
}
The funny part is that it displays the images on the left column, but doesn't on the right. As a matter of fact, it doesn't even gets in the converter for the second column. Thank you!!!

Bind image source like this.
<Image Grid.Column="1" Height="30">
<Image.Source>
<BitmapImage UriSource="{Binding Path=Image, Converter={StaticResource ImageToFlagConverter}}" />
</Image.Source>
</Image>

Check to see if your code has all the right elements -
llsRealTimeCurrencies1, llsRealTimeCurrencies2. Those are the lists that we use to bind the template to:
private async void Currencies_Tapped(object sender, System.Windows.Input.GestureEventArgs e)
{
mainViewModel.RealTimeViewModel.IsWatchListSettingsVisible = false;
ApplyForegroundFont(sender, realtimeTabs);
FrameworkElement parent = (FrameworkElement)((TextBlock)sender).Parent;
ApplyBackgroundColor(parent, realtimeTabsGrid);
if (!mainViewModel.RealTimeViewModel.CurrencyExchangeRates.Any())
{
if (mainViewModel.RealTimeViewModel.Realtime != null &&
mainViewModel.RealTimeViewModel.Realtime.Underlyings != null &&
mainViewModel.RealTimeViewModel.Realtime.Underlyings.Any())
{
mainViewModel.RealTimeViewModel.InitializeCurrencies();
}
else
{
mainViewModel.RealTimeViewModel.LoadProductsOrUnderlyingsFromDb<Currency>(mainViewModel.RealTimeViewModel.CurrencyExchangeRates);
}
}
if (!App.Settings.Contains(currenciesWereSaved))
{
App.Settings.Add(currenciesWereSaved, "");
App.Settings.Save();
await Task.Factory.StartNew(() =>
mainViewModel.RealTimeViewModel.SaveUnderlyingsAndProducts<Currency>(mainViewModel.RealTimeViewModel.CurrencyExchangeRates));
}
SetAndShowCorrectRealTimeList(new LongListSelector[] { llsRealTimeCurrencies1, llsRealTimeCurrencies2 }, mainViewModel.RealTimeViewModel.CurrencyExchangeRates);
GoogleAnalytics.EasyTracker.GetTracker().SendEvent("Real time", "click", "Currencies", 0);
MyBindableBase.LightstreamerClientInstance.Subscribe(mainViewModel.RealTimeViewModel.CurrencyExchangeRates.Cast<IRealtimeProperty>().ToList());
}

Related

Binding list view item template image to ObservableCollection not working

I know that there were many questions about this and I searched a lot and tried everything which I found about this but I can't get it working.
Simply put, for some reason I'm unable to show an image inside a ListView item template.
So I have this ItemViewModel class:
public class ItemViewModel : BaseViewModel, IItemViewModel
{
public ItemViewModel()
{
if (dalInterface == null)
{
dalInterface = ApplicationContext.Container.Resolve<IDalInterface>();
}
if (eventCenter == null)
{
eventCenter = ApplicationContext.Container.Resolve<IEventCenter>();
}
SaveCommand = new Command(SaveChanges, true);
DeleteCommand = new Command(RemoveItem, true);
AddNewItemCommand = new Command(AddNewItem, true);
}
public ICommand SaveCommand { get; set; }
public ICommand DeleteCommand { get; set; }
public ICommand AddNewItemCommand { get; set; }
private Item data;
public int ID { get; private set; }
private string title;
public string Title
{
get { return title; }
set
{
title = value;
NotifyPropertyChanged("Title");
}
}
private string author;
public string Author
{
get { return author; }
set
{
author = value;
NotifyPropertyChanged("Author");
}
}
private string shortDescription;
public string ShortDescription
{
get { return shortDescription; }
set
{
shortDescription = value;
NotifyPropertyChanged("ShortDescription");
}
}
private string buyPrice;
public string BuyPrice
{
get { return buyPrice; }
set
{
buyPrice = value;
NotifyPropertyChanged("BuyPrice");
}
}
private string borrowPrice;
public string BorrowPrice
{
get { return borrowPrice; }
set
{
borrowPrice = value;
NotifyPropertyChanged("BorrowPrice");
}
}
private int quantity;
public int Quantity
{
get { return quantity; }
set
{
quantity = value;
NotifyPropertyChanged("Quantity");
}
}
private string detailedDescription;
public string DetailedDescription
{
get { return detailedDescription; }
set
{
detailedDescription = value;
NotifyPropertyChanged("DetailedDescription");
}
}
private string imagePath;
public string ImagePath
{
get { return imagePath; }
set
{
imagePath = value;
NotifyPropertyChanged("ImagePath");
}
}
private Image image;
public Image Image
{
get { return image; }
set
{
image = value;
NotifyPropertyChanged("Image");
}
}
public void SetData(Item item)
{
data = item;
ID = data.ID;
Author = data.Author;
Title = data.Title;
Quantity = data.Quantity;
ShortDescription = data.ShortDescription;
DetailedDescription = data.DetailedDescription;
BuyPrice = data.BuyPrice;
BorrowPrice = data.BorrowPrice;
Image = GetImage(data.ImagePath);
}
private Image GetImage(string imagePath)
{
var imageUri = new Uri(imagePath, UriKind.RelativeOrAbsolute);
var bitmapImage = new BitmapImage(imageUri);
var image = new Image
{
Source = bitmapImage
};
return Image;
}
private void SaveChanges()
{
UpdateChanges(data);
dalInterface.UpdateItem(data);
}
private void RemoveItem()
{
dalInterface.RemoveItem(data);
}
private void AddNewItem()
{
var newItem = new Item();
if (AllDataCorrect())
{
UpdateChanges(newItem);
dalInterface.AddNewItem(newItem);
eventCenter.Publish(new AddItemEventArgs { OperationSuccess = true });
}
else
{
eventCenter.Publish(new AddItemEventArgs { OperationSuccess = false });
}
}
private void UpdateChanges(Item itemToUpdate)
{
itemToUpdate.Author = Author;
itemToUpdate.Title = Title;
itemToUpdate.BorrowPrice = BorrowPrice;
itemToUpdate.BuyPrice = BuyPrice;
itemToUpdate.DetailedDescription = DetailedDescription;
itemToUpdate.ShortDescription = ShortDescription;
itemToUpdate.Quantity = Quantity;
itemToUpdate.ImagePath = ImagePath;
}
private bool AllDataCorrect()
{
float val = -1.0F;
float.TryParse(BuyPrice, out val);
if (val <= 0.0F)
{
return false;
}
float.TryParse(BorrowPrice, out val);
if (val <= 0.0F)
{
return false;
}
if ((ShortDescription == string.Empty) ||
(DetailedDescription == string.Empty) ||
(Author == string.Empty) ||
(Title == string.Empty)
)
{
return false;
}
if (Quantity <= 0)
{
return false;
}
return true;
}
public void Clear()
{
Author = string.Empty;
Title = string.Empty;
ImagePath = string.Empty;
ShortDescription = string.Empty;
DetailedDescription = string.Empty;
BuyPrice = string.Empty;
BorrowPrice = string.Empty;
Quantity = 0;
}
}
And for this class I have the following user control:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="4"
Style="{StaticResource ImageBorderStyle}">
<Image Source="{Binding Image, Mode=TwoWay}"
MinWidth="80"
MinHeight="80"
Stretch="UniformToFill"/>
</Border>
<Border Grid.Row="0"
Grid.Column="1"
Style="{StaticResource BaseBorderStyle}">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Wiki"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="1"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="Auto"
Text="{Binding ShortDescription}"/>
</Border>
</Grid>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.Row="0"
Grid.Column="1"
Style="{StaticResource BaseBorderStyle}"
HorizontalAlignment="Left"
Width="100">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="About"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="1"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="Auto"
Text="{Binding DetailedDescription}"/>
</Border>
</Grid>
</Grid>
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.Row="0"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Title"
TextAlignment="Center"
HorizontalAlignment="Stretch"/>
</Border>
<Border Grid.Row="1"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding Title}"/>
</Border>
<Border Grid.Row="2"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Author"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="3"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding Author}"/>
</Border>
<Border Grid.Row="4"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Quantity"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="5"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding Quantity}"/>
</Border>
<Border Grid.Row="0"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Buy Price"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="1"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding BuyPrice}"/>
</Border>
<Border Grid.Row="2"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Borrow Price"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="3"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding BorrowPrice}"/>
</Border>
<Button Grid.Row="5"
Grid.Column="1"
Style="{StaticResource SaveButtonStyle}"/>
<Button Grid.Row="5"
Grid.Column="1"
Style="{StaticResource RemoveButtonStyle}"
HorizontalAlignment="Right"/>
</Grid>
</Grid>
Using this I want to show into a page, inside a list view, based on an observable collection, loaded from a database some items.
The page view model is the follow:
public class ManageItemsViewModel : BaseViewModel, IManageItemsViewModel
{
public ManageItemsViewModel()
{
if(dalInterface == null)
{
dalInterface = ApplicationContext.Container.Resolve<IDalInterface>();
}
if(eventCenter == null)
{
eventCenter = ApplicationContext.Container.Resolve<IEventCenter>();
}
Items = new ObservableCollection<ItemViewModel>();
}
public ObservableCollection<ItemViewModel> Items { get; set; }
public void Refresh()
{
var dalItems = dalInterface.GetAllItems();
foreach(Item item in dalItems)
{
var vm = Items.Where(v => v.ID.Equals(item.ID));
if(vm.Equals(null))
{
var newItemVm = (ItemViewModel)ApplicationContext.Container.Resolve<IItemViewModel>();
newItemVm.SetData(item);
Items.Add(newItemVm);
}
}
NotifyPropertyChanged("Items");
}
public void LoadData()
{
if(Items.Count == 0)
{
var dalItems = dalInterface.GetAllItems();
foreach(Item item in dalItems)
{
var newItemVm = (ItemViewModel)ApplicationContext.Container.Resolve<IItemViewModel>();
newItemVm.SetData(item);
Items.Add(newItemVm);
}
NotifyPropertyChanged("Items");
}
else
{
Refresh();
}
}
}
And the page view is the follow:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0"
Margin="2"
Style="{StaticResource AddButtonStyle}"
Click="GoToAddNewItem"/>
<Button Grid.Row="0"
Margin="2"
HorizontalAlignment="Right"
Style="{StaticResource CloseButtonStyle}"
Click="GoToItems"/>
<ListView Grid.Row="1"
ItemsSource="{Binding Items, Mode=TwoWay}"
Margin="5">
<ListView.ItemTemplate>
<DataTemplate>
<templates:EditableItem/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
That page should be like (at the moment I only have one item in DB for tests):
The problem which I facing now is that I cannot see the Image inside this item template, although all the other properties are correctly shown inside the item template.
I've tried to use the image path as I get it from DB and bind into item xaml the source path property to this, also I tried to have an ImageSource or BitmapImage property inside item VM to have the xaml image source bound to this property but without any success so far.
After reading many questions about and trying again and again without success I'm here...
Any hints about what I'm doing wrong?
Many thanks!
(P.S. The app which I'm working to is an UWP app, maybe it matters...)
You can't use an Image control as the value of the Source property of another Image control.
Use an ImageSource instead:
private ImageSource image;
public ImageSource Image
{
get { return image; }
set
{
image = value;
NotifyPropertyChanged("Image");
}
}
And change the GetImage method to:
private ImageSource GetImage(string imagePath)
{
return new BitmapImage(new Uri(imagePath, UriKind.RelativeOrAbsolute));
}
That said, you could simply drop the Image property and bind directly to ImagePath. Built-in type conversion will automatically convert from a path string to ImageSource.
<Image Source="{Binding ImagePath}"/>
Note also that Mode=TwoWay makes no sense for this Binding.
As I searched again and again about this problem and tried lots of suggested solutions I finally found a solution which is suitable for me and solves this problem.
I implemented a little demo to show how exactly is working. The UWP app is not constraint to interact with the pictures folder only. The images can be loaded from an arbitrary folder on disk.
Hope that this will help others to.
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
PickFileCommand = new ActionCommand(PickFile, true);
}
public event PropertyChangedEventHandler PropertyChanged;
public ICommand PickFileCommand { get; set; }
private BitmapImage imageSrc;
public BitmapImage ImageSrc
{
get { return imageSrc; }
set
{
imageSrc = value;
NotifyPropertyChanged("ImgSource");
}
}
private async void PickFile()
{
var filePicker = new FileOpenPicker
{
SuggestedStartLocation = PickerLocationId.PicturesLibrary
};
filePicker.FileTypeFilter.Add(".jpg");
filePicker.FileTypeFilter.Add(".jpeg");
filePicker.FileTypeFilter.Add(".png");
StorageFile file = await filePicker.PickSingleFileAsync();
if (file != null)
{
var stream = await file.OpenAsync(FileAccessMode.Read);
var bitmap = new BitmapImage
{
UriSource = new Uri(file.Path, UriKind.Absolute)
};
await bitmap.SetSourceAsync(stream);
ImageSrc = bitmap;
}
}
protected void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
Now, the trick which solved the image problem is in View code behind class.
public sealed partial class MainPage : Page
{
private ViewModel dataContext;
public MainPage()
{
this.InitializeComponent();
dataContext = new ViewModel();
DataContext = dataContext;
}
**private void PageLoaded(object sender, RoutedEventArgs e)
{
if (DataContext is ViewModel dc)
{
dc.PropertyChanged += Dc_PropertyChanged;
}
}
private void Dc_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if(e.PropertyName.Equals("ImgSource"))
{
if (DataContext is ViewModel dc)
{
ShowImage.Source = dc.ImageSrc;
}
}
}**
}
What was needed it was that the source of the Image UI element to be set explicitly. I made this by subscribing to the PropertyChanged event from ViewModel and set the image source.

Group Swtich Xamarin forms

I have a Listview that is creating dynamically data and a switch in every row, what i want is when i toggle a switch the others untoggle.
Is this possible to do?
Example:
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="6*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Text="{Binding article_description}"
FontAttributes="Bold" FontSize="13" Margin="10,5,0,-6" Grid.Row="0" LineBreakMode="NoWrap"/>
<Label Text="{Binding dish_name}"
FontSize="13" Margin="10,0,0,2" Grid.Row="1" Grid.Column="0"/>
<Label Grid.Row="0" Grid.Column="0" x:Name="LabelReserved" Text="{Binding reserved}" IsVisible="false" LineBreakMode="NoWrap"/>
<Switch Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" HorizontalOptions="Start" VerticalOptions="Center" IsEnabled="False" Toggled="SwitchMenu_OnToggled" >
<Switch.Triggers>
<DataTrigger TargetType="Switch" Binding="{Binding Source={x:Reference LabelReserved},
Path=Text.Length}" Value="7">
<Setter Property="IsToggled" Value="true" />
</DataTrigger>
</Switch.Triggers>
</Switch>
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
Note: This solution was a bit of an experiment on my part - so I would recommend that, if you decide to implement this, use it with caution.
The intent here is to extend Switch to be able to act as a grouped radio button.
First step would be to create a IsToggled or IsChecked or similar property in the item that acts as BindingContext to each list-item. You can implement an interface like:
public interface IToggableItem
{
string GroupName { get; } //not mandatory, only added to support grouped lists
bool IsChecked { get; set; }
}
Second step would be extend Switch to be aware of items-list. We can do that by adding a GroupContext bindable property - which basically represents the parent list-view's ItemsSource.
When a switch is toggled, it iterates through the items-list to set the property as false on other items.
For example:
public class GroupedSwitch : CustomSwitch
{
public static readonly BindableProperty IsGroupingEnabledProperty =
BindableProperty.Create(
"IsGroupingEnabled", typeof(bool), typeof(GroupedSwitch),
defaultValue: default(bool));
public bool IsGroupingEnabled
{
get { return (bool)GetValue(IsGroupingEnabledProperty); }
set { SetValue(IsGroupingEnabledProperty, value); }
}
public static readonly BindableProperty GroupContextProperty =
BindableProperty.Create(
"GroupContext", typeof(IEnumerable), typeof(GroupedSwitch),
defaultValue: default(IEnumerable));
public IEnumerable GroupContext
{
get { return (IEnumerable)GetValue(GroupContextProperty); }
set { SetValue(GroupContextProperty, value); }
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName != nameof(IsToggled))
return;
if (IsToggled != true || GroupContext == null)
return;
var currentItem = BindingContext as IToggableItem;
if (currentItem == null)
return;
if (IsGroupingEnabled)
{
var groupList = GroupContext as IEnumerable<IGrouping<string, IToggableItem>>;
var currentGroup = groupList.FirstOrDefault(x => x.Key == currentItem.GroupName);
if (currentGroup != null)
foreach (var item in currentGroup)
{
if (item != currentItem)
item.IsChecked = false;
}
}
else
{
var simpleList = GroupContext as IEnumerable<IToggableItem>;
if (simpleList != null)
foreach (var item in simpleList)
{
if (item != currentItem)
item.IsChecked = false;
}
}
}
}
Third step would be bind GroupContext property to parent ListView's items source. For example:
<ListView x:Name="ParentListView" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" VerticalOptions="Center">
<Label Text="{Binding Name}" />
<local:GroupedSwitch
IsToggled="{Binding IsChecked}"
GroupContext="{Binding ItemsSource, Source={x:Reference ParentListView}}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Or,
<ListView x:Name="_parentList" IsGroupingEnabled="true" >
<ListView.GroupHeaderTemplate>
<DataTemplate>
<TextCell Text="{Binding Key}" />
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Name}" />
<local:GroupedSwitch
ToggledStateFromCode="{Binding IsSwitchOn}"
IsGroupingEnabled="true"
GroupContext="{Binding ItemsSource, Source={x:Reference _parentList}}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
EDIT 1: Updated code to add support for grouped list.
You should use triggers on each switch. Check this out:
https://www.tutorialspoint.com/xaml/xaml_triggers.htm

Access a control inside a Listbox DataTemplate

I'm developing a Windows Phone 8 and I have a single selection Listbox and this DataTemplate:
<DataTemplate x:Key="LocalizationItemTemplate">
<Border BorderBrush="Black" BorderThickness="2" CornerRadius="8" Background="#FF003847" Height="80">
<Grid x:Name="contentGrid" Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*"/>
<ColumnDefinition Width="90*"/>
</Grid.ColumnDefinitions>
<CheckBox x:Name="selectedCheck" Content="CheckBox" HorizontalAlignment="Center" Height="20" Margin="0" VerticalAlignment="Center" Width="20"/>
<TextBlock x:Name="locationName" TextWrapping="Wrap" Text="{Binding Name}" Margin="10,34,0,34" VerticalAlignment="Center" FontSize="24" Grid.ColumnSpan="2" Height="0"/>
</Grid>
</Border>
</DataTemplate>
How can I access the selectedCheck CheckBox programmatically?
private T FindElementInVisualTree<T>(DependencyObject parentElement, string name) where T : DependencyObject
{
var count = VisualTreeHelper.GetChildrenCount(parentElement);
if (count == 0)
return null;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parentElement, i);
if (child != null && child is FrameworkElement && (child as FrameworkElement).Name.Equals(name))
{
return (T)child;
}
else
{
var result = FindElementInVisualTree<T>(child, name);
if (result != null)
return result;
}
}
return null;
}
Usage:
ListBoxItem item = list.ItemContainerGenerator.ContainerFromItem(list.SelectedItem) as ListBoxItem;
CheckBox check = FindElementInVisualTree<CheckBox>(item, "selectedCheck");
But, I think you need to bind IsChecked property on selectedCheck object to manipulate on it
<CheckBox x:Name="selectedCheck" IsChecked={Binding Checked, Mode=TwoWay} ...

ScrollViewer Scrolling control for Windows phone 7

I have to insert elements to a itemscontrol at top of the list. While inserting an element to the list at 0th position, list is scrolling to that item. But i don't want to scroll the item to the top of the list, when an element is inserted. Any ideas for making this possible.
Code of xaml :
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
<Button Height="72" Content="Add More" Click="Button_Click"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<ScrollViewer x:Name="MainScrollViewer" Grid.Row="1" Margin="12,0,12,0" >
<Grid x:Name="ContentPanel" >
<ItemsControl x:Name="MainItemsControl" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Margin="10" Text="{Binding Item}" FontSize="28" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</ScrollViewer>
</Grid>
My class Code:
public partial class MainPage : PhoneApplicationPage
{
private ObservableCollection<Model> mainList;
public ObservableCollection<Model> MainList
{
get { return mainList; }
set
{
mainList = value;
}
}
public MainPage()
{
InitializeComponent();
}
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
MainList = new ObservableCollection<Model>();
AddItems(15);
ContentPanel.DataContext = MainList;
}
void AddItems(int numberIfItemsToAdd)
{
Debug.WriteLine("AddItems()");
int start = MainList.Count;
int end = start + numberIfItemsToAdd;
for (int i = start; i < end; i++)
{
Model model = new Model();
model.Item="Item" + i;
MainList.Insert(0, model);
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
AddItems(1);
}
}
Thank you in advance.

ListPicker within LongListSelector not retaining value after scrolling

I am developing a WP7 app that contains a LongListSelector. Within the ItemTemplate there is a ListPicker. I select a value from the ListPicker for the first item in the LongListSelector, then select a value from the ListPicker for the 2nd item. If I then scroll down the page and back to the top again, the value I selected in the ListPicker for the 1st item will be reset (SelectedIndex=0).
I have put code in the Link and Unlink events of the LongListSelector to write to the output window and I have found that when the 1st item unlinks (due to scrolling down the page), the ListPicker value is what I selected but as soon as the link event fires (due to scrolling back up the page) for the 1st item the value is reset.
Im using ObserableCollection and implement the INPC interface for my objects and the ViewModel updates when the ListPicker selection is changed.
How can I ensure that the value in the ListPickers are retaining during scrolling of the LongListSelector?
MainPage.xaml
<phone:PhoneApplicationPage
x:Class="LongListSelector.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="5000"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True"
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit">
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Name="occurrenceItemTemplate">
<Grid>
<StackPanel Orientation="Vertical" Margin="5,0,0,0" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Name="tbkEventDateTime" Grid.Row="0" Grid.Column="0"
Text="{Binding ItemNumber}"
Style="{StaticResource PhoneTextSmallStyle}" />
<!--<TextBox Name="txtEventDateTime" Grid.Row="1" Grid.Column="0"
Text="{Binding Result}" />-->
<toolkit:ListPicker Name="lpkResult" Margin="12,0,12,12"
Grid.Row="1" Grid.Column="0"
ItemsSource="{Binding TestItemResultList, Mode=OneTime}"
SelectedIndex="{Binding Result, Mode=TwoWay}"
CacheMode="BitmapCache">
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" />
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
</toolkit:ListPicker>
</Grid>
</StackPanel>
</Grid>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" VerticalAlignment="Stretch">
<toolkit:LongListSelector x:Name="lstOutstandingOccurrences"
Margin="0,12,0,0" Padding="0,0,0,24"
ItemTemplate="{StaticResource occurrenceItemTemplate}"
ItemsSource="{Binding TestItemCollection, Mode=TwoWay}"
IsFlatList="True" ShowListHeader="False" >
</toolkit:LongListSelector>
</Grid>
</Grid>
MainPage.xaml.cs
public partial class MainPage : PhoneApplicationPage
{
private TestItemViewModel _vm;
public TestItemViewModel ViewModel
{
get
{
if (_vm == null)
_vm = new TestItemViewModel();
return _vm;
}
private set
{
_vm = value;
}
}
// Constructor
public MainPage()
{
InitializeComponent();
this.lstOutstandingOccurrences.Link += new EventHandler<LinkUnlinkEventArgs>(lstOutstandingOccurrences_Link);
this.lstOutstandingOccurrences.Unlink += new EventHandler<LinkUnlinkEventArgs>(lstOutstandingOccurrences_Unlink);
this.DataContext = this.ViewModel;
}
void lstOutstandingOccurrences_Link(object sender, LinkUnlinkEventArgs e)
{
var item = e.ContentPresenter.Content as TestItem;
Debug.WriteLine("Link event for ItemNumber {0} = {1}", item.ItemNumber, item.Result);
}
void lstOutstandingOccurrences_Unlink(object sender, LinkUnlinkEventArgs e)
{
var item = e.ContentPresenter.Content as TestItem;
Debug.WriteLine("Unlink event for ItemNumber {0} = {1}", item.ItemNumber, item.Result);
}
}
TestItemViewModel.cs
public class TestItemViewModel : BaseINPC
{
public ObservableCollection<TestItem> TestItemCollection
{
get;
private set;
}
// Constructor
public TestItemViewModel()
{
this.TestItemCollection = new ObservableCollection<TestItem>();
CreateTestData(20);
}
public void CreateTestData(int totalItems)
{
//create test data for long list selector.
for (int i = 1; i <= totalItems; i++)
{
this.TestItemCollection.Add(new TestItem(i, 0));
}
}
}
Models
public class ListHelperListItem
{
public int Value { get; set; }
public string Text { get; set; }
}
public class TestItem : BaseINPC
{
public TestItem(int ItemNumber, int Result)
{
this.ItemNumber = ItemNumber;
this.Result = Result;
}
public int ItemNumber { get; set; }
private int _Result;
public int Result
{
get
{
return _Result;
}
set
{
//if statement is for debugging purposes only.
if (this.ItemNumber == 1)
{
_Result = value;
}
_Result = value;
RaisePropertyChanged("Result");
}
}
private List<ListHelperListItem> _TestItemResultList;
public List<ListHelperListItem> TestItemResultList
{
get
{
_TestItemResultList = new List<ListHelperListItem>();
_TestItemResultList.Add(new ListHelperListItem { Value = 0, Text = " " });
_TestItemResultList.Add(new ListHelperListItem { Value = 1, Text = "Yes" });
_TestItemResultList.Add(new ListHelperListItem { Value = 2, Text = "No" });
_TestItemResultList.Add(new ListHelperListItem { Value = 3, Text = "Ignore" });
return _TestItemResultList;
}
}
}
Thanks for your help!

Resources