I am using Windows Phone Toolkit nuget package (http://phone.codeplex.com/). I want to create a LoopingSelector in my WP8 application, where I can select day dates, but I don't want the LoopingSelector to show days before today in it's selection.
My control looks like this:
http://s2.postimg.org/6bgnxefnt/image.png
This is perfectly working until I select the first element of the source (today Dec 04), then my application becomes unresponsive (I can press buttons, effect are shown, but the event handlers won't execute). I guess this is an infinite loop or deadlock, but it's not in my own code (I debugged it).
The definition of the LoopingSelector's data source:
public class NextDatesDataSource : LoopingDataSource<NextDateModel>
{
public NextDatesDataSource() : base(NextDateModel.Create(DateTime.Today)) { }
protected override NextDateModel GetNext(NextDateModel relativeTo)
{
if (relativeTo.Date == DateTime.Today + TimeSpan.FromDays(10))
return null;
return NextDateModel.Create(relativeTo.Date + TimeSpan.FromDays(1));
}
protected override NextDateModel GetPrevious(NextDateModel relativeTo)
{
//if i comment the next two lines, then everything works perfect
if (relativeTo.Date == DateTime.Today)
return null;
return NextDateModel.Create(relativeTo.Date - TimeSpan.FromDays(1));
}
}
LoopingDataSource:
public abstract class LoopingDataSource<T> : ILoopingSelectorDataSource
{
protected LoopingDataSource() { selectedItem = default(T); }
protected LoopingDataSource(T initialSelection) { selectedItem = initialSelection; }
protected abstract T GetNext(T relativeTo);
protected abstract T GetPrevious(T relativeTo);
public object GetNext(object relativeTo)
{
if (relativeTo == null) return null;
return GetNext((T)relativeTo);
}
public object GetPrevious(object relativeTo)
{
if (relativeTo == null) return null;
return GetPrevious((T)relativeTo);
}
private T selectedItem;
public object SelectedItem
{
get
{
return selectedItem;
}
set
{
object oldVal = selectedItem;
selectedItem = (T)value;
if (SelectionChanged != null)
SelectionChanged(this, new SelectionChangedEventArgs(new object[] { oldVal }, new object[] { value }));
}
}
public T Selected
{
get { return (T)SelectedItem; }
set { SelectedItem = value; }
}
public event EventHandler<SelectionChangedEventArgs> SelectionChanged;
}
The definition of the control element:
<p:LoopingSelector
x:Name="DaySelector"
Grid.Column="0" Grid.Row="1"
ItemSize="230,110"
ItemMargin="6" Margin="10,5,5,5">
<p:LoopingSelector.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Header}" FontSize="28" VerticalAlignment="Top"/>
<TextBlock Text="{Binding Body}" FontSize="42" FontWeight="Bold" VerticalAlignment="Bottom"/>
</Grid>
</DataTemplate>
</p:LoopingSelector.ItemTemplate>
</p:LoopingSelector>
I do this in the constructor:
DaySelector.DataSource = new NextDatesDataSource();
Thank you very much!
Related
I added a button to player from PlayerFramework, when click that button, a ListView appear for select video quality.
But I dont know how to implement ItemClicked event to handle when user click a item in ListView. Anyone can help me?
My code:
Entertainment.xaml
<AppBarButton x:Name="QualityButton"
Grid.Column="3"
Width="30"
Height="30"
Margin="8,0,8,0"
Icon="Setting"
Style="{TemplateBinding TransportBarButtonStyle}"
Visibility="Visible">
<AppBarButton.Flyout>
<Flyout>
<ListView Name="listView"
IsItemClickEnabled="True"
ItemsSource="{Binding List}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Flyout>
</AppBarButton.Flyout>
</AppBarButton>
CustomInteractiveViewModel.cs
public class CustomInteractiveViewModel : InteractiveViewModel
{
public CustomInteractiveViewModel(List<string> list, MediaPlayer player)
: base(player)
{
List = list;
}
public List<string> List { get; set; }
}
MainPage.cs
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var list = new List<string> { "360p", "480p", "720p" };
player.InteractiveViewModel = new CustomInteractiveViewModel(list, player);
player.Source = new Uri(Video, UriKind.RelativeOrAbsolute);
}
MainPage.xaml
<Page x:Class="testPlayer.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:testPlayer"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mmppf="using:Microsoft.PlayerFramework"
xmlns:webvtt="using:Microsoft.PlayerFramework.WebVTT"
mc:Ignorable="d">
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Entertainment.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<mmppf:MediaPlayer Name="player" />
</Grid>
</Page>
It is not supported to binding event like ItemClick or SelectionChanged in ResourceDictionary, a simple method is to create the code behind of this ResourceDictionary, but to maintain the MVVM pattern integrity, it's better to register a Attached property, and bind events to this attached property.
You can change your code in Entertainment.xaml like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Microsoft.PlayerFramework.Controls"
xmlns:local="using:Microsoft.PlayerFramework">
...
<AppBarButton x:Name="QualityButton" Grid.Column="3" Width="30" Height="30" Margin="8,0,8,0"
Icon="Setting" Style="{TemplateBinding TransportBarButtonStyle}" Visibility="Visible">
<AppBarButton.Flyout>
<Flyout>
<ListView Name="listView" IsItemClickEnabled="True" ItemsSource="{Binding List}" controls:CustomInteractiveViewModel.ItemClickCommand="{Binding ItemClickedCommand}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Flyout>
</AppBarButton.Flyout>
</AppBarButton>
...
</ResourceDictionary>
and the code in CustomInteractiveViewModel.cs:
public class CustomInteractiveViewModel : InteractiveViewModel
{
public CustomInteractiveViewModel(List<string> list, MediaPlayer player, DelegateCommand<string> itemclickedcommand)
: base(player)
{
List = list;
ItemClickedCommand = itemclickedcommand;
}
public List<string> List { get; set; }
public DelegateCommand<string> ItemClickedCommand { get; set; }
public static DependencyProperty ItemClickCommandProperty =
DependencyProperty.RegisterAttached("ItemClickCommand",
typeof(ICommand),
typeof(CustomInteractiveViewModel),
new PropertyMetadata(null, OnItemClickCommandChanged));
public static void SetItemClickCommand(DependencyObject target, ICommand value)
{
target.SetValue(ItemClickCommandProperty, value);
}
public static ICommand GetItemClickCommand(DependencyObject target)
{
return (ICommand)target.GetValue(ItemClickCommandProperty);
}
private static void OnItemClickCommandChanged(DependencyObject target,
DependencyPropertyChangedEventArgs e)
{
var element = target as ListViewBase;
if (element != null)
{
if ((e.NewValue != null) && (e.OldValue == null))
{
element.ItemClick += OnItemClick;
}
else if ((e.NewValue == null) && (e.OldValue != null))
{
element.ItemClick -= OnItemClick;
}
}
}
private static void OnItemClick(object sender, ItemClickEventArgs e)
{
GetItemClickCommand(sender as ListViewBase).Execute(e.ClickedItem);
}
}
Finally in your MainPage.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var list = new List<string> { "360p", "480p", "720p" };
var ItemClickedCommand = new DelegateCommand<string>(ItemClicked);
player.InteractiveViewModel = new CustomInteractiveViewModel(list, player, ItemClickedCommand);
}
public void ItemClicked(string item)
{
//TODO:
}
And the DelegateCommand<T> class is like this:
public class DelegateCommand<T> : ICommand
{
private readonly Action<T> _execute;
private readonly Func<T, bool> _canExecute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<T> execute, Func<T, bool> canexecute = null)
{
if (execute == null)
throw new ArgumentNullException(nameof(execute));
_execute = execute;
_canExecute = canexecute ?? (e => true);
}
public bool CanExecute(object p)
{
try { return _canExecute(ConvertParameterValue(p)); }
catch { return false; }
}
public void Execute(object p)
{
if (!this.CanExecute(p))
return;
_execute(ConvertParameterValue(p));
}
private static T ConvertParameterValue(object parameter)
{
parameter = parameter is T ? parameter : Convert.ChangeType(parameter, typeof(T));
return (T)parameter;
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
What I want to achieve may not be possible in XAML. If it is possible then its probably due to a XAML feature worth knowing. If not, then I've also learned something.
I have a button flyout which is data-bound to a view model. The view model provides a new instance of an object to the content of the flyout, via a get accessor.
Each time the button is pressed I want the flyout to present a new instance of the object.
The problem: The object is created only once, and re-presented each time the flyout is opened.
ViewModel.cs
class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
class ViewModel
{
static int itemCount;
public Item GetNewItem {
get {
itemCount++;
Debug.WriteLine("Created item: " + itemCount);
return new Item() { Id = itemCount, Name = "Item_" + itemCount} ;
}
}
}
MainPage.xaml.cs
<Page.Resources>
<local:ViewModel x:Key="ViewModel"/>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
DataContext="{StaticResource ViewModel}">
<Button Content="Create Item">
<Button.Flyout>
<Flyout>
<StackPanel DataContext="{Binding Path=GetNewItem}">
<TextBlock Text="{Binding Path=Id}"/>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
</Grid>
Output:
The trace statement "Created item: Item_1" appears, but not "Created Item_2", etc..
The same data ("1" and "Item_1") is presented each time the button is pressed.
Investigation
I can make it work in the code-behind of the main page. I name the grid, and add an Opening event handler to the flyout
private void Flyout_Opening(object sender, object e) {
var gridDataContext = (ViewModel)this.grid.DataContext;
this.stackPanel.DataContext = gridDataContext.GetNewItem;
}
Works fine now! (but I want to do it in XAML)
I have tried implementing INotifyPropertyChanged on the ViewModel, but this didn't work.
class ViewModel : INotifyPropertyChanged
{
static int itemCount;
public Item GetNewItem {
get {
itemCount++;
Debug.WriteLine("Created item: " + itemCount);
OnPropertyChanged("GetNewItem");
return new Item() { Id = itemCount, Name = "Item_" + itemCount} ;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name) {
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(name));
}
}
I have a user control which has a button and a dependency property for the action the button is to execute. The page which contains the control sets the action in XAML.
MyUserControl.cs
A Button, and dependency property ButtonAction, of type Action. When the button is clicked it executes the ButtonAction.
MainPage.xaml.cs
Action Action1
Action Action2
MainPage.xaml
Present an instance of MyUserControl, with ButtonAction=Action1
The problem: The ButtonAction property is not assigned from the XAML
MyUserControl.cs
public sealed partial class MyUserControl : UserControl
{
public Action ButtonAction {
get { return (Action)GetValue(ButtonActionProperty); }
set { SetValue(ButtonActionProperty, value); }
}
public static readonly DependencyProperty ButtonActionProperty =
DependencyProperty.Register("ButtonAction", typeof(Action), typeof(MyUserControl), new PropertyMetadata(null,ButtonAction_PropertyChanged));
private static void ButtonAction_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
Debug.WriteLine("ButtonAction_PropertyChanged");
// Is not called!
}
public MyUserControl() {
this.InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e) {
if (ButtonAction != null) {
// Never reaches here!
ButtonAction();
}
}
}
MyUserControl.xaml
<Grid>
<Button Click="Button_Click">Do The Attached Action!</Button>
</Grid>
MainPage.xaml.cs
Action Action1 = (
() => { Debug.WriteLine("Action1 called"); });
Action Action2 = (() => { Debug.WriteLine("Action2 called"); });
MainPage.xaml
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<local:MyUserControl x:Name="myUserControl" ButtonAction="{Binding Action1}"/>
</Grid>
It does work if in the code-behind for MainPage (MainPage.xaml.cs) I assign the action in the Loaded event.
private void Page_Loaded(object sender, RoutedEventArgs e) {
this.myUserControl.ButtonAction = Action1;
}
In this case the PropertyChanged callback in the user control is also called. (This handler is provided only for diagnostic purposes. I can't see how it can be used to support the property in practice).
The issue is in your data binding. The Action1 in ButtonAction="{Binding Action1}" should be a public property while you defined it as a private variable.
Also, you cannot just declare a normal property directly in the code behind like that. You will need either a dependency property, or more commonly, a public property inside a viewmodel which implements INotifyPropertyChanged.
If we go with the second approach, we will need to create a viewmodel class like the following with an Action1 property. Note the OnPropertyChanged stuff is just the standard way of implementing INotifyPropertyChanged.
public class ViewModel : INotifyPropertyChanged
{
private Action _action1;
public Action Action1
{
get { return _action1; }
set
{
_action1 = value;
OnPropertyChanged("Action1");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
And then you just need to assign this to the DataContext of your main page.
public MainPage()
{
this.InitializeComponent();
var vm = new ViewModel();
vm.Action1 = (() =>
{
Debug.WriteLine("Action1 called");
});
this.DataContext = vm;
}
With these two changes, your ButtonAction callback should be firing now. :)
what would be the steps to add timer to change selected item's image in listpicker. Any suggestions? FYI, have never used ListPicker before. So i am finding it kind of hard to understand where to start and what to do.
You will need an ObservableCollection of your ImageSources and a DispatcherTimer to fire the events every TimeSpan of your choosing.
Here's some code to help you get started. You can modify it to do exactly what you want. It basically contains a ListPicker that has a collection of images as its ItemTemplate. Every one second the DispatchTimer fires and switches the selectedItem's Image between the 2 default images that are created in about every single WP8.0 application.
Make it a habit to use ObervableCollection when you want to display something to the user instead of a List, it will make your WP8 development life a lot easier.
XAML
<toolkit:ListPicker x:Name="my_listpicker" SelectionChanged="my_listpicker_SelectionChanged_1" Background="Black">
<toolkit:ListPicker.HeaderTemplate>
<DataTemplate/>
</toolkit:ListPicker.HeaderTemplate>
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<StackPanel Background="Black">
<Image Source="{Binding ImageSource}" Height="200"></Image>
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
</toolkit:ListPicker>
C# Namespaces
using System.ComponentModel; // ObservableCollection
using System.Collections.ObjectModel; // INotifyPropertyChanged
using System.Windows.Threading; // Dispatch Timer
C# Model of your Images (pretty basic, but pay attention to the INotifyPropertyChanged
public class MyBindingImage : INotifyPropertyChanged
{
public MyBindingImage() { }
public MyBindingImage(string source)
{
this.ImageSource = source;
}
// Create the OnPropertyChanged method to raise the event
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
string image_source;
public String ImageSource {
get { return image_source; }
set
{
image_source = value;
OnPropertyChanged("ImageSource");
}
}
}
C# (Create the Timer and ObservableCollection and Set the ItemSource)
DispatcherTimer timer;
// Constructor
public MainPage()
{
// create our dispatch timer
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(2000);
timer.Tick += OnTimerTick;
InitializeComponent();
// create our list picker elements
ObservableCollection<MyBindingImage> my_image_list = new ObservableCollection<MyBindingImage>();
my_image_list.Add(new MyBindingImage("Assets/ApplicationIcon.png"));
my_image_list.Add(new MyBindingImage("Assets/AlignmentGrid.png"));
my_listpicker.ItemsSource = my_image_list;
}
C# Events (For the Timer & ListPicker SelectionChange)
// each time the selection has changd: stop the timer, then start it again
private void my_listpicker_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
if (timer != null)
{
timer.Stop();
timer.Start();
}
}
// if the timer is on, cycle the images of the selected item
private void OnTimerTick(object sender, EventArgs e)
{
try
{
MyBindingImage item = (MyBindingImage) my_listpicker.SelectedItem;
// cycle the selected image between to different images
if (item.ImageSource == "Assets/AlignmentGrid.png")
{
item.ImageSource = "Assets/ApplicationIcon.png";
}
else
{
item.ImageSource = "Assets/AlignmentGrid.png";
}
}
catch (Exception ex)
{
string error_message = ex.Message;
}
}
[APPLICATION SCREENSHOT]
Hi Listpicker not binding proper items.it binding project name list if i use async method.In case if i use same code inside constructor it working perfectly.
i tried this code:
c#:
List<Orderlist> GetOrderItems = new List<Orderlist>();
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
var resultOrderReq = await this.objOrderMgr.GetOrders(objOrderReq, this.objRequestHeaderHelper.GetRequestHeaders());
var reslistOrder = resultOrderReq.orderlist;
foreach (var item in reslistOrder)
{
GetOrderItems.Add(new Orderlist() { OrderId = item.orderid });
}
this.ProductSub.ItemsSource = GetOrderItems;
}
}
public class Orderlist
{
public long OrderId { get; set; }
}
XAML
<toolkit:ListPicker Grid.Row="0" x:Name="ProductSub" ItemTemplate="{StaticResource PickerItemTemplate}" ExpansionMode="ExpansionAllowed" FullModeItemTemplate="{StaticResource PickerFullModeItemTemplate}"/>
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Name="PickerItemTemplate">
<TextBlock Text="{Binding OrderId}"></TextBlock>
</DataTemplate>
<DataTemplate x:Name="PickerFullModeItemTemplate">
<TextBlock Text="{Binding OrderId}"></TextBlock>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
Usually, a problem like this comes down to one of two things:
You forgot to implement INotifyPropertyChanged for properties that change.
You are using a non-observable collection, e.g., List<T> instead of ObservableCollection<T>.
I can't tell for sure based on your problem description, but it looks like it might be the observable collection problem. Try replacing List<Orderlist> with ObservableCollection<Orderlist>.
//I made little changes in your code. may this will help you.
List<Orderlist> GetOrderItems = new List<Orderlist>();
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.NavigationMode != NavigationMode.Back)
{
var resultOrderReq = await this.objOrderMgr.GetOrders(objOrderReq, this.objRequestHeaderHelper.GetRequestHeaders());
var reslistOrder = resultOrderReq.orderlist;
foreach (var item in reslistOrder)
{
GetOrderItems.Add(new { OrderId = item.orderid });
}
this.ProductSub.ItemsSource = GetOrderItems;
}
}
public class Orderlist
{
public long OrderId { get; set; }
}