I have a puzzled problem of databinding in WPF.
There is a listbox in XAML which it has linked with ItemSource,
but when it runs, it shows the lists of class names.
so I have applied to DisplayMemberPath, but it doesn't helpful.
and also I'm wondering how I can access inside class from generic class.
Thanks.
result
puzzled.Member
puzzled.Member
puzzled.Member
puzzled.Member
<DockPanel>
<ListBox Name="lbxMbrList" DockPanel.Dock="Left" Width="200" Padding="10"></ListBox>
<ContentControl />
</DockPanel>
private void Window_Loaded(object sender, RoutedEventArgs e)
{
members.Add(new Member("superman", "123-1234567", "address1"));
members.Add(new Member("batman", "111-111111", "address2"));
members.Add(new Member("goodman", "222-222222", "address3"));
members.Add(new Member("badman", "333-333333", "address4"));
lbxMbrList.ItemsSource = members;
lbxMbrList.DisplayMemberPath = members.MemberDetails; //<<it won't helpful
//var i = members.member.Name; //<<how can I access inside class?
//if (i == "superman")
//{
// MessageBox.Show("superman");
//}
}
public class Member
{
private string _name;
private string _phone;
private string _address;
public string Name { get { return _name; } set { _name = value; } }
public string Phone { get { return _phone; } set { _phone = value; } }
public string Address { get { return _address; } set { _address = value; } }
public Member() { }
public Member(string name, string phone, string address)
{
_name = name; _phone = phone; _address = address;
}
public string lbxMember
{
get { return string.Format("{0} - {1}", Name, Phone, Address); }
}
}
class MemberList : IEnumerable<Member>
{
private ObservableCollection<Member> memberList = new ObservableCollection<Member>();
public Member this[int i]
{
get {return memberList[i];}
set {memberList[i] = value;}
}
public void Add(Member member)
{
memberList.Add(member);
}
public void Remove(Member member)
{
memberList.Remove(member);
}
public IEnumerator<Member> GetEnumerator()
{
return memberList.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public Member member { get; set; } //<< it think I has misunderstood it
public string MemberDetails
{
get
{ return string.Format("{0} - {1}", member.Name, member.Phone, member.Address); }
}
}
You are assigning the output of your MemberDetails property to the DisplayMemberPath. Instead, you need to assign the name of the property as a string.
lbxMbrList.DisplayMemberPath = "MemberDetails";
For what its worth, this will be easier to work with if you use an ItemTemplate in the ListBox.
[Edit]
Also, as #Blam mentions in his answer, your MemberDetails property is defined in the wrong class, it needs be in the Member class.
lbxMbrList.DisplayMemberPath = "lbxMember";
or
lbxMbrList.DisplayMemberPath = "MemberDetails";
And MemberDetails need to be a property of Member (not MemberList)
Related
My View Model class:
class Student : INotifyPropertyChanged
{
private string name;
private bool isVisible;
public event PropertyChangedEventHandler PropertyChanged;
public string PersonName
{
get { return name; }
set
{
name = value;
OnPropertyChanged("PersonName");
}
}
public bool IsVisible
{
get { return isVisible; }
set
{
isVisible = value;
OnPropertyChanged("IsVisible");
}
}
}
My Students collection that store all my objects:
public ObservableCollection<Student> Students { get; set; }
XAML:
<ComboBox x:Name="cbStudents"
ItemsSource="{Binding Students}"
SelectionChanged="cbInterfaces_SelectionChanged"/>
So in some point i want to disappear several Students from my ComboBox so i just change IsVisible value to False.
Any idea how to do that using XAML ?
You can have your Students collection return only visible students.
//All students (visible and invisible)
ObservableCollection<Students> _AllStudents = GetAllStudentsFromDataSource();
//only visible students
ObservableCollection<Students> _VisibleStudents = new ObservableCollection<Students>();
foreach(var _s in _AllStudents.Where(x => x.IsVisible)){
_VisibleStudents.Add(_s);
}
//your property
public ObservableCollection<Student> Students { get{ return _VisibleStudents; } }
In the case of your check box toggling the visibility of students, your checkbox can be bound to a command like this:
<Checkbox IsChecked="{Binding IsCheckboxChecked}" Command={Binding ToggleStudents}" />
And your view model has an extra control for the checkbox toggle and the command:
bool _IsCheckboxChecked = false;
public bool IsCheckboxChecked {
get { return _IsCheckboxChecked;}
set {
if(_IsCheckboxChecked != value)
{
_IsCheckboxChecked = value;
}
}
}
public ICommand ToggleStudents
{
get;
internal set;
}
private void ToggleStudentsCommand()
{
ToggleStudents = new RelayCommand(ToggleStudentsExecute);
}
public void ToggleStudentsExecute()
{
_VisibleStudents.Clear();
if(_IsCheckboxChecked){
foreach(var _s in _AllStudents.Where(x => x.IsVisible)){
_VisibleStudents.Add(_s);
}
}
else
{
foreach(var _s in _AllStudents.Where(x => x.IsVisible == false)){
_VisibleStudents.Add(_s);
}
}
OnPropertyChanged("Students");
}
Your xaml doesn't need to change.
namespace colourchanges
{
public class Group
{
public string Name { get; set; }
//its a class for adding parent list using group class
}
public class EmployeeTree : INotifyPropertyChanged
{
public EmployeeTree()
{
this.GroupStaff = new List<Group>();
GroupStaff.Add(new Group { Name = "Designers" });
GroupStaff.Add(new Group { Name = "Developers" });
GroupStaff.Add(new Group { Name = "Managers" });
//here we are declaring list for adding parent list
}
private List<Group> _GroupStaff;
public List<Group> GroupStaff
{
get { return _GroupStaff; }
set
{
_GroupStaff = value;
RaisePropertyChanged("GroupStaff");
}
}
//creates a list for parentlist
private Group _selectedGroupStaff;
public Group selectedGroupStaff
{
get { return _selectedGroupStaff; }
set
{
_selectedGroupStaff = value;
if (selectedGroupStaff.Name == "Designers")
{
City = "Chennai";
Country = "India";
Email = "Designer#gmail.com";
MobileNo = 9094117917;
Address = "Annanagar";
}
else if (selectedGroupStaff.Name == "Developers")
{
City = "Trichy";
Country = "India";
Email = "Developer#gmail.com";
MobileNo = 9094667878;
Address = "Koyambedu";
}
else if (selectedGroupStaff.Name == "Managers")
{
City = "Salem";
Country = "India";
Email = "Manager#gmail.com";
MobileNo = 9094154678;
Address = "Arumbakkam";
}
RaisePropertyChanged("selectedGroupStaff");
}
}//for selecting parent list in order to bind to textbox
private string _City;
private string _Country;
private string _Email;
private long _MobileNo;
private string _Address;
//properties of parent list to bind to textbox
public string City
{
get { return _City; }
set
{
_City = value;
RaisePropertyChanged("City");
}
}
public string Country
{
get { return _Country; }
set
{
_Country = value;
RaisePropertyChanged("Country");
}
}
public string Email
{
get { return _Email; }
set
{
_Email = value;
RaisePropertyChanged("Email");
}
}
public long MobileNo
{
get { return _MobileNo; }
set
{
_MobileNo = value;
RaisePropertyChanged("MobileNo");
}
}
public string Address
{
get { return _Address; }
set
{
_Address = value;
RaisePropertyChanged("Address");
}
}
///raise property changed event handler code
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
//how to add sub list for designers developers and managers in the constructor
Let the following be your Model class
public class Group
{
private string _City;
private string _Country;
private string _Email;
private long _MobileNo;
private string _Address;
public Group()
{
Items = new ObservableCollection<Group>();
}
public ObservableCollection<Group> Items { get; set; }
}
and at the ViewModel's constructor, you can add the Items.
public EmployeeTree()
{
this.GroupStaff = new List<Group>();
Group rootGroup = new Group(){Name ="Manager"};
Group childGroup = new Group(){Name = "Developer"};
rootGroup.Items.Add(childGroup);
this.GroupStaff.Add(rootGroup);
}
This is for Hierarchical structure. Hope you are looking for this.
And your XAML should be like this
<TreeView Name="GroupTreeView">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
I have a WFP Project and i am using MVVM Pattern.
I have AddressView User control which i used in CustomerView UserControl.
<my:AddressVeiw Width="340" DataContext="AddressViewModel"/>
My AddressVeiw userControl has a AddressViewModel and CustomerView has a CustomerViewModel
Code for CustomerViewModel
public DelegateCommand<object> SaveCommand { get; set; }
private string firstName;
public string FirstName
{
get { return firstName; }
set {
firstName = value;
RaisePropertyChanged("FirstName");
SaveCommand.RaiseCanExecuteChanged();
}
}
private string lastName;
public string LastName
{
get { return lastName; }
set {
lastName = value;
RaisePropertyChanged("LastName");
SaveCommand.RaiseCanExecuteChanged();
}
}
private AddressViewModel addressViewModel;
public AddressViewModel AddressViewModel
{
get { return addressViewModel; }
set { addressViewModel = value; }
}
private string middleName;
public string Middlename
{
get { return middleName; }
set
{
middleName = value;
RaisePropertyChanged("MiddleName");
SaveCommand.RaiseCanExecuteChanged();
}
}
private string fullName;
public string FullName
{
get { return fullName; }
set {
fullName = value;
RaisePropertyChanged("FullName");
}
}
private void InitializeCommands()
{
SaveCommand = new DelegateCommand<object>(OnSaveCommand, CanSaveExcute);
}
private bool CanSaveExcute(object obj)
{
if (string.IsNullOrEmpty(firstName) ||string.IsNullOrEmpty(lastName))
return false;
return true;
}
private void OnSaveCommand(object obj)
{
FullName = FirstName + " " + LastName;
}
}
Code for AddressViewModel
private ObservableCollection<Country> countryList = new ObservableCollection<Country>();
public ObservableCollection<Country> CountryList
{
get { return countryList; }
set { countryList = value; }
}
public DelegateCommand<object> SaveCommand { get; set; }
private void Load()
{
try
{
CountryList = (new CountryRepository().GetAll());
}
catch (Exception ex)
{
OnSetStatusBarText("Error: " + ex.Message.ToString());
}
}
private void OnSetStatusBarText(string message)
{
var evt = eventAgg.GetEvent<StatusBarMessageEvent>();
evt.Publish(message);
}
private void InitializeCommands()
{
SaveCommand = new DelegateCommand<object>(OnSaveCommand, CanSaveExcute);
}
private bool CanSaveExcute(object obj)
{
return true;
}
private void OnSaveCommand(object obj)
{
}
Some how i can hook my AdddressViewModel To my AddressView, Customer Works fine...
What Must be done to resolve this problem?
Thanks
You need to use a binding expression for the DataContext of your AddressView. Instead of this...
<my:AddressVeiw Width="340" DataContext="AddressViewModel"/>
...try this...
<my:AddressVeiw Width="340" DataContext="{Binding AddressViewModel}"/>
You're close, but you need a binding:
<my:AddressVeiw Width="340" DataContext="{Binding AddressViewModel}"/>
I'm starting Caliburn Micro development and I have thought of an architecture where a viewmodel has properties, injected by MEF, which are other viewmodels. That way I can use contentcontrols in the view to position them the way I want.
public class ContactsProfileViewModel : Conductor<IContentItem>, IContactsModuleViewModel, IModule, IPartImportsSatisfiedNotification
{
private string name;
private string nameCaption;
private ISingleLineTextContentItem firstName;
private ISingleLineTextContentItem lastName;
public ContactsProfileViewModel()
{
this.DisplayName = "Contact Tab";
}
public string Name
{
get
{
return this.name;
}
set
{
this.name = value;
this.NotifyOfPropertyChange(() => Name);
}
}
public string NameCaption
{
get
{
return this.nameCaption;
}
set
{
this.nameCaption = value;
this.NotifyOfPropertyChange(() => NameCaption);
}
}
[Import(typeof(ISingleLineTextContentItem))]
public ISingleLineTextContentItem FirstName
{
get { return this.firstName; }
set
{
this.firstName = value;
this.NotifyOfPropertyChange(() => FirstName);
}
}
[Import(typeof(ISingleLineTextContentItem))]
public ISingleLineTextContentItem LastName
{
get { return this.lastName; }
set
{
this.lastName = value;
this.NotifyOfPropertyChange(() => LastName);
}
}
The viewmodel of SingleLineTextContentItem looks like this:
[Export(typeof(ISingleLineTextContentItem))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class SingleLineTextContentItemViewModel : PropertyChangedBase, ISingleLineTextContentItem
{
private string textBoxText;
private string caption;
public string TextBoxText
{
get { return textBoxText; }
set
{
textBoxText = value;
this.NotifyOfPropertyChange(() => TextBoxText);
}
}
public string Caption
{
get { return caption; }
set
{
this.caption = value;
this.NotifyOfPropertyChange(() => Caption);
}
}
}
Now, I need a way to bind the NameCaption property to the Caption property in a two-way manner. Is that possible? I'm I on the right track with this or is there a better way to do this?
Thanks,
Roland
What I do is instead of having a backing field just route to the other view model
public string NameCaption
{
get
{
return FirstName.Caption;
}
set
{
FirstName.Caption = value;
this.NotifyOfPropertyChange(() => NameCaption);
}
}
However if the Caption property on the ISingleLineTextContentItem can get set independently then you need to register changes on the event and have the view model listen to changes. So instead you need somthing along the lines of:
public string NameCaption
{
get
{
return FirstName == null ? string.Empty : FirstName.Caption;
}
set
{
if(FirstName != null)
FirstName.Caption = value;
}
}
[Import(typeof(ISingleLineTextContentItem))]
public ISingleLineTextContentItem FirstName
{
get { return this.firstName; }
set
{
if(this.FirstName != null)
this.FirstName.PropertyChanged -= FirstNameChanged;
this.firstName = value;
if(this.FirstName != null)
this.FirstName.PropertyChanged += FirstNameChanged;
this.NotifyOfPropertyChange(() => FirstName);
this.NotifyOfPropertyChange(() => NameCaption);
}
}
private void FirstNameChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertName == "Caption")
this.NotifyOfPropertyChange(() => NameCaption);
}
Since either the Caption property or the FirstName property can change then we need to raise the event in the FirstName property and in the handler.
Following is part of service layer which is provided by WCF service :
[Serializable]
public class WaitInfo
{
private string roomName;
private string pName;
private string tagNo;
public string RoomName
{ get { return roomName; } set { this.roomName = value; } }
public string PName
{ get { return pName; } set { this.pName = value; } }
public string TagNo
{ get { return tagNo; } set { this.tagNo = value; } }
}
public class Service1 : IService1
{
public List<WaitInfo> GetWaitingList()
{
MyDBDataContext db = new MyDBDataContext();
var query = from w in db.WAIT_INFOs
select new WaitInfo
{
TagNo = w.PATIENT_INFO.TAG_NO,
RoomName= w.ROOM_INFO.ROOM_NAME,
PName= w.PATIENT_INFO.P_NAME
};
List<WaitInfo> result = query.ToList();
return result;
}
And following is codebehind part of UI layer which is provided by Silverlight
public MainPage()
{
InitializeComponent();
Service1Client s = new Service1Client();
s.GetWaitingListCompleted +=
new EventHandler<GetWaitingListByCompletedEventArgs>( s_GetWaitingListCompleted);
s.GetWaitingListAsync();
}
void s_GetWaitingListCompleted(object sender,
RadControlsSilverlightApplication1.ServiceReference2.GetWaitingListByCompletedEventArgs e)
{
GridDataGrid.ItemsSource = e.Result;
}
And following is xaml code in Silverlight page
<Grid x:Name="LayoutRoot">
<data:DataGrid x:Name="GridDataGrid"></data:DataGrid>
</Grid>
It is very simple code, however what I am thinking weird is property name of object at "e.Result" in the code behind page.
In the service layer, although properties' names are surely "RoomName, PName, TagNo", in the silverlight properties' names are "roomName, pName, tagNo" which are private variable name of the WaitingList Object.
Did I something wrong?
Thanks in advance.
Unless you specifically decorate your class with the DataContract attribute (which you should, instead of Serializable) then a default DataContract will be inferred. For normal Serializable types, this means the fields will be serialized as opposed to the properties.
You can markup your class in either of the following two ways. The latter will use the property accessors when serializing/deserializing your object which may be very useful or be a hassle depending on your circumstances.
[DataContract]
public class WaitInfo
{
[DataMember(Name="RoomName")]
private string roomName;
[DataMember(Name="PName")]
private string pName;
[DataMember(Name="TagNo")]
private string tagNo;
public string RoomName
{ get { return roomName; } set { this.roomName = value; } }
public string PName
{ get { return pName; } set { this.pName = value; } }
public string TagNo
{ get { return tagNo; } set { this.tagNo = value; } }
}
The method I prefer:
[DataContract]
public class WaitInfo
{
private string roomName;
private string pName;
private string tagNo;
[DataMember]
public string RoomName
{ get { return roomName; } set { this.roomName = value; } }
[DataMember]
public string PName
{ get { return pName; } set { this.pName = value; } }
[DataMember]
public string TagNo
{ get { return tagNo; } set { this.tagNo = value; } }
}