Accessing function on Usercontrol hosted from Window in MVVM and C#
-
**NEVER** build a TreeView by hand. ESPECIALLY in WPF/MVVM. You should build a simple nested collection and then use a HierarchicalDataTemplate to define how to populate the TreeView from that collection. The collection(s) should implement INotifyCollectionChanged and the objects inside of the collection(s) should implement INotifyPropertyChanged. Then just simply set the ItemsSource of the TreeView to point to your collection and sit back and watch the magic happen. If you do this, any changes you make to your collection(s) and item(s) will auto-magically be reflected in the UI.
How do I populate that type of Treeview? I've created a listview such like you said for my MRU list I need to populate the Treeview with sorted data by Alphabetic base letter A-Z then Lastname, Firstname middle intial. I'm already using a collection:
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;namespace Manufactured_Housing_Manager
{
public class tvIndex
{
private string _index;
public string Index
{
get { return _index; }
set { _index = value; }
}private ObservableCollection \_contacts; public ObservableCollection Contacts { get { if (\_contacts == null) \_contacts = new ObservableCollection(); return \_contacts; } } public tvIndex() { } public tvIndex( string indexx, ObservableCollection contacts) { \_index = indexx; \_contacts = contacts; } }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace Manufactured_Housing_Manager
{
public class Contact
{
private string _name;
private int _id;
public string Name
{
get { return _name; }
set { _name = value; }
}public int Id { get { return \_id; } set { \_id = value; } } public Contact() { } public Contact(string name, int id) { \_name = name; \_id = id; } }
}
My Xaml is this:
</x-turndown>
-
How do I populate that type of Treeview? I've created a listview such like you said for my MRU list I need to populate the Treeview with sorted data by Alphabetic base letter A-Z then Lastname, Firstname middle intial. I'm already using a collection:
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;namespace Manufactured_Housing_Manager
{
public class tvIndex
{
private string _index;
public string Index
{
get { return _index; }
set { _index = value; }
}private ObservableCollection \_contacts; public ObservableCollection Contacts { get { if (\_contacts == null) \_contacts = new ObservableCollection(); return \_contacts; } } public tvIndex() { } public tvIndex( string indexx, ObservableCollection contacts) { \_index = indexx; \_contacts = contacts; } }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace Manufactured_Housing_Manager
{
public class Contact
{
private string _name;
private int _id;
public string Name
{
get { return _name; }
set { _name = value; }
}public int Id { get { return \_id; } set { \_id = value; } } public Contact() { } public Contact(string name, int id) { \_name = name; \_id = id; } }
}
My Xaml is this:
</x-turndown>
You mean, you want your tree to look like: A ALastName, AFirstName ALastName2, AFirstName2 B BLastName, BFirstName BLastName2, BFirstName ?
-
You mean, you want your tree to look like: A ALastName, AFirstName ALastName2, AFirstName2 B BLastName, BFirstName BLastName2, BFirstName ?
Yeah I already have this doing it but not in this wpf version. I was hosting everything inside one form and had several classes. Now I'm trying to properly create user controls for the ribbon the treeview/outlook bar, statusbar, and tabs which will display records. Basically I'm trying to improve my coding techniques and more correctly adapt to MVVM style. Old dog trying to learn new tricks.
-
Yeah I already have this doing it but not in this wpf version. I was hosting everything inside one form and had several classes. Now I'm trying to properly create user controls for the ribbon the treeview/outlook bar, statusbar, and tabs which will display records. Basically I'm trying to improve my coding techniques and more correctly adapt to MVVM style. Old dog trying to learn new tricks.
Haha... yeah, MVVM is a learning curve on top of the WPF learning curve. Trust me though, I come from the MFC / C++ world and did a bit of time in the C# / Winforms world... the WPF / MVVM way is by far the best. It results in *really* clean code. Anyways... Your data structure should kind of try to closely mirror how you want it in the tree. Obviously we aren't going to be modifying the alphabet any time soon, so we have 26 root nodes. I'd probably try to store it in something like: public class ContactCollection : ObservableCollection<Contact> The reason you need to do that is because XAML doesn't work well with generics. SortedDictionary<char, ContactCollection> so you'll populate the sorted dictionary with keys 'A' through 'Z' and new up an ContactCollection as the value. Then populate each ContactCollection with the names matching that letter. There is a little known trick to sort the ObservableCollections after you populate them. You derive a class from ObservableCollection and add a method like this:
public void Sort(Comparison<T> comparison)
{
((List<T>)Items).Sort(comparison);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}Then your HierachialDataTemplate would just specify Key as the top level display binding and Values as the ItemsSource. Another HierachialDataTemplate would be typed for Contact and have the name as the display value. This all might not make sense now, but once you get going, it will.
-
Haha... yeah, MVVM is a learning curve on top of the WPF learning curve. Trust me though, I come from the MFC / C++ world and did a bit of time in the C# / Winforms world... the WPF / MVVM way is by far the best. It results in *really* clean code. Anyways... Your data structure should kind of try to closely mirror how you want it in the tree. Obviously we aren't going to be modifying the alphabet any time soon, so we have 26 root nodes. I'd probably try to store it in something like: public class ContactCollection : ObservableCollection<Contact> The reason you need to do that is because XAML doesn't work well with generics. SortedDictionary<char, ContactCollection> so you'll populate the sorted dictionary with keys 'A' through 'Z' and new up an ContactCollection as the value. Then populate each ContactCollection with the names matching that letter. There is a little known trick to sort the ObservableCollections after you populate them. You derive a class from ObservableCollection and add a method like this:
public void Sort(Comparison<T> comparison)
{
((List<T>)Items).Sort(comparison);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}Then your HierachialDataTemplate would just specify Key as the top level display binding and Values as the ItemsSource. Another HierachialDataTemplate would be typed for Contact and have the name as the display value. This all might not make sense now, but once you get going, it will.
Kind of understand some but still confused. Below is the class I'm constructing
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;namespace Manufactured_Housing_Manager
{
public class ContactCollection : ObservableCollection
{
#region left, right, mid string functionspublic static string left(string param, int length) { //we start at 0 since we want to get the characters starting from the //left and with the specified length and assign it to a variable string result = param.Substring(0, length); //return the result of the operation return result; } public static string mid(string param, int startIndex, int length) { //start at the specified index in the string and get the number of //characters depending on the length and assign it to a variable string result = param.Substring(startIndex, length); //return the result of the operation return result; } public static string right(string param, int length) { //start at the index based on the length of the string minus //the specified length and assign it a variable string result = param.Substring(param.Length - length, length); //return the result of the operation return result; } #endregion public ContactCollection() : base() { Add(new Contact("Davis, Adam T.", 0)); Add(new Contact("Zappa, Frank M.", 1)); Add(new Contact("Henry, Victor", 2)); Add(new Contact("Vern, Jules W.", 3)); } } public class Contact { private string \_name; private int \_id; public string Name { get { return \_name; } set { \_name = value; } } public int Id { get { return \_id; } set { \_id = value; } } public Contact() { } public Contact(string name, int id) { \_name = name; \_id = id; } }
}
Thing is I need to open my OLEDB Database to populate my dataset then I populate ObservableCollection with the names from each Contact in
-
Kind of understand some but still confused. Below is the class I'm constructing
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;namespace Manufactured_Housing_Manager
{
public class ContactCollection : ObservableCollection
{
#region left, right, mid string functionspublic static string left(string param, int length) { //we start at 0 since we want to get the characters starting from the //left and with the specified length and assign it to a variable string result = param.Substring(0, length); //return the result of the operation return result; } public static string mid(string param, int startIndex, int length) { //start at the specified index in the string and get the number of //characters depending on the length and assign it to a variable string result = param.Substring(startIndex, length); //return the result of the operation return result; } public static string right(string param, int length) { //start at the index based on the length of the string minus //the specified length and assign it a variable string result = param.Substring(param.Length - length, length); //return the result of the operation return result; } #endregion public ContactCollection() : base() { Add(new Contact("Davis, Adam T.", 0)); Add(new Contact("Zappa, Frank M.", 1)); Add(new Contact("Henry, Victor", 2)); Add(new Contact("Vern, Jules W.", 3)); } } public class Contact { private string \_name; private int \_id; public string Name { get { return \_name; } set { \_name = value; } } public int Id { get { return \_id; } set { \_id = value; } } public Contact() { } public Contact(string name, int id) { \_name = name; \_id = id; } }
}
Thing is I need to open my OLEDB Database to populate my dataset then I populate ObservableCollection with the names from each Contact in
Ok, so your ROOT object is going to be a:
SortedDictionary<char, ContactCollection>
So you would do something like:
SortedDictionary<char, ContactCollection> dict = new SortedDictionary();
for (int i = System.Convert.ToInt32('A'); i <= System.Convert.ToInt32('Z'); i++)
{
ContactCollection cc = new ContactCollection();// add contacts with the last name starting with i to cc here
foreach (...)
{
}dict.Add(System.Convert.ToChar(i), cc);
}tv.ItemsSource = dict;
Your contact object also needs to implement INotifyPropertyChanged for this all to work correctly. Report what your tree looks like on the screen and we can tweak the HierarchialDataTemplate to display the right content.
-
Ok, so your ROOT object is going to be a:
SortedDictionary<char, ContactCollection>
So you would do something like:
SortedDictionary<char, ContactCollection> dict = new SortedDictionary();
for (int i = System.Convert.ToInt32('A'); i <= System.Convert.ToInt32('Z'); i++)
{
ContactCollection cc = new ContactCollection();// add contacts with the last name starting with i to cc here
foreach (...)
{
}dict.Add(System.Convert.ToChar(i), cc);
}tv.ItemsSource = dict;
Your contact object also needs to implement INotifyPropertyChanged for this all to work correctly. Report what your tree looks like on the screen and we can tweak the HierarchialDataTemplate to display the right content.
One last problem I think. How do I get tv.ItemsSource = dict; to work. I get, An object reference is required for the non-static field, method, or property 'Manufactured_Housing_Manager.Outlookbar.MyTreeView' I tried adding this to the tope of this class using tv = Manufactured_Housing_Manager.Outlookbar; also this is my xaml for binding it's inside Outlookbar.xaml
modified on Friday, August 12, 2011 7:49 PM
-
One last problem I think. How do I get tv.ItemsSource = dict; to work. I get, An object reference is required for the non-static field, method, or property 'Manufactured_Housing_Manager.Outlookbar.MyTreeView' I tried adding this to the tope of this class using tv = Manufactured_Housing_Manager.Outlookbar; also this is my xaml for binding it's inside Outlookbar.xaml
modified on Friday, August 12, 2011 7:49 PM
I know I'm over thinking this. The whole Observable Collections thing is new to me. I'm confusing myself and getting discouraged. I created a hierarchical observable collection that worked before. but populating the class with data from my user selected OLE database was done outside the class. I can vaguely see how this is supposed to work , I'm just not visualizing the code to make it happen very well. All of you help has been and is appreciated.
-
I know I'm over thinking this. The whole Observable Collections thing is new to me. I'm confusing myself and getting discouraged. I created a hierarchical observable collection that worked before. but populating the class with data from my user selected OLE database was done outside the class. I can vaguely see how this is supposed to work , I'm just not visualizing the code to make it happen very well. All of you help has been and is appreciated.
I was just showing you what property you needed to set. In proper MVVM, you don't set the ItemsSource explicitly. You just return it from your ViewModel. public SortedDictionary> ItemsSource { get { if (_dict == null) { // create the dictionary and observable collections here and // store in _dict } return _dict; } then your XAML's DataContext should point to your ViewModel. Then you can just do: and your tree should be built for you.
-
I was just showing you what property you needed to set. In proper MVVM, you don't set the ItemsSource explicitly. You just return it from your ViewModel. public SortedDictionary> ItemsSource { get { if (_dict == null) { // create the dictionary and observable collections here and // store in _dict } return _dict; } then your XAML's DataContext should point to your ViewModel. Then you can just do: and your tree should be built for you.
You were talking about my needing to implement INotifyPropertyChanged, I've tried to use this before but I never was able to get it to work properly. How do you implement it and respond to it? I know that if I add or remove an item from my collection it will flag the INotifyPropertyChanged event. and I know that if I setup a function to respond to this it will run the code within it. But how do I use it actually?
-
You were talking about my needing to implement INotifyPropertyChanged, I've tried to use this before but I never was able to get it to work properly. How do you implement it and respond to it? I know that if I add or remove an item from my collection it will flag the INotifyPropertyChanged event. and I know that if I setup a function to respond to this it will run the code within it. But how do I use it actually?
If you use ObservableCollection, that already implements INotifyCOLLECTIONChanged for you. That will automatically update the UI when an item is added or removed. Your Contact object needs to implement INotifyPROPERTYChanged so the UI knows when a property of the contact object has changed. See this for a basic example: http://msdn.microsoft.com/en-us/library/ms229614.aspx[^]. For both INotifyPropertyChanged and INotifyCollectionChanged, you do not need to do anything else beyond implement the interfaces. When you bind to stuff, the WPF infrastructure will subscribe to the necessary events and do its thing for you.
-
If you use ObservableCollection, that already implements INotifyCOLLECTIONChanged for you. That will automatically update the UI when an item is added or removed. Your Contact object needs to implement INotifyPROPERTYChanged so the UI knows when a property of the contact object has changed. See this for a basic example: http://msdn.microsoft.com/en-us/library/ms229614.aspx[^]. For both INotifyPropertyChanged and INotifyCollectionChanged, you do not need to do anything else beyond implement the interfaces. When you bind to stuff, the WPF infrastructure will subscribe to the necessary events and do its thing for you.
This is what I came up with
using System;
using System.Data;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;namespace Manufactured_Housing_Manager
{
public class Contact : INotifyPropertyChanged
{
private string _name = String.Empty;
private int _id = 0;public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } } public string Name { get { return this.\_name; } set { if (value != this.\_name) { this.\_name = value; NotifyPropertyChanged("Name"); } } } public int Id { get { return this.\_id; } set { if (value != this.\_id) { this.\_id = value; NotifyPropertyChanged("Id"); } } } public Contact() { } public Contact(string name, int id) { \_name = name; \_id = id; } }
}
My Xaml code is
-
This is what I came up with
using System;
using System.Data;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;namespace Manufactured_Housing_Manager
{
public class Contact : INotifyPropertyChanged
{
private string _name = String.Empty;
private int _id = 0;public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } } public string Name { get { return this.\_name; } set { if (value != this.\_name) { this.\_name = value; NotifyPropertyChanged("Name"); } } } public int Id { get { return this.\_id; } set { if (value != this.\_id) { this.\_id = value; NotifyPropertyChanged("Id"); } } } public Contact() { } public Contact(string name, int id) { \_name = name; \_id = id; } }
}
My Xaml code is
I have told you how a few times now :) and even gave you the code last time. You need to have *26* ObservableCollection's. Your *ROOT* object needs to be a SortedDictionary>. You will insert 26 entries into the sorted dictionary. Key is 'A' - 'Z' and the value is a new ObservableCollection that contains the last names starting with that key.
-
Ok, so your ROOT object is going to be a:
SortedDictionary<char, ContactCollection>
So you would do something like:
SortedDictionary<char, ContactCollection> dict = new SortedDictionary();
for (int i = System.Convert.ToInt32('A'); i <= System.Convert.ToInt32('Z'); i++)
{
ContactCollection cc = new ContactCollection();// add contacts with the last name starting with i to cc here
foreach (...)
{
}dict.Add(System.Convert.ToChar(i), cc);
}tv.ItemsSource = dict;
Your contact object also needs to implement INotifyPropertyChanged for this all to work correctly. Report what your tree looks like on the screen and we can tweak the HierarchialDataTemplate to display the right content.
Thanks for all the help I have it working now! :)