I have a listbox with the following xaml which filled from a XMLReader
<ListBox Name="listBox4" Height="498" SelectionChanged="listBox4_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Epon}" FontSize="32"/>
<TextBlock Text="{Binding Telnum}" FontSize="24" />
<TextBlock Text="{Binding Beruf}" FontSize="16" />
<TextBlock Text="{Binding Odos}" FontSize="16"/>
<TextBlock Text="{Binding Location}" FontSize="16"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I want to call the phone when i'll select the lisbox item so I created the following class
public class PhoneList
{
public string Epon { get; set; }
public string Telnum { get; set; }
public string Beruf { get; set; }
public string Odos { get; set; }
public string Location { get; set; }
public PhoneList(string Telnum, string Epon, string Beruf, string Odos, string Location)
{
this.Telnum = Telnum;
this.Epon = Epon;
this.Beruf = Beruf;
this.Odos = Odos;
this.Location = Location;
}
}
On the event of the selection which is below
private void listBox4_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
PhoneList nPhone = (PhoneList)listBox4.SelectedItem;
string mPhoneCopy = nPhone.Telnum;
string mNameCopy = nPhone.Epon;
var pt = new PhoneCallTask();
pt.DisplayName = mNameCopy;
pt.PhoneNumber = mPhoneCopy;
pt.Show();
}
I get the error InvalidCastException in the first line of the event.
What is causing this error?
From the XAML posted, there is not a collection bound to the ListBox. This either means there is no binding, or the binding is being set in the code behind. The following are merely shots in the dark since no additional code has been posted:
Properly Bind the ListBox
Assuming a collection is part of the DataContext, the collection will need to be bound to the ListBox:
<ListBox ItemsSource="{Binding Path=MyCollection}"... />
A starting resource: MSDN: How to: Bind to a Collection and Display Information Based on Selection
Verify the Object Before Casting
This may be the case where the item selected is empty, i.e., the first item in the list has no value. In that case, check to see if the object is the type you are expecting before doing anything else:
private void listBox4_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var nPhone = listBox4.SelectedItem as PhoneList;
if (nPhone == null)
{
return;
}
string mPhoneCopy = nPhone.Telnum;
string mNameCopy = nPhone.Epon;
var pt = new PhoneCallTask();
pt.DisplayName = mNameCopy;
pt.PhoneNumber = mPhoneCopy;
pt.Show();
}
Other Thoughts
I suspect that there may not be a collection bound to the ListBox`; perhaps there is supposed to be some code-behind to set the binding that isn't being executed?
In the end, if none of the above applies to your case, edit the post with the relevant code that is creating the collection and setting the collection as the ItemsSource of the ListBox.
Related
Problem: When location is changed via ComboBox cb1 the related location TextBlock does not change to updated value.
I am self learning and below is experiment code on binding that has
public EmpDeptViewModel vm; its initialize on button click event as below
private void btn2_Click(object sender, RoutedEventArgs e) {
vm = new EmpDeptViewModel();
this.Bindings.Update(); }
The XAML looks like this.
<ListView x:Name="listview3" ItemsSource="{x:Bind vm.InstanceOfDepartmentData}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="classes:Department">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="5">
<Run Text="DeptNo: " /><Run Text="{x:Bind DeptNo}" />
</TextBlock>
<TextBlock Margin="5">
<Run Text="DeptName: " /><Run Text="{x:Bind DeptName}" />
</TextBlock>
<TextBlock Margin="5">
<Run Text="Location: " /><Run Text="{x:Bind Location, Mode=OneWay}" />
</TextBlock>
<ComboBox x:Name="cb1" ItemsSource="{Binding Source={StaticResource MyLocatonList}, Path=ListofLocationsInsideViewModel, Mode=TwoWay}" DisplayMemberPath="LocationName" SelectedValuePath="LocationName" SelectedValue="{x:Bind Location}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Problem: When location is changed via ComboBox cb1 the related location TextBlock does not change to updated value.
The x:DataType="classes:Department" looks like this.
public class Department : BindableBase
{ private string _location;
public Department(int pdeptNo, string pdeptName, string plocation)
{
DeptNo = pdeptNo;
DeptName = pdeptName;
Location = plocation;
ListOfDeparmentEmployees = new List<Employee>(); }
public int DeptNo { get; set; }
public string DeptName { get; set; }
public string Location {
get { return this._location; }
set { this.SetProperty(ref this._location, value); }
}
public List<Employee> ListOfDeparmentEmployees { get; set; }
}
You may be bound in the wrong position
In your ComboBox, you set TwoWay to the ItemsSource. This does not make sense. You cannot change the Location if you modify the value of the ComboBox.
Try this:
Xaml
...
<ComboBox x:Name="cb1" ItemsSource="{Binding Source={StaticResource MyLocatonList}, Path=ListofLocationsInsideViewModel}"
DisplayMemberPath="LocationName" SelectedValuePath="LocationName" SelectedValue="{x:Bind Location,Mode=TwoWay}" />
...
However, if you write it directly, it will cause an endless loop and then report an error. You need to rewrite the Location property of the Department class.
Department.cs
...
public string Location
{
get { return this._location; }
set
{
if (_location != value)
{
this.SetProperty(ref this._location, value);
}
}
}
...
In addition, please note whether your BindableBase base class implements the INotifyPropertyChanged interface, which is the basis for modifying the UI while modifying the data.
Best regards.
I have a problem, I have a list View lets call it ListView1, that is populated dynamically from local storage after a sync from Azure.
Now I am having a problem selecting and getting information from the item click.
I need to get four different types of information and pass it to a flyout when there is an item click on the listview.
//This is the way the List is Created
string DB_PATH = Path.Combine(ApplicationData.Current.LocalFolder.Path, "my.db");
public async void CreateList ()
{
await AzureWebService.Instance.InitLocalStoreAsync(DB_PATH);
var t = await AzureWebService.Instance.GetListItems(); //GET THE OG LIST
var list = new ObservableCollection<winMerchant>(); //create a new list of Names, (with the BitmapImage Field )
foreach (var f in t)
{
var m = new winDirectory(); //give the customized merchant object, the same VALUES as the ACTUAL mercahnt object
m.MyName1 = f.MyName1;
m.MyName2 = f.MyName2;
m.MyNumber = f.MyNumber; //int
m.MyPic = ImageHelper.Base64StringToBitmap(f.MyPic); //CUSTOM PART ==> Take the newly defined BitmapImage (wMyPic) and convert the Pic string into it
list.Add(m); //add the new People into a list
}
MyList.ItemsSource = list;
}
And this is the xaml
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Stretch">
<Image Source="{Binding wPic}" Width="75" Height="75" />
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
<TextBlock Text="{Binding wMyName1}" />
<TextBlock Text="{Binding wMyName2}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
Helper Class
class winMerchant: Contacts
{
public BitmapImage wPic { get; set; }
public int wMyNumber { get; set; }
public string wMyName1 { get; set; }
public string wMyName2 { get; set; }
}
The Number should be passed to a flyout on click...
Any help will be much appreciated!
You can just get the selected item's number from item click event of ListView.
private void MainListView_ItemClick(object sender, ItemClickEventArgs e)
{
var item = e.ClickedItem as winMerchant;
int number = item.wMyNumber;
}
Also make IsItemClickEnabled="True" of your Listview.
Try to add
AutomationProperties.Name="{Binding wMyNumber}" to the <Image>
With this you can pass the number of the clicked object, in the c# (inside the click event) you can just create a List<> or an Array with data about your objects (so you can use this collection of object to display it in the flyout)
On selection of item in AutoSuggestBox instead of the property value it binds to the property.
This is my xaml.
<AutoSuggestBox x:Name="txtSearchBox" ItemsSource="{Binding}"
PlaceholderText="Search in Distributor" Style="{StaticResource AutoSuggestBoxStyle1}"
Margin="10,25,10,0" DisplayMemberPath="{Binding entityName}" TextMemberPath="{Binding entityName}"
BorderBrush="#000000" BorderThickness="2" TextChanged="txtSearchBox_TextChanged"
SuggestionChosen="txtSearchBox_SuggestionChosen">
<AutoSuggestBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding entityName}"
Tag="{Binding entityId}"/>
</DataTemplate>
</AutoSuggestBox.ItemTemplate>
</AutoSuggestBox>
This is the Code Behind
private void txtSearchBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
List<Master_User_Hierarchy_VM> lstUserHierarchy = new List<Master_User_Hierarchy_VM>();
txtSearchBox.ItemsSource = null;
foreach (Master_User_Hierarchy_VM obj in lstMaster_UserHierarchy_VM)
{
if (sender.Text != "")
{
if (obj.entityName.Contains(sender.Text))
{
lstUserHierarchy.Add(obj);
}
}
}
txtSearchBox.ItemsSource = lstUserHierarchy;
}
else if (args.Reason == AutoSuggestionBoxTextChangeReason.SuggestionChosen)
{
//txtSearchBox.Text = txtSearchBox.Items[0].ToString();
}
}
private void txtSearchBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
{
txtSearchBox.Text = ((Master_User_Hierarchy_VM)args.SelectedItem).entityName;
}
This is when I enter a character
When I click an Item in this list
Again I get the selected Item in the suggestions box. When I click it I get the property Name instead of the value
Use
TextMemberPath="entityName"
instead of
TextMemberPath="{Binding entityName}"
The solution is that I used UpdateTextOnSelect="False" & did explicit binding in Code behind.
I did resolve this by following the UWP/Xaml Samples. Here is my implementation.
Model:
public class Partner : EntityBase
{ //Name
public string Name { get; set; }
//Address
public string Street { get; set; }
...
//It is important to return the formated string
public string DisplayName { get { return string.Format("{0}", Name); } }
}
XAML:
SuggestionChosen event is not needed to implement.
<AutoSuggestBox PlaceholderText="Search"
DisplayMemberPath="DisplayName"
TextMemberPath="DisplayName"
QueryIcon="Find"
Width="200"
TextChanged="{x:Bind ViewModel.SupplierAutoSuggest_TextChanged}"
>
<!--If you are using a DataTemplate you need to delete the DisplayMemberPath property, if not you will get an unhandled exception-->
<!--AutoSuggestBox.ItemTemplate>
<DataTemplate x:DataType="models:Partner">
<TextBlock
Text="{Binding Name, Mode=OneWay}"/>
</DataTemplate>
</!-->
</AutoSuggestBox>
TextChanged Event Method (Implemented in the corresponding viewModel Class):
public async void SupplierAutoSuggest_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
var queryText = sender.Text;
if(args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
if(!string.IsNullOrEmpty(queryText))
{
//IEnumerable Returned
var suggestion = await App.Repository.Partners.GetAsync(queryText);
if (suggestion.Any())
sender.ItemsSource = suggestion;
}
}
}
Notice that you can use x:bind for MVVM pattern porpouse
Set DisplayMemberPath to `DisplayMemberPath="entityName", no Binding needed.
I'm new to Windows Phone development and I'm currently facing an issue while using the LongListSelector in WP8, and I don't know how to proceed to achieve the result I want.
I use it to display a list of items as usual. The class used contains 5 items, and one of them is a float value. I want to display, in the list header, the sum of all positive float values contained in the list, but I have no idea whatsoever about how to do this.
I tried to bind another variable (result of the sum) specificly to the listheader in addition to the original binding, or to add another item in the class containing the sum result (hence repeated throughout the list in each list item), but it didn't work.
I guess this is a pretty basic fonctionnality (for instance to count and display the number of elements of the list), but I can't figure out how to do this.
EDIT : I thought showing my code wouldn't help, but here it is. (I took away the formatting that wasn't relevant)
XAML
<phone:LongListSelector x:Name="ListeSolde" LayoutMode="List">
<phone:LongListSelector.ListHeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding SommeTotale}" />
</DataTemplate>
</phone:LongListSelector.ListHeaderTemplate>
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Nom}" />
<TextBlock Text="{Binding DerniereConnexion}" />
<TextBlock Text="{Binding Depuis}" />
<TextBlock Text="{Binding Solde}" />
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
Class definition
public class resume
{
public string Nom { get; set; }
public double Solde { get; set; }
public string Depuis { get; set; }
public string DerniereConnexion { get; set; }
public resume(string nom, double solde, string depuis, string derniereconnexion)
{
this.Nom = nom;
this.Solde = solde;
this.Depuis = depuis;
this.DerniereConnexion = derniereconnexion;
}
}
public class total
{
public double Total { get; set; }
public double calculTotal(List<resume> soldes)
{
double total = new double();
foreach (resume solde in soldes)
{
if (solde.Solde > 0)
total += solde.Solde;
}
return total;
}
public total(double Dtotal)
{
this.Total = Dtotal;
}
}
And code behind
public MainPage()
{
InitializeComponent();
List<resume> soldes = new List<resume>();
Donnees MainData = new Donnees();
soldes = MainData.RefreshResume(soldes); // A method that basically add items to the list
total SommeTotale = new total(1);
SommeTotale.Total = SommeTotale.calculTotal(soldes);
ListeSolde.ItemsSource = soldes;
}
This of course doesn't work (as far as the list header is concerned) and this is how I would do it
This is how I managed to bind other data from my ViewModel to the header of the LongListSelector.ListHeader.
<phone:LongListSelector.ListHeaderTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="2" Foreground="DarkViolet"
Text="{Binding ElementName=LayoutRoot, Path=DataContext.AddressBookList.Count}" />
</Grid>
</DataTemplate>
</phone:LongListSelector.ListHeaderTemplate>
The ListHeader property binds directly to the DataContext of the LongListSelector. This is different that the items contained within the LongListSelector. The items contained within it are bound to each item within the ItemSource. One of the best ways to get the ListHeader to display is to create an object that houses the data for the LongListSelector
public class ResumeContainer
{
public double SommeTotale { get { return Resumes.Sum(r => r.Value); } }
public IEnumerable<Resume> Resumes { get; set; }
}
You would set the DataContext of the LongListSelector to be an instance of the ResumeContainer. This would preferably be a property of your ViewModel. You would need to change your xaml to be
<phone:LongListSelector x:Name="ListeSolde" ItemsSource="{Binding Resumes}">
Your code-behind then changes to
List<resume> soldes = new List<resume>();
Donnees MainData = new Donnees();
soldes = MainData.RefreshResume(soldes);
ListeSolde.DataContext = new ResumeContainer { Resumes = soldes };
I'll try my best to explain this without speaking too much about the specific content and purpose of the app. I'll just say on the main page there are 3 empty "slots" for items you can select to occupy them. When you click on one, it takes you to a separate page that let's you select a specific item for that slot. Here is the code to better explain:
<TextBlock Text="{Binding FirstSelectionName}" />
<TextBlock Text="{Binding FirstSelectionType}" />
<HyperlinkButton Content="Choose First Option"
Name="firstHyperLink"
NavigateUri="/Pages/FirstChoices.xaml"
/>
<TextBlock Text="{Binding SecondSelectionName}" />
<TextBlock Text="{Binding SecondSelectionType}" />
<HyperlinkButton Content="Choose Second Option"
Name="secondHyperLink"
NavigateUri="/Pages/SecondChoices.xaml"
/>
<TextBlock Text="{Binding ThirdSelectionName}" />
<TextBlock Text="{Binding ThirdSelectionType}" />
<HyperlinkButton Content="Choose Third Option"
Name="thirdHyperLink"
NavigateUri="/Pages/ThirdChoices.xaml"
/>
Here is the code behind for this XAML page:
public class FirstSelection
{
public string FirstSelectionName { get; set; }
public string FirstSelectionType { get; set; }
}
public class SecondSelection
{
public string SecondSelectionName { get; set; }
public string SecondSelectionType { get; set; }
}
public class ThirdSelection
{
public string ThirdSelectionName { get; set; }
public string ThirdSelectionType { get; set; }
}
On the selection pages an XML file is looped through and saved into new instances of a class. When the user selects a certain option from that list with a button press, I want to set the corresponding slot on the main page equal to that selection. Here is an example of the first slots selection page:
<ListBox Name="firstOptionsList">
<ListBox.ItemTemplate>
<DataTemplate>
<Button BorderThickness="3"
Click="setSelectedToFirst">
<StackPanel Orientation="Vertical">
<TextBlock Name="nameTextBlock"
Text="{Binding Name}"
/>
<TextBlock Name="typeTextBlock"
Text="{Binding Type}"
/>
</StackPanel>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And finally the code behind for the selectin page:
public FirstOptionsPage()
{
InitializeComponent();
Dispatcher.BeginInvoke((Action)(() => firstList.ItemsSource = firstdata));
WebClient firstWebClient = new WebClient();
firstWebClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(first_DownloadStringCompleted);
firstWebClient.DownloadStringAsync(new Uri("http://www.website.com/firstoptions.xml"));
}
void first_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null)
return;
XElement xmlitem = XElement.Parse(e.Result);
var firstdata = new List<FirstOptionsClass>();
foreach (XElement item in xmlitem.Elements("entry"))
{
var name = item.Element("name");
var namevalue = (name == null) ? null : name.Value;
var type = item.Element("type");
var typevalue = (type == null) ? null : type.Value;
firstdata.Add
(new FirstOptionsClass
{
Name = namevalue,
Type = typevalue,
}
);
}
firstList.ItemsSource = firstdata;
}
public class FirstOptionsClass
{
public string Name { get; set; }
public string Type { get; set; }
}
public System.Collections.IEnumerable firstdata { get; set; }
private void setSelectedToFirst(object sender, RoutedEventArgs e)
{
//THIS IS THE PART WHERE I'M NOT SURE HOW TO SET FirstOptionsClass Name = FirstSelectionName on the MainPage.xaml
}
See the comment line in the click event for my main problem. How to I set these values equal to eachother across these pages? I know it may look like a mess here so I appreciate the help.
TBH I'm not sure what you want to do. What fires setSelectedToFirst?
Anyways, to save some data and read it during app's lifycycle you can use IsolatedStorageSettings.