I need to show the WCF service Return Value(LIST) in Silverlight Listbox.
create GetAllAgents Class like,
public class GetAllAgents
{
public List<string> FirstName { get; set; }
public GetAllAgents(List<string> firstName)
{
FirstName = firstName;
}
}
The Following Method used for Consume the WCF Service
public partial class AgentQueue : UserControl
{
ChatACDService.ChatACDServiceClient ChatAcdClient = new ChatACDService.ChatACDServiceClient();
public ObservableCollection<GetAllAgents> _GetAllAgents = new ObservableCollection<GetAllAgents>();
public AgentQueue()
{
InitializeComponent();
LoadAgentList();
this.AllList.DataContext = _GetAllAgents;
}
private void LoadAgentList()
{
ChatAcdClient.GetAllAgentListCompleted += new EventHandler<GetAllAgentListCompletedEventArgs>(ChatAcdClient_GetAllAgentListCompleted);
ChatAcdClient.GetAllAgentListAsync();
}
void ChatAcdClient_GetAllAgentListCompleted(object sender, GetAllAgentListCompletedEventArgs e)
{
if (e.Error != null)
{
}
else
{
// AllAgents.ItemsSource = e.Result;
_GetAllAgents.Add(new GetAllAgents(e.Result.ToList()));
}
}
I use the following code For create List Box in XAML page
<ListBox x:Name="AllList" ItemsSource="{Binding}"
DisplayMemberPath="FirstName"
Margin="403,54,0,35" HorizontalAlignment="Left" Width="101" />
But The Output like ,
I need to show the WCF method's result(return type is list) in Listbox by using ObservableCollection.What are the changes are need to make in above Program?
Actually it works pretty well:
You ask to display the Member Path "FirstName" of your object GetAllAgents.
But the Member Path "FirstName" is a list of string.
So your XAML display what you expect from it: the toString() conversion of your memberPath.
And the default toString of your member path which is FirstName which is a list of string is: System.Collection.Generic.List[System.String]
I guess what you expect is that your list of first name should be the item source of your ListBox.
So if your only need is to display their firstName, just replace
public ObservableCollection<GetAllAgents> _GetAllAgents = new ObservableCollection<GetAllAgents>();
By
public ObservableCollection<string> _GetAllAgents = new ObservableCollection<string>();
and
_GetAllAgents.Add(new GetAllAgents(e.Result.ToList()));
By
foreach (var agentName in e.Result.ToList())
{
_GetAllAgents.Add(agentName);
}
And it will display the name of your agent.
If you need mor than that, you will need to create a viewModel per agent object and a dataTemplate to let know Silverlight how you want it to be display.
Hope it helps.
Related
So let me preface by saying that I am very new to WPF and MVVM.
I am using the mvvm design pattern for my application. My goal, is that I need to have two combo boxes loaded with content to select from( in this case, units to convert from and to). The content of these combo boxes is determined by a third combo box which determines the type of units to load.
So for example, the first combo box would let the user select a unit type, such as speed or temperature. So if I select temperature, the other two combo boxes would be loaded with a list of temperature units. Likewise if I select speed, then the list in the other two combo boxes would be replaced with units for speed.
I already have a class that handles the from and to conversion. But I'm a little lost with how to start working with these combo boxes. I have only done some basic things with combo boxes like loading content straight in the xaml. I have seen people make lists and somehow bind them but some it was a little overwhelming.
All I need is a good example and explanation to get me started. Would greatly appreciate it.
Everything you need is a ViewModel class to work with the binding.
Each combo box will binding the ItemSources to a Property in the ViewModel. Everytime the selected of the first combo box is change, you will update the data source of the second combo box.
Here is example of the ViewModel class:
namespace WpfApp1
{
class SampleVM : ViewModelBase
{
private ObservableCollection<UnitEntry> _comboBox1ItemSource;
private ObservableCollection<TypeEntry> _comboBoxTypeItemSource;
private int _selectedTypeIndex;
public ObservableCollection<UnitEntry> ComboBoxUnitItemSource
{
get => _comboBox1ItemSource;
set
{
_comboBox1ItemSource = value;
RaisePropertyChange(nameof(ComboBoxUnitItemSource));
}
}
public ObservableCollection<TypeEntry> ComboBoxTypeItemSource
{
get => _comboBoxTypeItemSource;
set
{
_comboBoxTypeItemSource = value;
RaisePropertyChange(nameof(ComboBoxTypeItemSource));
}
}
public int SelectedTypeIndex
{
get => _selectedTypeIndex;
set
{
_selectedTypeIndex = value;
RaisePropertyChange(nameof(SelectedTypeIndex));
//Here where we will handle the data in the second combo box depend on the Type value when it changed
if(value == 0)
{
ComboBoxUnitItemSource = GetDataUnitType1();
}
else
{
ComboBoxUnitItemSource = GetDataUnitType2();
}
}
}
public SampleVM()
{
InitData();
}
private void InitData()
{
//Init Type data
ComboBoxTypeItemSource = new ObservableCollection<TypeEntry>();
TypeEntry type1 = new TypeEntry(0, "Type 1");
TypeEntry type2 = new TypeEntry(1, "Type 2");
ComboBoxTypeItemSource.Add(type1);
ComboBoxTypeItemSource.Add(type2);
//Selected Index set to default by 0
SelectedTypeIndex = 0;
}
private ObservableCollection<UnitEntry> GetDataUnitType1()
{
//Get your real data instead of fake data below
ObservableCollection<UnitEntry> data = new ObservableCollection<UnitEntry>();
for (int i = 0; i < 5; i++)
{
UnitEntry unitEntry = new UnitEntry(i, $"Type 1 - Entry: {i}");
data.Add(unitEntry);
}
return data;
}
private ObservableCollection<UnitEntry> GetDataUnitType2()
{
//Get your real data instead of fake data below
ObservableCollection<UnitEntry> data = new ObservableCollection<UnitEntry>();
for (int i = 0; i < 5; i++)
{
UnitEntry unitEntry = new UnitEntry(i, $"Type 2 - Entry: {i}");
data.Add(unitEntry);
}
return data;
}
}
public class TypeEntry
{
public int ID { get; set; }
public string Name { get; set; }
public TypeEntry(int id, string name)
{
ID = id;
Name = name;
}
}
public class UnitEntry
{
public int ID { get; set; }
public string Name { get; set; }
public UnitEntry(int id, string name)
{
ID = id;
Name = name;
}
}
}
And here is the xaml class looks like:
<!-- The "Name" value is the Name property in the Entry class-->
<ComboBox Grid.Row="0"
Grid.Column="0"
Width="200"
Height="30"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedIndex="{Binding SelectedTypeIndex}"
ItemsSource="{Binding ComboBoxTypeItemSource}"/>
<ComboBox Grid.Row="0"
Grid.Column="1"
Width="200"
Height="30"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedIndex="0"
ItemsSource="{Binding ComboBoxUnitItemSource}"/>
Finally, important part, you need to assign the ViewModel to the View class:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new SampleVM();
}
}
Am I missing something or is there more to it that I am not getting? I'm working on a mobile app and have to use pickers for choices from a data table. To start, I have many such pickers that are key/value based. I have an internal ID and a corresponding Show value. The IDs do not always have 1, 2, 3 values such as originating from a lookup table and may have things as
KeyID / ShowValue
27 = Another Thing
55 = Many More
12 = Some Item
Retrieved as simple as
select * from LookupTable where Category = 'demo'
So I have this class below that is used for binding the picker via a list of records
public class CboIntKeyValue
{
public int KeyID { get; set; } = 0;
public string ShowValue { get; set; } = "";
}
Now, the data record that I am trying to bind to has only the ID column associated to the lookup. Without getting buried into XAML, but in general, I have my ViewModel. On that I have an instance of my data record that has the ID column.
public class MyViewModel : BindableObject
{
public MyViewModel()
{
// Sample to pre-load list of records from data server of KVP
PickerChoices = GetDataFromServerForDemo( "select * from LookupTable where Category = 'demo'" );
ShowThisRecord = new MyDataRec();
// for grins, I am setting the value that SHOULD be defaulted
// in picker. In this case, ID = 12 = "Some Item" from above
ShowThisRecord.MyID = 12;
}
// this is the record that has the "ID" column I am trying to bind to
public MyDataRec ShowThisRecord {get; set;}
// The picker is bound to this list of possible choices
public List<CboIntKeyValue> PickerChoices {get; set;}
}
I can’t bind to the index of the list, because that would give me 0, 1, 2, when I would be expecting the corresponding "ID" to be the basis of proper record within the list.
In WPF, I have in the past, been able to declare the show value for the screen, but also the bind value to the ID column in similar. So, the binding of the INT property on my "ShowThisRecord" would drive and properly refresh.
I can see the binding of SelectedItem, but that is the whole item of the KVP class which is not part of the MyDataRec. Only the ID is the common element between them.
What is the proper bindings to get this to work?
<Picker ItemDisplayBinding="{Binding ShowValue}"
SelectedItem="{Binding ???}" />
Just to confirm my record bindings are legitimate, my page has binding context to MyViewModel as I can properly see the ID via a sample text entry I added to the page via.
<Entry Text="{Binding Path=ShowThisRecord.MyID}"/>
I created a demo to test your code, and it works properly. The full demo is here. I also added a function to verify the selected item.
If you want to get the SelectedItem object synchronously, the MyViewModel should implement INotifyPropertyChanged, and I created a selectedRecordfield for SelectedItem, so you can do like this:
public class MyViewModel : ViewModelBase
{
public MyViewModel()
{
// Sample to pre-load list of records from data server of KVP
//PickerChoices = GetDataFromServerForDemo("select * from LookupTable where Category = 'demo'");
PickerChoices = new ObservableCollection<TestModel>() {
new TestModel{MyID = 5, ShowValue="test1"}, new TestModel{MyID = 9, ShowValue="test2"},
new TestModel{MyID = 18, ShowValue="test18"}, new TestModel{MyID = 34, ShowValue="test4"}
};
// Set the default selected item
// foreach (TestModel model in PickerChoices) {
// if (model.MyID == 18) { // Default value
// SelectedRecord = model;
// break;
// }
// }
ShowThisRecord = new TestModel();
// For grins, I am setting the value that SHOULD be defaulted
// in picker. In this case, ID = 12 = "Some Item" from above
ShowThisRecord.MyID = 12;
}
// This is the record that has the "ID" column I am trying to bind to
public TestModel ShowThisRecord { get; set; }
//*****************************************
TestModel selectedRecord; // Selected item object
public TestModel SelectedRecord
{
get { return selectedRecord; }
set
{
if (selectedRecord != value)
{
selectedRecord = value;
OnPropertyChanged();
}
}
}
//*****************************************
// The picker is bound to this list of possible choices
public ObservableCollection<TestModel> PickerChoices { get; set; }
}
class ViewModelBase
public class ViewModelBase: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And the XAML content:
<Picker Title="Select a value" x:Name="mypicker"
ItemsSource="{Binding Path= PickerChoices}"
SelectedItem="{Binding SelectedRecord}"
ItemDisplayBinding="{Binding MyID}"/>
File xaml.cs:
public partial class MainPage : ContentPage
{
ObservableCollection<TestModel> items = new ObservableCollection<TestModel>();
MyViewModel testModel = null;
public MainPage()
{
InitializeComponent();
testModel = new MyViewModel();
BindingContext = testModel;
// This will also work
//if (testModel!=null && testModel.PickerChoices!=null) {
// for (int index=0; index< testModel.PickerChoices.Count; index++) {
// TestModel temp = testModel.PickerChoices[index];
// if (18 == temp.MyID) {
// mypicker.SelectedIndex = index;
// break;
// }
// }
//}
foreach (TestModel model in testModel.PickerChoices)
{
if (model.MyID == 18)
{ // Default value
testModel.SelectedRecord = model;
break;
}
}
}
// To show the selected item
private void Button_Clicked(object sender, EventArgs e)
{
if (testModel.SelectedRecord!=null) {
DisplayAlert("Alert", "selected Item MyID: " + testModel.SelectedRecord.MyID + "<--> ShowValue: " + testModel.SelectedRecord.ShowValue, "OK");
}
}
}
The result is:
You need to set the ItemsSource property to your list of CboIntValue items:
<Picker Title="Select a value"
ItemsSource="{Binding PickerChoices}"
ItemDisplayBinding="{Binding ShowValue}" />
After much work, I ended up writing my own separate class and template style for what I needed. Due to the length of it, and posting the source code for anyone to use, review, assess, whatever, I posted that out on The Code Project.
Again, the primary issue I had is if I have an integer key ID coming from a data source, the picker would not automatically refresh itself by just the given ID (int or string).
Basic question from a novice. I've been stuck on this and have read through a lot of material and several similar questions on SO; hopefully not a completely duplicate question. I simplified the code as much as I know how to.
I'm trying to make the ListView show a filtered ObservableCollection) property (as the ItemsSource?), based on the selection in the ComboBox.
Specifically, which "meetings" have this "coordinator" related to it.
I'm not seeing any data errors in the output while it's running and debugging shows the properties updating correctly, but the ListView stays blank. I'm trying to avoid any code-behind on the View, there is none currently.
Thanks!
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<Meeting> meetings;
public ObservableCollection<Meeting> Meetings
{
get
{
return meetings;
}
set
{
meetings = value;
OnPropertyChanged("ListProperty");
OnPropertyChanged("Meetings");
}
}
private string coordinatorSelected;
public string CoordinatorSelected
{
get
{
return coordinatorSelected;
}
set
{
coordinatorSelected = value;
Meetings = fakeDB.Where(v => v.CoordinatorName == CoordinatorSelected) as ObservableCollection<Meeting>;
}
}
private ObservableCollection<string> comboProperty = new ObservableCollection<string> { "Joe", "Helen", "Sven" };
public ObservableCollection<string> ComboProperty
{
get
{
return comboProperty;
}
}
private ObservableCollection<Meeting> fakeDB = new ObservableCollection<Meeting>() { new Meeting("Joe", "Atlas"), new Meeting("Sven", "Contoso"), new Meeting("Helen", "Acme") };
public ObservableCollection<Meeting> ListProperty
{
get
{
return Meetings;
}
}
public class Meeting
{
public string CoordinatorName { get; set; }
public string ClientName { get; set; }
public Meeting(string coordinatorName, string clientName)
{
CoordinatorName = coordinatorName;
ClientName = clientName;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<Window.Resources>
<local:ViewModel x:Key="VM"></local:ViewModel>
</Window.Resources>
<DockPanel DataContext="{StaticResource ResourceKey=VM}">
<ComboBox Margin="10" ItemsSource="{Binding ComboProperty}" SelectedItem="{Binding CoordinatorSelected}" DockPanel.Dock="Top"/>
<ListView Margin="10" ItemsSource="{Binding ListProperty, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="ClientName"/>
</DockPanel>
Update:
This seems to show that the lambda is returning a Meeting object but the assignment to Meetings is failing. Is this an error in casting maybe?
Thanks again.
You always have to change a property's backing field before you fire a PropertyChanged event. Otherwise a consumer of the event would still get the old value when it reads the property.
Change the Meetings property setter like this:
public ObservableCollection<Meeting> Meetings
{
get
{
return meetings;
}
set
{
meetings = value;
OnPropertyChanged("ListProperty");
OnPropertyChanged("Meetings");
}
}
I believe I've found two solutions to the same problem. The error pointed out #Clemens was also part of the solution. The Meetings property problem is solved if I change ListProperty and Meetings to IEnumerable. Alternatively this approach without changing the type, which I believe invokes the collection's constructor with the filtered sequence as an argument.
set
{
coordinatorSelected = value;
var filteredList = fakeDB.Where(v => v.CoordinatorName == coordinatorSelected);
Meetings = new ObservableCollection<Meeting>(filteredList);
OnPropertyChanged("ListProperty");
}
I have a C# WPF application that has a form with two fields. Every time the form is submitted, I want to get the values and use the Instructor class to add the new item to a list. Then, I want to loop through the list and display the items in the ListView element. I realize I can do this without the class, but having the class is a requirement for my school assignment.
Here is my Main Window class:
public partial class MainWindow : Window
{
private List<Instructor> instList;
public MainWindow()
{
InitializeComponent();
List<Instructor> instList = new List<Instructor> { };
}
private void btnCreateInstructor_Click(object sender, RoutedEventArgs e)
{
spCreateInstructor.Visibility = (spCreateInstructor.Visibility == Visibility.Hidden) ? Visibility.Visible : Visibility.Hidden;
}
private void btnInstructorSubmit_Click(object sender, RoutedEventArgs e)
{
instList.Add(new Instructor { firstName = txtInstructorFirstName.Text, lastName = txtInstructorLastName.Text });
foreach (var inst in instList)
{
lvInstructorList.Items.Add("{0} {1}", inst.firstName, inst.lastName);
//Error occurs on the line above.
}
}
}
This is the instructor class:
class Instructor
{
public string firstName { set; get; }
public string lastName { set; get; }
}
My problem is that I get an error saying No overload for method Add takes 3 arguments What am I doing wrong? I have indicated where the error occurs with a comment in the code.
Try replacing this line:
lvInstructorList.Items.Add("{0} {1}", inst.firstName, inst.lastName);
with this one
lvInstructorList.Items.Add(new Instructor { firstName = inst.firstName, lastName = inst.lastName });
It is similar to how you added to the instList.
Edit
After realizing lvInstructorList is a ListView xaml element, this should work:
var listViewItem = new ListViewItem(new Instructor { firstName = inst.firstName, lastName = inst.lastName });
lvInstructorList.Items.Add(listViewItem);
ah here is how to create a listview and add items to it in wpf
check this question Add Items to Columns in a WPF ListView
In short
list view takes an object so you need to add items like that
lvInstructorList.Items.Add(lvInstructorList.Items.Add(new Instructor { firstName = inst.firstName, lastName = inst.lastName });
and you should bind to it in xaml
<ListView x:Name="lvInstructorList">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding firstName }"/>
</GridView>
</ListView.View>
Not sure why you want to loop again and again to the lvInstructorList. But you have to clear every time.
instList.Add(new Instructor { firstName = txtInstructorFirstName.Text, lastName = txtInstructorLastName.Text });
lvInstructorList.Clear();
foreach (var inst in instList)
{
lvInstructorList.Items.Add(inst);
}
It looks to me like you are not intending on binding the list view to an object, but rather just insert string values into the list view. If that is the case:
lvInstructorList.Items.Add(string.Format("{0} {1}", inst.firstName, inst.lastName));
I am trying to make an enroll/withdraw student into course project however I am not entirely sure how to add specific students to specific course.
I have a Course and Student class, and then a xaml window with a combobox, and list box with appropriate buttons.
When I press the enrol right now it simply takes the selected student and adds it into the "EnrolledStudents" text box to display the name however it doesn't actually assign it to the selected Course.
Code I have so far:
public MainWindow()
{
InitializeComponent();
Course bsc = new Course("BSc(Hons) Applied Computing");
Course hnd = new Course("Higher National Diploma (HND) Applied Computing");
Course partTime = new Course("Computer Science Part Time (MSc)");
Student andy = new Student("Andy", "Watt");
Student dave = new Student("Dave","Newbold");
Student daniel = new Student("Daniel","Brown");
lbCourses.Items.Add(bsc);
lbCourses.Items.Add(hnd);
lbCourses.Items.Add(partTime);
cbStudents.Items.Add(andy);
cbStudents.Items.Add(dave);
cbStudents.Items.Add(daniel);
}
and the enroll button click code:
private void butEnroleStudent_Click(object sender, RoutedEventArgs e)
{
cbStudents.SelectedItem.ToString();
lbEnroledStudents.Items.Add(cbStudents.SelectedItem);
}
but I am not sure where to go from here. My main issue is I don't know how to select the Student and Course instead of the string values.
As BradleyDotNET suggests, MVVM would be a lot easier for you to use, especially if your UI is going to get a lot more complicated. Also, any application like this is most likely going to rely on a database behind the scenes and hence you would be looking to bind all of your data controls.
That said, here is a sample that would achieve what you are trying to do there.
Presuming your Student class looks something like this:
private class Student
{
public String FirstName { get; set; }
public String Surname { get; set; }
public Student(String firstName, String surname)
{
FirstName = firstName;
Surname = surname;
}
public override string ToString()
{
return FirstName + " " + Surname;
}
}
And your Course class something like this:
private class Course
{
public String Name { get; set; }
public List<Student> EnrolledStudents { get; set; }
public Course(String name)
{
Name = name;
EnrolledStudents = new List<Student>();
}
public override string ToString()
{
return Name;
}
}
(Note that I have added a List to store the students enrolled on the given course)
Rather than adding to the Items property of your ListBox and ComboBox create collections that we can bind to:
private List<Student> _students;
private List<Course> _courses;
Constructing your test data then looks like this:
_courses = new List<Course>();
_courses.Add(new Course("BSc(Hons) Applied Computing"));
_courses.Add(new Course("Higher National Diploma (HND) Applied Computing"));
_courses.Add(new Course("Computer Science Part Time (MSc)"));
_students = new List<Student>();
_students.Add(new Student("Andy", "Watt"));
_students.Add(new Student("Dave", "Newbold"));
_students.Add(new Student("Daniel", "Brown"));
lbCourses.ItemsSource = _courses;
cbStudents.ItemsSource = _students;
Then when you click your enroll button
private void butEnroleStudent_Click(object sender, RoutedEventArgs e)
{
if (lbCourses.SelectedIndex >= 0 && cbStudents.SelectedIndex >= 0)
{
// Both a student and course are selected
Course selectedCourse = (Course)lbCourses.SelectedItem;
Student studentToAdd = (Student)cbStudents.SelectedItem;
if (!selectedCourse.EnrolledStudents.Contains(studentToAdd))
{
// Course does not already contain student, add them
selectedCourse.EnrolledStudents.Add(studentToAdd);
lbEnroledStudents.Items.Refresh();
}
}
}
Finally, to show the enrolled students in lbEnroledStudents as you click through the courses, you will need to hook up a new event handler in the xaml:
<ListBox x:Name="lbCourses" SelectionChanged="lbCourses_SelectionChanged"></ListBox>
And in the code behind:
private void lbCourses_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Course selectedCourse = (Course)lbCourses.SelectedItem;
lbEnroledStudents.ItemsSource = selectedCourse.EnrolledStudents;
}