Shifting nodes up and down in a TreeView - c#

In my program I have a UserControl that contains a TreeView. That TreeView has a ViewModel and a Model relating to it. I would like to make it so that by clicking buttons, I can shift nodes up and down throughout the tree. This is similar to what one might implement on a listBox.
As a guide, I am using this article.
I am implementing the following functions into the code-behind of the UserControl for which the TreeView exists.
//Move up
private void moveUp_Click(object sender, RoutedEventArgs e)
{
if(UCViewModel.TreeView.SelectedItem != null)
{
if(UCViewModel.TreeView.SelectedItem is TreeModel)
{
TreeModel tm = UCViewModel.TreeView.SelectedItem as TreeModel;
if(tm.Rank != 1)
{
}
}
}
}
private void MoveUp(TreeModel tm)
{ //My guess on how to call the equivalent command...
foreach (TreeModel item in // **UCViewModel.TreeView.GetAllChildren....? )
{
}
}
Because my structure is different, and I am actually implementing an ObservableCollection as a TreeView, I do not have access to the same methods as the code in the example.
The following lines are the lines that I am concerned about...
TreeView.Items();
TreeView.Items.Clear();
TreeView.Items.Add();
How can I make the equivalent calls with the way my TreeView is setup? Please let me know if more code would be helpful.

The main idea of MVVM is not to use anything like treeView.Items.Add() or treeView.GetAllChildren() or whatever method you need from TreeView.
MVVM Pattern says you dont care about View and you dont know about the View or any control inside the View.
Therefore if you have an ObservableCollection as ItemsSource in your ViewModel you just need to move items there and the TreeView will follow you.
As simple as that. Your TreeView just needs to know where the ObservableCollection is placed inside your ViewModel.
Whenever you change something inside ObservableCollection you trigger collection changed event with appropriate event arguments holding information whether you added new items or shifted items around. That is how TreeView will know what to do.

Related

Accessing UserControl when it's a DataTemplate in a ListBox

Fist time question long time user of Stack Overflow. I'm somewhat new to the whole MVVM space so I think my Google Fu is failing me. I apologize if I get some of these terms incorrect.
I currently have an observable list class:InstrumentExecutor.
This list is then set as the ItemsSource of a ListBox called InstrumentList.
The ListBox is setup to use a DataTemplate with a FrameworkElementFactory that uses the InstrumentControl UserControl to display the data.
DataTemplate code:
DataTemplate instrumentDataTemplate = new DataTemplate();
instrumentDataTemplate.DataType = typeof(InstrumentExecutor);
FrameworkElementFactory frameelement = new FrameworkElementFactory(typeof(InstrumentControl));
instrumentDataTemplate.VisualTree = frameelement;
InstrumentList.ItemTemplate = instrumentDataTemplate;
And that is all working just great! Data is flowing in and out and everyone is happy. Here's the part where I'm having trouble.
In the user control there is some Expander WPF code. I am wanting to send an event, call a method, do something to trigger the expansion or collapse from the parent ListBox to each and every item in this listbox. Whenever I iterate the InstrumentList.Items the data source (InstrumentExecutor) is the only data I see.
There's some ways I can hack around using properties and notifications in the data class (Instrument Executor) but I'd like to see if I can make it cleaner.
So is there a way I can access each UserControl? I've looked at ItemContainerGenerator but haven't had much luck.
Thanks for the help!
Update 1: Using the VisualTreeHelper idea and some StackOver during the usercontrol load event for each line item I can find the parent (code below) and build a linkage that way. Here's the code that goes with this idea.
public static T FindParent<T>(DependencyObject current) where T : class
{
DependencyObject parent = VisualTreeHelper.GetParent(current);
if (parent == null)
return null;
if (parent is T)
return parent as T;
else
return FindParent<T>(parent);
}
It works but it's not as sexy as I'm used to. Next up is trying the Binding idea. Thanks!

How to programmatically click on a node?

I'd like to programmatically emulate a click on a node in a TreeView control. There's no clickable method as far I can see (something corresponding to other controls) and I guess that I need to go for the currently selected node.
So I've tried the following:
christmasTreeView.SelectedNode. ???
However, intellisense gave me no hint on what to call in order to fire a clickety-click on the node. How can it be done?
You can do something like:
// find the node you want to select and make it the SelectedNode
christmasTreeView.SelectedNode = christmasTreeView.Nodes[1]; // <-- the index you need
// Now trigger a select
christmasTreeView.Select();
// or
//christmasTreeView.Focus();
This will fire:
private void christmasTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
// awesome
}
Possible approach (not very smooth, though).
TreeNode preSelected = ChristmasTreeView.SelectedNode;
ChristmasTreeView.SelectedNode = null;
ChristmasTreeView.SelectedNode = preSelected;
ChristmasTreeView.Select();
Your main issue is that a Windows Forms TreeNode does not derive from a Control like a TreeView does (or, for example, a Button). It's much closer to a "model" class, meaning that it's primarily concerned with the hierarchical organization of your data. Although some of the presentational abstraction is leaked in properties like Color, Bounds, Handle and similar, a TreeNode doesn't know how to paint itself, nor how to handle click events.
On the other hand, a TreeView is an actual Control, meaning you can derive from it and be able to override its protected OnClick method, like shown in the example you linked.
If you want to follow that path, you could create your derived TreeView class from it and override the protected OnNodeMouseClick method. This method is specific to the TreeView and called by its WndProc method when a certain node is clicked.
But having read your comments to other answers, it seems that this is not what you really need to do to accomplish your goal.
You need to use event handler for TreeView.NodeMouseClick.
This Event got parameter which You can call in Your EventHandler like below:
void MyTreeview_NodeMouseClick(object sender,
TreeNodeMouseClickEventArgs e)
{
// do something with e.Node
}

When to separate View from ViewModel?

So I'm working on a GUI and most of it I implemented with 1 window and used the code-behind for that window to handle most of the logic. The program is very GUI driven. Say there is a combo box, if you select something from the combo box, the data drastically changes and all the other GUI boxes/labels/grids change or clear ect ect.
I'm doing a lot of refactoring and I've been aware of MVVM, but I've never really seen the need for it. I understand what and why its used, but functionality its just easier to reference all the GUI components straight from the code behind I've found.
For example...
In my main window I have
<ComboBox x:Name="MyTitlesUI" ItemsSource="{Binding Titles}" SelectionChanged="MyTitlesUI_SelectionChanged">
So the ComboBox is tied to a List Titles in my MainWindowViewModel right?
Where should MyTitlesUI_SelectionChanged event go? It needs to go in the View correct? But what if the functionality of SelectionChanged has to do with data inside MainWindowViewModel?
Say you change the selection in MyTitlesUI and now the program has to look up up that Title string in a database. All of that database functionality is in DBClass which you declare in MainWindowViewModel. How do you access that functionality? Why would you have to do this:
In main window cs:
private void MyTitlesUI_SelectionChanged(object sender, EventArgs e)
{
viewModel.ConnectToDataBase((string)MyTitlesUI.SelectedItem);
}
In MainWindowViewModel.cs
private SelectedTitle;
public void ConnectToDataBase(string title)
{
SelectedTitle = title;
DBClass myDB = new DBClass(SelectedTitle);
.... //do stuff with myDB
}
That just seems kind of unnecessary no? This is just a mild mild example of course and maybe that seems pretty clean. But if you're doing really complex back and fourth between View and ViewModel, the reference to MyTitlesUI.SelectedItem in View may be needed in ViewModel for other functions to work hence the SelectedTitle private variable.
Now you have more assignments, more variables, more functions that just call other functions than just a simple MyTitlesUI.SelectedItem to deal with.
Why not bring the DBClass reference up to the View or similar?
Especially if you're doing a lot of UI manipulation that the information inside your ViewModel will be playing with. Say once I change the selection of Title, I need graph to clear. But my graph can't clear until my ViewModel has connected to the DB or something.
I'm going to have graphs or grids defined in my View that depend on dynamically created data in my ViewModel that needs to update. And I'm trying to wrap around what needs to be in View and what needs to be in ViewModel. It seems to be not proper to reference View from ViewModel, so something like MyTitlesUI.SelectedItem can't be called in ViewModel.
EDIT:
So going back to the Selected Item example, say I have a Treeview UI item. I want to bind that to a Treeview that I don't have yet. I create the data for it procedural with DB connect. So the user selects from the combobox the Title they want. Db Connect then creates, asynchronously, a TreeviewItem in some kind of data structure.
private SelectedTitle;
public void ConnectToDataBase(string title)
{
SelectedTitle = title;
DBClass myDB = new DBClass(SelectedTitle);
if(myDB.doneWorking)
{
myTreeView.ItemsSource = myDB.GetTree();
}
}
but functionality its just easier to reference all the GUI components
straight from the code behind I've found
Wrong. MVVM delivers a clean, Property-based approach that's much easier to work with than the txtPepe_TextChanged() winforms-like approach. Try to change the Text for a TextBlock buried deep inside a DataTemplate that is used as the ItemTemplate of a Virtualized ItemsControl using code behind... WPF is not winforms.
Where should MyTitlesUI_SelectionChanged event go?
Nowhere. MVVM works best with a property/DataBinding based approach, as opposed to a procedural event-based approach.
For instance, a ComboBox-based UI that "does stuff" when the user changes the selection in the ComboBox should be defined like this:
<ComboBox ItemsSource="{Binding MyCollection}"
SelectedItem="{Binding SelectedItem}"/>
ViewModel:
public class ViewModel
{
public ObservableCollection<MyItems> MyCollection {get;set;}
private void _selectedItem;
public MyItem SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
NotifyPropertyChanged();
DoStuffWhenComboIsChanged();
}
}
private void DoStuffWhenComboIsChanged()
{
//... Do stuff here
}
}
Now you have more assignments, more variables, more functions that
just call other functions than just a simple MyTitlesUI.SelectedItem
to deal with.
Wrong. What you have now is a Strongly Typed property in the ViewModel of type MyItem that you work with instead of the horrible casting stuff (MyItem)ComboBox.SelectedItem or things of that sort.
This approach has the additional advantage that your application logic is completely decoupled from the UI and thus you're free to do all sorts of crazy stuff on the UI (such as replacing the ComboBox for a 3D rotating pink elephant if you wanted to).
Why not bring the DBClass reference up to the View or similar?
Because DataBase code does NOT belong into the UI.
Especially if you're doing a lot of UI manipulation
You don't do "UI manipulation" in WPF. You do DataBinding which is a much cleaner and scalable approach.

Change TreeListNode Index (Position) in DevExpree TreeList Control

I'm using DevExpree XtraTreeList Control, I want to randomly Set one of the first levels nodes to be the first node in the Tree, nothing helpful shown in the TreeList Control's Methods nor in the TreeListNode Methods,
Please Advice.
Edit: My Code
private void btnSetMaster_ButtonClick(object sender, DevExpress.XtraEditors.Controls.ButtonPressedEventArgs e)
{
//Load reprot
if (treeLstRprtDS.FocusedNode != null)
{
treeLstRprtDS.SetNodeIndex(treeLstRprtDS.FocusedNode,0);
//Get selected underlying object
ReportDataSource rprtDataSourceSelected =
(ReportDataSource)treeLstRprtDS.GetDataRecordByNode(treeLstRprtDS.FocusedNode);
theReport.SetReportDataSourceAsMaster(rprtDataSourceSelected);
}
}
Edit:
Note: working on bound mode
Solution:
I implemented the CompareNodeValues Event for the XtrTreeList Control
Read here...
and then forced the tree to do sorting using Column.SortIndex Read here...
Do you wish to scroll the TreeList so that a certain node to be the top one? If so, use the TreeList's TopVisibleNodeIndex property. If you need a certain node to be the first one, you should sort the TreeList within its CompareNodeValues event handler.
It sounds like you're looking for the SetNodeIndex method.

Is there an easy way to associate an event with a ListViewItem?

I have a WinForms ListView, obviously containing ListViewItems. I'd like to be able to attach a click event to each item, instead of to the entire ListView (and then trying to figure out what item was clicked). The reason for this is that I need to perform a different action based on which item was selected. The ListViewItem class seems to be very limited in this regard. Is there any way to do what I want, or am I forced to use the ListView.Click event?
I would still use the ListView Click event.
A trick I've used in these situations is to use the Tag property of a ListViewItem. It's great for storing per item data and you can put anything in it.
It may make sense to subclass ListViewItem and use virtual dispatch to select the appropriate behavior based on the selected ListViewItem in the appropriate ListView event.
E.g. (uncompiled)
public abstract class MyItems : ListViewItem
{
public abstract DoOperation();
}
public class MyItemA : MyItems
{
public override DoOperation()
{ /* whatever a */ }
}
public class MyItemB : MyItems
{
public override DoOperation()
{ /* whatever b */ }
}
// in ListView event
MyItems item = (MyItems)this.SelectedItem;
item.DoOperation();
As others have mentioned, it may also make sense to use the appropriate Tag property. Which technique you go for really depends on what your action is (and therefore where it belongs, architecturally). I assumed the subclass made more sense because you're looking for a click on a listview item, and that (to me) seems more likely to be presentation-layer b/c you're overriding some standard control behavior (which would normally just select an item) as opposed to doing something in response to behavior.
In most use cases, a ListViewItem is a representation in the UI of some object, and what you're trying to do is execute a method of the object that the ListViewItem represents when the user clicks on it. For the sake of simplicity and maintainability, you want as few things to sit between the user's mouse-click and the actual method being executed.
You can store the object in the ListViewItem's Tag property and then reference it in the Click event handler, but that results in code that's got some inherent weak points:
private void MyListView_Click(object sender, EventArgs e)
{
ListView l = (ListView)sender;
if (l.SelectedItem != null)
{
MyClass obj = l.SelectedItem.Tag as MyClass;
if (obj != null)
{
obj.Method();
}
}
}
That's a lot of casting and null-reference checking. And the really weak thing about this code is that if it turns out that Tag is null, or contains something other than a MyClass object, you don't really know where to look to find out where the problem is occurring.
Contrast it with code like this:
private void MyListView_Click(object sender, EventArgs e)
{
MyClass.ListViewClicked(sender as ListView);
}
When you're maintaining this code, you don't know how that ListViewClicked method is implemented, but at least you know where to look for it - in MyClass. And when you do, you'll see something like this:
public static void ListViewClicked(ListView listView)
{
if (listView.SelectedItem == null)
{
return;
}
if (ListViewItemLookup.ContainsKey(listView.SelectedItem))
{
ListViewItemLookup[listView.SelectedItem].Execute();
}
}
Well, that's interesting. Following the thread, how does that dictionary get populated? You find that in another method in MyClass:
private static Dictionary<ListViewItem, MyClass> ListViewItemLookup =
new Dictionary<ListViewItem, MyClass>();
public ListViewItem GetListViewItem()
{
ListViewItem item = new ListViewItem();
item.Text = SomeProperty;
// population of other ListViewItem columns goes here
ListViewItemLookup.Add(item, this);
return item;
}
(Reasonable people can disagree about whether or not it's appropriate for a class to be so closely tied to a specific form of its representation in the UI - there are those who would isolate these methods and this dictionary in a helper class instead of in MyClass itself, and depending on how hairy the rest of the problem is I might do it too.)
This approach solves a number of problems: it gives you a simple way of handling the ListView's Click event properly, which is what you asked for. But it also isolates the not-always-trivial process of creating the ListViewItem in the first place. It reduces the amount of code you'll have to move around if you refactor your form and move the ListView to another form. And it reduces the number of things that your form class needs to know about, which is generally a good thing.
Also, it's testable. Generally, the only way to test code in a UI event handler is through the UI. This approach lets you isolate all of the logic surrounding this part of the UI in something that you can unit test; the only thing you can't write a unit test for is a single line of code in the form.
I should point out that the other approach people have been suggesting - subclassing ListViewItem - is perfectly fine too. You put the logic I put in the GetListViewItem method in the class's constructor, make the MyClass instance a private property of the class, and expose a Click method that calls the method of MyClass. Pretty much the only reason I don't like it is that it still leaves you with a fair amount of code in your form that you can't really unit test:
ListView l = (ListView)sender;
if (l.SelectedItem != null)
{
MyClassListViewItem item = l.SelectedItem as MyClassListViewItem;
if (item != null)
{
item.MyClass.Method();
}
}
You might however have luck sticking a reference to a delegate or other handler in the tag field (assuming there is a tag property of a ListViewItem). You would still have to determine which ListViewItem is clicked, but you could then go straight to the tag instead of another decision structure.
You want to create a new class (or classes if there are various types), which inherits from ListViewItem, then populate your ListView with these objects (as long as they inherit from listview (even several levels of inheritence) The ListView control will take them).
Then add a click method to your custom class(es) and on the ItemClick event of your listView, just call the click method of the clicked item. (some casting may be needed)
Actually there is no way to use a ListViewItem. You have to use the ListView itself. By using the 'SelectedItems' property of the ListView you can access the selected ListViewItems.
One option is to override the ListViewItem class an implement the specific stuff in there. Then you can cast the selected item to the overridden one and perform the action.
I really don't understand the reason to do so instead of just using the regular ListView Click event, but if I were to do like you suggest I would assign an EventHandler delegate to the Tag property of each ListViewItem, then in the ListView Click event handler I would check if the ListViewItem.Tag <> null, and if so call the delegate.

Categories

Resources