Accessing function on Usercontrol hosted from Window in MVVM and C#
-
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! :)