Instantiate new object from Binding in xaml for Flyout - windows-runtime

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));
}
}

Related

Close Flyout which contains a UserControl

I am building a user control for a TextBox because I want it to have some special behaviour.
The control can be used in several contexts, including as a flyout for a button. When it is a flyout I want to close the flyout when the user presses the Enter key while editing text.
To achieve this, the control has a ParentButton dependency property which, if set, stores the button with the flyout, and the XAML for the parent page sets it in this case. The control has a KeyUp handler which detects the Enter key and, if ParentButton property is set, closes its flyout.
TextBoxUC.xaml
<UserControl
x:Class="TextBoxUCDemo.TextBoxUC"
...
xmlns:local="using:TextBoxUCDemo"
...>
<StackPanel Width="250">
<TextBox KeyUp="TextBox_KeyUp" Text="Hello" />
</StackPanel>
TextBoxUC.xaml.cs
public sealed partial class TextBoxUC : UserControl
{
public TextBoxUC() {
this.InitializeComponent();
}
internal static readonly DependencyProperty ParentButtonProperty =
DependencyProperty.Register("ParentButton", typeof(Button), typeof(TextBoxUC), new PropertyMetadata(null));
public Button ParentButton {
get { return ((Button)GetValue(ParentButtonProperty)); }
set { SetValue(ParentButtonProperty, value); }
}
private void TextBox_KeyUp(object sender, KeyRoutedEventArgs e) {
switch (e.Key) {
case VirtualKey.Enter:
// (Do something with the Text...)
// If this is a flyout from a button then hide the flyout.
if (ParentButton != null) { // Always null!
ParentButton.Flyout.Hide();
}
break;
default: return;
}
}
}
MainPage.xaml
<Page
x:Class="TextBoxUCDemo.MainPage"
...
xmlns:local="using:TextBoxUCDemo"
...>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="200,300">
<Button Name="flyoutTextBoxButton" Content="Edit">
<Button.Flyout>
<Flyout>
<local:TextBoxUC ParentButton="{Binding ElementName=flyoutTextBoxButton, Path=.}"/>
</Flyout>
</Button.Flyout>
</Button>
</Grid>
The problem is that the ParentButton is always null.
-- Edit --
I've narrowed the problem down to the binding to the element in the XAML. If I set the ParentButton from the code-behind of the MainPage, then it works.
In 'MainPage.xaml':
Loaded="Page_Loaded"
....
<local:TextBoxUC/>
In MainPage.xaml.cs
private void Page_Loaded(object sender, RoutedEventArgs e) {
textBoxUC.ParentButton = this.flyoutTextBoxButton;
}
Effect:
if (ParentButton != null) {
// Reaches here
}
So: THE PROBLEM is in the xaml ParentButton="{Binding ElementName=flyoutTextBoxButton, Path=.}", which compiles but has no effect.
If I add a changed event handler to the registration of the dependency property, then the handler is called when the ParentButton is set from the code-behind, but never called for the binding to the ElementName. The handler seems to be only useful for debugging purposes. I can't see that it is needed to make the property work.
Okay, how about this? I've used it in the past. Works fine.
[Microsoft.Xaml.Interactivity.TypeConstraint(typeof(Windows.UI.Xaml.Controls.TextBox))]
public class CloseFlyoutOnEnterBehavior : DependencyObject, IBehavior
{
public DependencyObject AssociatedObject { get; set; }
public void Attach(DependencyObject obj)
{
this.AssociatedObject = obj;
(obj as TextBox).KeyUp += TextBox_KeyUp;
}
void TextBox_KeyUp(object sender, KeyRoutedEventArgs e)
{
if (!e.Key.Equals(Windows.System.VirtualKey.Enter))
return;
var parent = this.AssociatedObject;
while (parent != null)
{
if (parent is FlyoutPresenter)
{
((parent as FlyoutPresenter).Parent as Popup).IsOpen = false;
return;
}
else
{
parent = VisualTreeHelper.GetParent(parent);
}
}
}
public void Detach()
{
(this.AssociatedObject as TextBox).KeyUp -= TextBox_KeyUp;
}
}
Use it like this:
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="Click Me">
<Button.Flyout>
<Flyout Placement="Bottom">
<TextBox Width="200"
Header="Name"
PlaceholderText="Jerry Nixon">
<Interactivity:Interaction.Behaviors>
<local:CloseFlyoutOnEnterBehavior />
</Interactivity:Interaction.Behaviors>
</TextBox>
</Flyout>
</Button.Flyout>
</Button>
Learn more about behaviors here:
http://blog.jerrynixon.com/2013/10/everything-i-know-about-behaviors-in.html
And here (lesson 3):
http://blog.jerrynixon.com/2014/01/the-most-comprehensive-blend-for-visual.html
Best of luck!
You can add to your control normal property of type Action that will contain lambda expression.
You will set this property when creating control and then invoke it inside your control on EnterPressed event.
public class MyControll
{
public Action ActionAfterEnterPressed {get; set;}
private void HandleOnEnterPressed()
{
if(ActionAfterEnterPressed != null)
{
ActionAfterEnterPressed.Invoke();
}
}
}
somwhere where you create your control
...
MyControl c = new MyControl()
c.ActionAfterEnterPressed = CloseFlyuot;
....
private void CloseFlyuot()
{
_myFlyout.IsOpen = false;
}
This way you can set any action and invoke it when needed from inside of your control withou needing to bother with what action actually does.
Best of luck.
You're making it a dependency property. That's the first, right start. But until you handle the changed event, you aren't really going to get any value from it.
I discuss this more here:
http://blog.jerrynixon.com/2013/07/solved-two-way-binding-inside-user.html
Best of luck!

Listpicker is not bind after async call in windows phone 8

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; }
}

Windows phone 8: get value item of listbox

I have a listbox. Every item has an image and title (binding from my list). When I click an item of the listbox, how do I get the title value of that item.
create an event in ListBox called "SelectionChanged" and map it the method in the code behind of the XAML file. In the .cs file get the value from myListBox.SelectedItem and cast it to your list item type.
EX: in XAML file
<ListBox x:Name="myListBox" ItemsSource="{Binding Items}"
SelectionChanged="myListBox_SelectionChanged">
in xaml.cs file:
private void myListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var mySelectedItem = myListBox.SelectedItem as myObject;
}
I hope this helps.
There are already few similar questions (first, second). I'll try to show you an example (little extending this what #KirtiSagar (if it helps accept his solution as it's the same method) has said):
Let's assume that your ItemClass look like this:
public class ItemClass
{
public Uri ImagePath { get; set; }
public string Tile { get; set; } // I used string as an example - it can be any class
}
And you Bind it to your ListBox like this:
<ListBox Name="myList" Grid.Row="2">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImagePath}"/>
<TextBlock Text="{Binding Tile}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
It is a very simple example but should show you an overview how it works.
Then in my Page and Constructor I need to add my Collection and subscribe to events:
ObservableCollection<ItemClass> items = new ObservableCollection<ItemClass>();
public MainPage()
{
InitializeComponent();
myList.ItemsSource = items;
myList.SelectionChanged += myList_SelectionChanged;
}
The SelectinChanged event can be used for your purpose in many ways. For example you can use its SelectionChangedEventArgs properties. I will show some methods which will produce the same result. I mixed some things on purpose just to show how it can be done.
private void myList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (myList.SelectedItem != null)
{
string myTile = ((sender as ListBox).SelectedItem as ItemClass).Tile;
// you can also get your item like this - using EventArgs properties:
string myTileToo = ((ItemClass)(e.AddedItems[0])).Tile;
}
// also you can get this by SelectedIndex and your Item Collection
if ((sender as ListBox).SelectedIndex >= 0)
{
string myTileThree = items[myList.SelectedIndex].Tile;
}
}
Note that your LisBox can work in different SelectionModes for example Multipe - you can also try to use that if you need it (there are properties SelectedItems and AddedItems / RemovedItems, which are IList).

WinRT FlipView like control in WP8

In my app I need to display a collection of Images exactly like in the Windows Phone 8 Photo App where you can swipe right and left between the images.
I've tried both the Panorama and Pivot control but both controls don't behave like WinRTs FlipView.
Panorama fits quite well but appears to have the "Right-Peek" Amount hardwired into the control. (please correct me if I'm wrong)
Pivot in turn shows blackness during swipes (finger still down) and only displays the next image when you release your finger and the control scrolls the next item into place.
Any suggestions?
Here is the customized FlipView control for WP8 like WINRT FlipView Control...
Step 1 : Add a new Usercontrol and name it as "FlipView.xaml"
Step 2 : Add following xaml in "FlipView.xaml"
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<ContentPresenter Name="contentPresenter"/>
<Button BorderThickness="0" Name="leftButton" FontSize="70" Margin="-25" HorizontalAlignment="Left" VerticalAlignment="Center" Content="<" Click="Button_Click"/>
<Button BorderThickness="0" Name="rightButton" FontSize="70" Margin="-25" HorizontalAlignment="Right" VerticalAlignment="Center" Content=">" Click="Button_Click_1"/>
</Grid>
Step 3 : Add the following code in the "FlipView.cs"
public partial class FlipView : UserControl
{
public FlipView()
{
InitializeComponent();
Datasource = new List<object>();
SelectedIndex = 0;
}
private IList Datasource;
public static readonly DependencyProperty ItemTemplateProperty =
DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(FlipView), new PropertyMetadata(default(DataTemplate)));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set
{
SetValue(ItemTemplateProperty, value);
contentPresenter.ContentTemplate = value;
contentPresenter.Content = SelectedItem;
}
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IList), typeof(FlipView), new PropertyMetadata(default(IList)));
public IList ItemsSource
{
get { return (IList)GetValue(ItemsSourceProperty); }
set
{
SetValue(ItemsSourceProperty, value);
Datasource = value;
SelectedIndex = SelectedIndex;
}
}
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(FlipView), new PropertyMetadata(default(int)));
public int SelectedIndex
{
get { return (int)GetValue(SelectedIndexProperty); }
set
{
SetValue(SelectedIndexProperty, value);
rightButton.Visibility = leftButton.Visibility = Visibility.Visible;
if (SelectedIndex == 0)
{
leftButton.Visibility = Visibility.Collapsed;
}
if (SelectedIndex + 1 == Datasource.Count)
{
rightButton.Visibility = Visibility.Collapsed;
SelectedItem = Datasource[SelectedIndex];
}
if (Datasource.Count > SelectedIndex + 1)
{
SelectedItem = Datasource[SelectedIndex];
}
}
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(FlipView), new PropertyMetadata(default(object)));
public object SelectedItem
{
get { return (object)GetValue(SelectedItemProperty); }
set
{
SetValue(SelectedItemProperty, value);
contentPresenter.Content = SelectedItem;
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
SelectedIndex--;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
SelectedIndex++;
}
}
Step 4 : Now at the mainpage, add the namespace to use the flipview Usercontrol
Example:
xmlns:FlipViewControl="clr-namespace:ImageFlip" (Note: It differs according to your Solution name).
Step 5 : Using the namespace, add the flipview control as follow as..
<Grid x:Name="LayoutRoot" Background="Transparent">
<FlipViewControl:FlipView Name="imgViewer">
<FlipViewControl:FlipView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}" Stretch="Fill"/>
</DataTemplate>
</FlipViewControl:FlipView.ItemTemplate>
</FlipViewControl:FlipView>
</Grid>
Step 6 : Add the following code in mainpage.cs
// Constructor
public MainPage()
{
InitializeComponent();
// Sample code to localize the ApplicationBar
//BuildLocalizedApplicationBar();
imgViewer.ItemsSource = new List<string> { "/Images/1.jpg", "/Images/2.jpg", "/Images/3.jpg" };
}
Hope this will help.
Thanks
There is no direct equivalent to the FlipView in Windows Phone. The Panorama and Pivot controls have very different functionalities and are designed fro different purposes.
Telerik have a SlideView control which is very similar to the native control used by the photos app.
You can also get the Telerik controls free as part of the Nokia Premium Developer Program. (Worth investigating if you don't have a Dev Center subscription.)
I know it is not the same solution, but maybe you can tweak this coverflow example here... so that the images are not stacked but side by side?

ICommand will not trigger

My ICommand will not trigger unless I move the DataContext field into the the DataTemplate (contlisttemplate) for the Button. I have images set in a style resource, those disappear as soon as I move the DataContext field into the DataTemplate. Both images and ICommand should be using the same DataContext so I am unsure of why it will not work.
Here is a snippet of my code below.
DataContext="{Binding LongListViewModel, Source={StaticResource viewModelLocator}}"
<i:Interaction.Behaviors>
<GamePad:XboxBehavior StartFocusControlName="continuousList1" IsTopLevelViewForFocus="True"/>
</i:Interaction.Behaviors>
<UserControl.Resources>
<DataTemplate x:Key="contlisttemplate" >
<Button
Command="{Binding Gotodetailpage}"
Style="{StaticResource custherotile}">
</Button>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<xbox:ContinuousList
HorizontalAlignment="Left"
Name="continuousList1"
VerticalAlignment="Top"
ItemTemplate="{StaticResource contlisttemplate}"
ItemsSource="{Binding LongListItems}" Height="316" Width="1280"
Grid.Row="1"
>
<i:Interaction.Behaviors>
<GamePad:XboxBehavior IsContinuousListVuiEnabled="True" HasFocusRetention="True"/>
</i:Interaction.Behaviors>
</xbox:ContinuousList>
public class LongListViewModel : ViewModelBase<LongListViewModel>
{
private readonly IDialogService dialogService;
public Navigateto compass = new Navigateto();
public LongListViewModel()
{
LongListItems = new ObservableCollection<object>();
dictionaryListwithkey = new Dictionary<string, object>();
Gotodetailpage = new RelayCommand(PerformGotoDetailPage);
}
public LongListViewModel(IDialogService dialogService)
: this()
{
this.dialogService = dialogService;
}
public Program getherovideo
{
get { return (Program)LongListItems[0]; }
set
{
//SetProperty(ref currentVideo, value,x => x.CurrentVideo);
}
}
public ObservableCollection<object> LongListItems
{
get;
set;
}
public Dictionary<string, object> dictionaryListwithkey
{
get;
set;
}
public ICommand Gotodetailpage { get; private set; }
private void PerformGotoDetailPage()
{
// Console.WriteLine("List item clicked");
compass.goToDetailsPageWithPath("89");
}
}
In case anyone was wondering what the answer was . As per Aaron Hill ATG :
This looks like an issue of scope. The outer DataContext is your LongListViewModel class, which contains the desired ICommand, but the ItemsSource for the container is set to the LongListItems collection exposed by the view-model. This means the effective DataContext for the DataTemplate is an individual member of the collection, not the overall view-model.
Overriding the DataContext of the DataTemplate would let you point back to the view-model and access the ICommand, however it also means you lose any data present within the individual elements of the LongListItems collection. That is probably why the images no longer work in this case.
Since each item in the collection has its own button, it probably makes sense for the ICommand property to be exposed on the individual item rather than the view-model.