I want to write a custom class in which whenevr I add a node to treeview it should call an event. so that I can run it recursively.
I've rarely used TreeView, but at a glance this is my first impression. Since TreeViewCollection cannot be inherited from, you may have to create a separate method on your new class that will perform the actual adding to the Nodes collection. This will allow you to tie in an event. Something like this in your inherited class:
public event EventHandler NodeAdded;
public void AddNode(TreeViewNode node)
{
Nodes.Add(node);
if (NodeAdded != null)
{
NodeAdded(this, EventArgs.Empty);
}
}
You could then create a custom EventArgs class and include any information you may want to use.
Unfortunately, since TreeNodeCollection is in fact a tree, I think you're going to be stuck needing to implement your own tree view control essentially from scratch, with a custom tree data structure with events, or finding a third-party or open source one to use.
If it were a simple collection, I'd say just wrap a framework TreeView control in a custom user control, and hide the Nodes property, exposing only the accessors and mutators you want. Then you could just fire your control's events before and/or after passing the calls through to the underlying TreeView control. However, this would only give you events on the first layer of nodes.
It's easy to make a List or Dictionary class with mutator events, because you can just wrap them like that. But trees are royal pain to have to implement, especially if you're going to distribute or sell the code that uses it. And unfortunately, we get essentially no help with them from the .NET framework. Because of all the complexities of implementing a tree structure, I would strongly recommend trying to find a pre-made solution, either third-party or open source.
Related
I have treeview control on a winform and need to implement onChange event for it. However, it looks like it doesn't have one and only has onChangeUI.
If the treeview doesn't support onChange event, what is its equivalent in .NET.
I've searched MSDN Library and didn't find any information.
Update: a side note I am converting win32 program for .net.
Thanks in advance,
Support for selection change is a little limited in the Windows Forms TreeView control.
Basically, there is a pair of events (BeforeSelect and AfterSelect) that allow you to react when a tree node is selected. BeforeSelect allows you to cancel the new selection, AfterSelect does not (because it occurs after the new selection has been committed).
However, none of these events are triggered when a node is unselected. To detect that case, you'll have to handle the generic MouseUp event and check the IsSelected property of the clicked node to get the actual selection state.
This is a common restriction in the wrapper classes that wrap native Windows controls. Which only generate notifications for things that you cannot know about. Like anything that the user can do that affects the control. It omits notifications for things that you do, with the underlying philosophy that you don't have to be reminded about something you already know.
Which is certainly the case for TreeView, the user cannot add any nodes. Only you can. Same thing for the text displayed in the nodes. No event to tell you the text changed. Except in the very specific case when the user edits the node, AfterLabelEdit event.
You can derive your own class from TreeView and add a Change event and OnChange() method that fires it. It is up to you to write the code to call the method. Beware that this is difficult to do reliably, the TreeNodeCollection class doesn't have virtual methods so you cannot override them to detect the client code changing nodes. You are actually better off not writing that code and simply generate an internal event in the form in any method that modifies the TreeView content.
I was looking around for something like ObservableCollection<T> that would also raise an event whenever the properties of item in the collection change. I came across BindingList<T>, but was disappointed to learn that it's not available in Silverlight.
I'm aware that I could build this fairly easily by subclassing ObservableCollection<T> and subscribing to the PropertyChanged events of items as they are added (and unsubscribing as they are removed, of course). Before I do that, though, I want to make sure there isn't something I can use out of the box.
The WPF way to do this is to have the objects that you're putting in the ObservableCollection inherit from DependencyObject and have DependencyProperties instead of old fashioned properties and PropertyChanged events. This may mean you have to create a wrapper object that implements this stuff. This will make bindings from UI to the objects in your collection automatically update.
If, on the other hand, you're not doing databinding, and instead you just want a way to get an event to fire in code whenever any property on one of your objects changes, you'd probably need to create your own collection to do this.
http://msdn.microsoft.com/en-us/library/ms752914.aspx has a good explanation of DependencyProperties and examples of the implementation.
I've adopted the following pattern for putting ListViewItems in a ListView with multiple columns (when I want to display information about a list of MyObject types), and I'm just curious to see if this is the best way to accomplish this task, or if there's anything more efficient and readable in code:
Create an inherited ListViewItem class that takes a MyObject object in the constructor - I'll call this MyObjectListViewItem - and a Refresh() method that clears and re-populates the ListViewItem subitems.
Populate the ListView with my new MyObjectListViewItem items.
example:
public MyObject MyObject { get; set; }
public MyObjectListViewItem(MyObject myObj)
{
MyObject = myObj;
this.Refresh();
}
public void Refresh()
{
this.SubItems.Clear();
this.Text = myObj.FirstColumnProperty;
this.SubItems.Add(myObj.SecondColumnProperty); // etc...
}
Suggestions? Better ways?
Have you considered using a BindingSource, or creating your own which implements IBindingListView? This keeps concerns about the data and its state scoped to the data itself and not on any controls which consume it. Since .NET controls are already built to work with BindingSources, you can take advantage of some more robust functionality. Instead of explicitly invoking a screen refresh, the control is simply responsible for responding to events raised by the binding source, and a controller that notifies whether the control is ready to be refreshed without forcing it.
Making ListViewItems that know how to build themselves is a good idea.
If you extend that idea a little, you make the columns know how to build each subitem, which also allows them to be able to automatically sort the ListView, support grouping and copy/drag and drop rows. This is just a few of the things that ObjectListView does for you.
ObjectListView is an open source wrapper around a .NET WinForms ListView control that makes the ListView much easier to use -- as well as adding some very nice new features and getting around some annoying bugs/limitations.
If you did like #Rex's idea of using a BindingSource, the ObjectListView project also provides a data-aware DataListView which is data bindable.
I have a a user control which contains several other user controls. I am using MVVM. Each user control has a corresponding VM. How do these user controls send information to each other? I want to avoid writing any code in the xaml code behind. Particularly I am interested in how the controls (inside the main user control) will talk to each other and how will they talk to the container user control.
EDIT:
I know that using events-delegates will help me solve this issue. But, I want to avoid writing any code in xaml code-behind.
Typically, it's best to try to reduce the amount of communication between parts, as each time two user controls "talk" to each other, you're introducing a dependency between them.
That being said, there are a couple of things to consider:
UserControls can always "talk" to their containing control via exposing properties and using DataBinding. This is very nice, since it preserves the MVVM style in all aspects.
The containing control can use properties to "link" two properties on two user controls together, again, preserving clean boundaries
If you do need to have more explicit communication, there are two main approachs.
Implement a service common to both elements, and use Dependency Injection to provide the implementation at runtime. This lets the controls talk to the service, which can in turn, keep the controls synchronized, but also keeps the dependency to a minimum.
Use some form of messaging to pass messages between controls. Many MVVM frameworks take this approach, as it decouples sending the message from receiving the message, again, keeping the dependencies to a minimum.
Your conceptual problem is here:
Each user control has a corresponding VM.
Having a separate ViewModel for every view pretty much defeats the concept of a ViewModel. ViewModels should not be one-to-one with views, otherwise they are nothing but glorified code-behind.
A ViewModel captures the concept of "current user interface state" -- such as what page you are on and whether or not you are editing -- as opposed to "current data values'.
To really reap the benefits of M-V-VM, determine the number of ViewModel classes used based on distinct items that need state. For example, if you have a list of items each of which can be displayed in 3 states, you need one VM per item. Contrarily, if you have three views all of which display data in 3 different ways depending on a common setting, the common setting should be captured in a single VM.
Once you have strucutred your ViewModels to reflect the requirements of the task at hand you generally find there is no need nor desire to communicate state between views. If there is such a need, the best thing to do is to re-evaluate your ViewModel design to see if a shared ViewModel could benefit from a small amount of additional state information.
There will be times when the complexity of the application dictates the use of several ViewModels for the same model object. In this case the ViewModels can keep references to a common state object.
There are many differenct mechanisms for this, but you should first find out in what layer of your architecture this communication belongs.
One of the purposes of the MVVM framework is that different views can be made over the same viewmodel. Would those usercontrols talk to each other only in the view you are currently implementing, or would they have to talk to each other in other possible views? In the latter case, you want to implement it below the view level, either in the viewmodel or the model itself.
An example of the first case may be if your application is running on a very small display surface. Maybe your user controls have to compete for visual space. If the user clicks one usercontrol to maximize, the others must minimize. This would have nothing to do with the viewmodel, it's just an adaption to the technology.
Or maybe you have different viewmodels with different usercontrols, where things can happen without changing the model. An example of this could be navigation. You have a list of something, and a details pane with fields and command buttons that are connected to the selected item in the list. You may want to unit test the logic of which buttons are enabled for which items. The model isn't concerned with which item you're looking at, only when button commands are pressed, or fields are changed.
The need for this communication may even be in the model itself. Maybe you have denormalized data that are updated because other data are changed. Then the various viewmodels that are in action must change because of ripples of changes in the model.
So, to sum up: "It depends...."
I think the best solution would be using Publisher/Subscriber pattern. Each control registers some events and attaches delegetes to events exposed by other controls.
In order to expose events and attach to them you would need to use some kind of Mediator/EventBroker service. I found a good example here
The best way to do this in my opinion is via Commanding (Routed Commands / RelayCommand, etc).
I want to avoid writing any code in the xaml code behind.
While this is a laudable goal, you have to apply a bit of practicality to this, it shouldn't be applied 100% as a "thou shalt not" type of rule.
You can communicate between elements on the UI by using element binding, so assuming a user control you created exposes a property, the other user controls could bind to it. You can configure the binding, use dependency properties instead of basic properties / implement INotifyPropertyChanged but it is in theory possible, but does require some forethought to enable to communication this way.
You will probably find it far easier using a combination of events, code and properties than try a pure declarative way, but in theory possible.
You can share some View Model objects between controls as well as Commands...
For example, you have some main control, which contains two other controls. And you have some filtering functionality in the main control, but you want to allow user to set some part of the filter in the first sub-control (like "Full filter") and some part of the filter in another (like "Quick filter"). Also you want to be able to start filtering from any of sub-controls. Then you could use code like this:
public class MainControlViewModel : ObservableObject
{
public FirstControlViewModel firstControlViewModel;
public SecondControlViewModel firstControlViewModel;
public ICommand FilterCommand;
public FilterSettings FilterSettings;
public MainControlViewModel()
{
//...
this.firstControlViewModel = new FirstControlViewModel(this.FilterSettings, this.FilterCommand);
this.secondControlViewModel = new SecondControlViewModel(this.FilterSettings, this.FilterCommand);
}
}
public class FirstControlViewModel : ObservableObject
{
//...
}
public class SecondControlViewModel : ObservableObject
{
//...
}
In the main control XAML you will bind sub-controls DataContext to the appropriate View Models. Whenever a sub-control changes filter setting or executes a command other sub-control will be notified.
As others have said you have a couple of options.
Exposing DepedencyProperties on your user controls and binding to those properties provides a pure XAML solution in most cases but can introduce some UI dependencies in order for the bindings to see each other
The other option is a decoupled messaging pattern to send messages between ViewModels. I would have your user controls bind to properties on thier own VM's and then on the property change inside that VM it can "publish" a message that notifies other "subscribers" that something has happened and they can react to that message however they want to.
I have a blog post on this very topic if it helps: http://www.bradcunningham.net/2009/11/decoupled-viewmodel-messaging-part-1.html
If you're using strict MVVM, then the user-control is a View and should only "talk", or rather, bind, to its ViewModel. Since your ViewModels most likely already implement INotifyPropertyChanged, as long as they have a reference to each other, they can use the PropertyChanged events to be notified when properties change, or they can call methods (better if it's through an interface) to communicate with each other.
I'm trying to use a TreeView to display a tree structure of objects. I have a tree of four types of object, Company (the root node), City, Store and Employee.
The interface is designed to add/remove Cities/Stores/Employees, so the TreeView has to update to reflect any changes.
I am wondering on the correct way to get the TreeView to display the tree structure, and receive updates when it changes.
I am thinking that the Company object should have events, such as company.CityAdded and company.CityRemoved, then whatever wrapper I put round the TreeView responds to those events? When the TreeView has been built up, there will be a node per city/store/employee. Each node could then respond to events of the node it represents in the tree.
Is that the right idea? Or is there a better method?
I just wanted to add that if WPF is an option for this, it becomes incredible simple using heirarchtical databinding and observablecollections. It basically does all the event handle stuff for you, and lets you just interact with your business objects.
Sounds like you are on the right path. I had to do similar thing, a few pointers I'd like to share:
Store object reference in TreeNode tag property.
Give each Treenode a unique name that can easily identify an object, for example: object hashcode, Company ID, etc.
This way you can easily find and update TreeNode when object state changes. And when user selects a node, you can grab an object it's representing from the Tag property.
Good luck.
You're on the right lines about the concept of listening to events (it's a standard publisher/subscriber pattern).
For the actual updating of the treeview I tend to have two methods: AddOrUpdateTreeItem, and RemoveTreeItem. The add or update method does what it says, looks for the tree item (based on a path) and updates it or adds it. Of course if the model is being updated on a thread other than the one the form was created on you will need to marshal the call using Control.BeginInvoke().
This approach can be a little slow if you're populating a full tree at form_load or something, so you might have a different method for an initial population, and use the concept I've described here for subsequent updates.
I do the same for listviews, here's a typical example. The main difference when adding a tree item is that you might need to add the parent nodes in order to add the node being requested, which makes it a bit recursive. Give it a try.
private void AddOrUpdateListItem(DomainModelObject item)
{
ListViewItem li = lvwListView.Items[GetKey(item)];
if (li == null)
{
li = new ListViewItem
{
Name = GetKey(item),
Tag = item
};
li.SubItems.Add(new ListViewItem.ListViewSubItem());
li.SubItems.Add(new ListViewItem.ListViewSubItem());
li.SubItems.Add(new ListViewItem.ListViewSubItem());
li.ImageIndex = 0;
lvwListView.Items.Add(li);
}
li.Text = [Itemtext];
li.SubItems[1].Text = [Itemtext];
li.SubItems[2].Text = [Itemtext];
li.SubItems[3].Text = [Itemtext];
}
Here's an example of how BeginInvoke() might be implemented:
public class MyForm : Form
{
...
void data_Changed(object sender, DataChangedEventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new EventHandler<DataChangedEventArgs>(data_Changed), sender, e);
return;
}
AddOrUpdateListItem(e.DataItem);
}
...
}
Instead of ...
Business objects subscribe to UI events
Commands update the UI
Business objects are updated when the UI is updated
... you can also do it the other way around (i.e. commands update the tree of business objects, which results in a corresponding update to the UI).
Part of the key to the publish/subscribe pattern for updates is how to wrap the information of what to do when the event triggers.
When the object representing "Store X" is updated with a new name, and fires an event to announce this has happened, which object consumes the event?
Similarly, when City Y is added, which object should be notified of the creation?
One common approach is to have some kind of large uber-manager class that handles the entire process - it subscribes to all the events and does everything.
A different approach, one I've used to good effect, is to create much simpler wrapper/coordinator objects that handle just one part of the puzzle. Typically, I suffix the name of these classes with "Editor".
So, you could have a CityEditor class whose constructor takes both a City object, and the TreeNode that represents that object. The CityEditor would subscribe to events on both the City object and the TreeNode, and would take care of populating the TreeNode with a caption and selecting an icon.
When the City object updates, the CityEditor responds to the triggered event by updating the TreeNode. When the City object is removed, the CityEditor makes sure the node is removed from the Treeview.
When a new Store object is added to the City, the CityEditor can take care of creating a StoreEditor to coordinate updates at that level. Similarly, when an Employee is added to a Store, an instance of EmployeeEditor handles updates of the Treeview.