I'm starting out with C# Windows Phone 8 development and I am trying to write an app which uses a LongListSelector. The app will show a long list of train station names.
I have been looking at some of the samples online, including the MSDN PeopleHub, and PhotoHub samples but they seem very complex. I took the PeopleHub sample and hacked at it a bit until I got it to display a list of stations in a LongListSelector but I wasn't able to find out which item had been selected in the list. The SelectedItem property was just returning the app name when passed to another page and not which item had been picked in the list.
I think I need a basic example of how to add some items to a LongListSelector and then find and pass the selected item to another page. I don't fully understand how the control works, if you have to use some sort of DataBinding with the LongListSelector to populate it or whether it's something simpler like:
LongListSelectorThing.add("trainstationA");
LongListSelectorThing.add("trainstationB");
LongListSelectorThing.add("trainstationC");
etc
Can someone give me some simple basic pointers as to how to populate this control and find out which item the user selects? When I say which item they select, when the LongListSelector grid appears, they click on A for example, and it then shows a list of things beginning with A and they then click on trainstationA, I'd want to be able to detect they've picked trainstationA and pass that info to another page so I can display further information about it.
Sorry for if this seems basic, I'm quite new to this.
Thanks!
Here is a basic example which should help you understand:
First in your Page (xaml file) you define the control LongListSelector (LLS):
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<phone:LongListSelector Name="myLLS" Margin="0">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
You also declare how its Items will look like. It can be any UIElement - a button, Image, Grid and so on. In the code above I declared that my Item would be a TextBlock which content (text) I've bound to a property 'Name'. I've also given the LLS a name, that I can refer to it later.
In Page.cs code you populate the LLS. Lets create the simple Station class, and populate LLS:
public class Station
{
private string _stationName;
public string Name
{
get { return _stationName; }
set { _stationName = value; }
}
public Station(string station)
{
this.Name = station;
}
}
public partial class MainPage : PhoneApplicationPage
{
ObservableCollection<Station> trainStations = new ObservableCollection<Station>();
public MainPage()
{
InitializeComponent();
myLLS.ItemsSource = trainStations;
trainStations.Add(new Station("Germany"));
trainStations.Add(new Station("France"));
trainStations.Add(new Station("Italy"));
}
}
What is important:
look that in my Station class there is property called 'Name' - that's the one to which content of the TextBlock is bound.
I've created ObservableCollection which is a collection of my Stations - it's similar to a List but when the new item is added or removed the PropertyChanged event is raised and therefore your LongListSelector can be automatically updated when you add a new station.
I've assigned created collection to myLLS.ItemsSource - it means that created LLS will be populated with Items (described in xaml as DataTemplate) and a Source of this items is that collection.
Hope this helps. Happy coding.
Related
First thank you guys in StackOverFlow to give the opportunity, u guys doin really great job, I'm very beginner in windows 10 universal apps, I created app which has employee class for example in the models folder let us say like the following:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
}
and in xaml I created AutoSuggestBox and listView which I bind to ObervableCollection list of type Employee and this as the following:
<ListView ItemsSource="{x:Bind EmployeesList}"
HorizontalAlignment="Left">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Employee">
<StackPanel Orientation="Horizontal">
<CheckBox Checked="StatusCB_Checked"
Margin="5,2,5,0"/>
<TextBlock Name="EmpNametxtBlk"
Text="{x:Bind Name}"
VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Now once I type name in the AutoSuggestBox the list view will show the checkBox and the employee name , I want to check the checkbox and I want to get the employee object to get deleted from the list view and then add to another list view let's say for chosen employees , my questions is how I can bind the checkBox to the employee ID and then i can use that object in the code behind for the check event ? I mean once i check the checkbox of any added employee on the Listview I wanted to do :
deleted that employee from the ListView and add him in other list view
the other list view will be hidden till I delete employees from the first Listview.
how I can bind the checkbox to the ID of the employee and how I can extract the employee object in the check event or any other even, well I wish I explained what I want in clear way !
how I can extract the employee object in the check event or any other even
You can get the object from DataContext property of the sender object (just make sure you cast the sender from object to FrameworkElement type first)
As #thang2410199 said
You can get the object from DataContext property of the sender object.
You don't need bind the Id property to somewhere for deleting since you can get the employee instance from checkbox's DataContext property. You can delete the checked employee using the following code:
private void StatusCB_Checked(object sender, RoutedEventArgs e)
{
var checkbox = sender as CheckBox;
if(checkbox!=null)
{
var employee =(Employee)checkbox.DataContext;
Employeeslist.Remove(employee);
}
}
A completed demo to meet your requirements you can download here. More details about Listview reference the official sample.
For your list you should use an ObservableCollection.
Implement the INotifyPropertyChanged Interface so the UI is getting notified when data is changing.
Create a new class called ListCountToVisibilityConverter.cs, Implement the IValueConverter interface. When the count of your
Observable collection is > 0 return Visibility.Visible else return
Visibility.Collapsed .
You can delete Items by getting the selected index in the ListView and you can add items by using the add method of the observable collection.
You may want to implement the MVVM pattern. MVVM Wikipedia
Imagine your working on a UI for an Wedding Planner app. You'll have a list of guests and you want to display their name on the screen next to a combobox containing values of 'Will Attend' / 'Maybe' / 'No'.
I've tried to something just that....I have a collection of items inside a view model (the guests). For each of those items I want to display a Label and a ComboBox. Each ComboBox has the same values in the drop down (the possible responses).
I've created an ItemTemplate that contains a label and a combobox. I bind it to my collection of guests and it works as expected. I'm using ancestor binding so that the ComboBox's ItemsSource is bound to the list of possible responses. That works great.
What I'm struggling with is how to bind the SelectedItem to get the values the user selects? I want to have a collection of selected values on the ViewModel somehow, but I'm having a lot of trouble finding the correct words to describe this / search for it.
Can anyone help me? Am I going about this the wrong way?
You may create an enum for the attendance state and add an Attendance property to your Guest class:
public enum Attendance
{
Yes,
No,
Maybe
}
public class Guest
{
...
public Attendance Attendance { get; set; } // raise PropertyChanged event if necessary
}
Now you could set the Tag property of the ComboBox items to the appropriate enum value and bind the SelectedValue property:
<ItemsControl.ItemTemplate>
<DataTemplate>
<ComboBox SelectedValue="{Binding Attendance}" SelectedValuePath="Tag">
<TextBlock Tag="Yes">Will Attend</TextBlock>
<TextBlock Tag="No">Won't Attend</TextBlock>
<TextBlock Tag="Maybe">May Attend</TextBlock>
</ComboBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
Basically, I'm not sure how to use MVVM, and/or use commands correctly in my current situation. So, I have a View, containing a list box, and a panel of animation objects, that I created. These animation objects can be animated through a simple public method, Animate(). The goal here, is to associate this Animate() method with buttons inside the list box, like so:
As we can see in the diagram below, both the ListBox items and the visual elements inside of the animation area are associated with the same collection of models from the ViewModel, with the items in each being templated. For example, the ListBox items are simply defined to have some text related to a data item, and the AnimationObjects take on an appearance according to the data. These models, I feel, should not understand that an animation is occurring - they're simple data, and the animation does not change them.
Finally, I show in the below diagram, that I have created two FrameworkElement child types, one for holding animation objects, and another that defines these animation objects.
How can I connect this animation action to the buttons within the list box? It doesn't make sense to me that the models/viewmodels know about the animation, because it doesn't change the state of anything in my application - it's just for visual purposes. I've thought about using a RoutedCommand defined in AnimationObject, and having the buttons bind their command property accordingly, but I worry that will simply make every element animate at the same time.
It is also important for my sake, that I conform to MVVM, as these data will be used in many other situations, perhaps even a different version of this view.
Any advice would be appreciated.
What you can do is call a command in your ViewModel , i.e. the DataContext of your ListBox.
CS :
public class ViewModel
{
public ICommand AnimateObjectCommand { get; }
}
XAML :
<DataTemplate x:Key="AnimationObjectItemTemplate">
<Button Command="{Binding Path=DataContext.AnimateObjectCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}" />
</DataTemplate>
<ListBox ItemsSource="{Binding AnimationObjects}" ItemTemplate="{StaticResource AnimationObjectItemTemplate}"/>
your Command implementation should be one that accepts an argument which would be passed by the CommandParameter .
private ICommand _animateObjectCommand;
public ICommand AnimateObjectCommand
{
get
{
if (_animateObjectCommand == null)
{
_animateObjectCommand = new RelayCommand<AnimationObject>( ao => { ao.Animate(); });
}
return _animateObjectCommand;
}
}
The CommandParameter = {Binding} meaning this.DataContext where this is an Item in your ListBox and it's DataContext is an AnimationObject.
I have a WPF GUI setup like this currently:
The "Check for Third Party Updates" button will query the machine for outdated application installs and display the results, each update grouped in its own row/section with some text describing the update and a button allowing them to initiate the install.
I have a class built for third party updates that contains application name, version, installpath, message to display, etc. My question is largely how to implement the visual components. Every time the list of "apps to be updated" is iterated through and a member is found, a new row needs to be generated with common elements (button, text, picture,etc.). And I don't know how many rows might be generated, so I need to allow for the potential of scrolling down within the tab. Is a listbox control the way to go? How can I setup a visual template for the rows that are dynamically created to adhere to?
A ListBox would be a sensible approach. You would have to create a DataTemplate for the ListBoxItems and assign that to the ItemTemplate property of the ListBox, as described in Styling and Templating an ItemsControl. All the rest, like the ability to select items, or to scroll through the list, is of course done automatically by the ListBox control.
It might look like this:
<ListBox ItemsSource="{Binding ThirdPartyUpdates}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding AppIcon}" Margin="5"/>
<TextBlock Text="{Binding AppName}" Margin="5"/>
<TextBlock Text="{Binding AppVersion}" Margin="5"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The view model for the above ListBox would be something like this:
public class ThirdPartyUpdate
{
public string AppIcon { get; set; }
public string AppName { get; set; }
public string AppVersion { get; set; }
}
public class ViewModel
{
public ObservableCollection<ThirdPartyUpdate> ThirdPartyUpdates { get; set; }
}
You can use ItemsControl and bind it to a collection of your Class and use ItemsControl's template to bind your data to whichever control you want. Check out this Example
For each item in the collection you will have a row created. Surround the item control with a ScrollViewer. Set the VerticalScrollbar visibility to auto so that it will be visible only when required. And if you set a maximumheight to a value you feel right and set the height to auto. It will grow till the maximum height and the scroll bar will be visible if items are added beyond that.
This question deals with a dinky little Winforms GUI. Let it be known that I have basic knowledge of data bindings and INotifyPropertyChanged and use both in my ViewModels in WPF. But I don't know Winforms. This is for a school assignment.
So I have a class that has a DisplayName property. I also have a ListBox whose Items are a sequence of instances of my class. I have pointed myListBox.DisplayMember = "DisplayName"; After changing a value in an instance of my class that will cause the DisplayName property to return a different value, how do I tell my ListBox to pull the DisplayName property again to refresh its value?
I needed to do the same thing but with a combobox. The workaround I found is to clear and reset the DisplayMember property.
This worked:
myComboBox.DisplayMember = null;
myComboBox.DisplayMember = "DisplayName";
It's important to note that this is perhaps not the best solution as it will cause multiple SelectedValueChanged events but the end result is successful.
Doing it this way probably requires re-binding the listbox, loosing selectedIndex etc.
One workaround is to forget about the DisplayMember property and handle the Format event of the ListBox instead. Something like (from memory) :
// untested
e.Value = (e.Item as MyClass).DisplayValue;
I know this was ages ago but I had similar problem and could not find satisfying solution and finally solved with this single line at the end after updating the values:
bindingsource.EndEdit();
Items on listbox reflects any changes entered into textboxes after Update button clicked. So after lines like this:
textbox1.DataBindings["Text"].WriteValue();
textbox2.DataBindings["Text"].WriteValue();
just insert this line:
bindingsourcevariable.EndEdit();
Hope this helps others who also encounter similar problem but haven't found the right solution
Here is solution code that does everything in XAML as opposed to back end C#. This is how I do my projects utilizing MVVM (minimizing the back end code, and if possible having no back end code)
<ListBox x:Name="lstServers" HorizontalAlignment="Left" Height="285" Margin="20,37,0,0" VerticalAlignment="Top" Width="215"
ItemsSource="{Binding Settings.Servers}"
SelectedItem="{Binding Settings.ManageSelectedServer, Mode=TwoWay}"
DisplayMemberPath="UserFriendlyName"/>
This is a listbox on the Window. The keys to point out here, which can be very tricky, are the usual ItemsSource property being set to a Settings object on my view model, which has a Servers Observable collection.
Servers is a class that has a property called UserFriendlyName.
public sealed class AutoSyncServer : ObservableModel
{
public AutoSyncServer()
{
Port = "80";
UserFriendlyName = "AutoSync Server";
Server = "localhost";
}
private string _userFriendlyName;
public string UserFriendlyName
{
get { return _userFriendlyName;}
set
{
_userFriendlyName = value;
OnPropertyChanged("UserFriendlyName");
}
}
This is a partial code snippet for you of the class itself.
The SelectedItem of the ListBox is bound to an instance of the Selected object that I store in the model view called ManageSelectedServer.
The tricky part here is the DisplayMemberPath is set to "UserFriendlyName" as opposed to "{Binding UserFriendlyName}". This is key
If you use {Binding UserFriendlyName} it will display the UserFriendlyNames in the collection but will not reflect any changes to that property.
The XAML for the TextBox where the user can update the user friendly name (which should change the text in the listbox also) is:
<TextBox x:Name="txtDisplayName" HorizontalAlignment="Left" Height="23" Margin="395,40,0,0" TextWrapping="Wrap"
Text="{Binding ElementName=lstServers,Path=SelectedItem.UserFriendlyName, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Top" Width="240"/>
This sets the Text property of the TextBox and binds it to the ListBox element lstServers SelectedItem property UserFriendlyName. I've also included an UpdateSourceTrigger=PropertyChanged so that any changes made to the text source notify that they have been changed.
XAML is tricky!