C# WPF How do I get a TreeViewItem from HierarchicalDataTemplate item? - wpf

I have the same problem as a previous question on this forum:
previous thread
I have a TreeView which uses a HierarchicalDataTemplate to bind its data. I need to get the TreeViewItem from the selected item, which is my own class.
I have tried the solution given in the previous thread as well as modified versions of it. Nothing works. myTreeView.Items.CurrentPosition returns -1. If I check inside Items I can only see my 2 root items. I have several levels of items.
myTreeView.ItemContainerGenerator.ContainerFromObject(myTreeView.SelectedItem) doesn't work either, it returns null.
myTreeView.ItemContainerGenerator.ContainerFromObject(myTreeView.Items.CurrentObject(myTreeView.Items.CurrentItem) returns null.

this is what you need:
private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
object entity = null;
entity = e.NewValue;
}

Related

how to add\remove multiple items from one listview to another in MVVM using wpf?

I have two listviews. One of left handside and another on right hand side. I have two buttons to add and remove items from the two listviews.
LHSListview is bound to List and RHSListview is bound to List. Column class has two variables 'order' and 'Id'.
when I click on the add button all the selected items from LHSListview must move to RHSListview. And vice versa when clicked on remove button.
This is what I am trying to do on the click of add button
var list1 = new ArrayList(lstAllFields.SelectedItems);
foreach (var item in list1)
{
lstAllFields.Items.Remove(item);
SelectedFields.Items.Add(item);
}
But this throws an error on lstAllFields.Items.Remove(item); this line saying "Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead."
You mentioned you're using MVVM, so you probably know that you shouldn't be changing the items from the ListViews inside the view. What you should do is modify the collection you are bound to in the ViewModel.
Problem is it's kind of tricky to get the multiple selections in MVVM, because the SelectedItems property isn't a Dependency Property.
There are 2 ways to achieve what you're after, both support MVVM:
The shorter and easier way is to listen to the Button_Click in the View's CodeBehind, create a new list of the selected items and pass it to the VM to do the logic of adding and removing items.
So a short version would look like this:
Code Behind:
private void MyButton_OnClick(object sender, RoutedEventArgs e)
{
List<MyObject> mySelectedItems = new List<MyObject>();
foreach (MyObject item in listview1.SelectedItems)
{
mySelectedItems.Add(item);
}
(this.DataContext as MainVM).MoveMethod(mySelectedItems);
}
View Model (in my class I called it MainVM)
public void MoveMethod(List<MyObject> selected)
{
foreach (var item in selected)
{
List1.Remove(item);
List2.Add(item);
}
}
That's it. Just remember, the List1 and List2 (which are the ItemSource's that ListView1 and ListView2 bind to, must be ObservableCollection to see the update in the UI.
I promised a longer option too, for that see the great 3-part blog post on the subject:
MVVM and Multiple Selection – Part I
MVVM and Multiple Selection – Part II
MVVM and Multiple Selection – Part III

Finding the TextBlock that is part of the default control template ComboBox generated through code

I'm trying to find the TextBlock that is inside the control template of a comboBox.
using VisualTreeHelpar.GetChildrenCount is working only if the comboBox is declared in XAML.In that case GetChildrenCount returns 1 and a recursive search is possible.
However, if I declare the combo as a member of the Window class using code, allocated and setting it to its place, the function GetChildrenCount return 0. When I run snoop in this scenario It shows the combo children hierarchy. I want to be able to search the comboBox just as snoop does.
Any help would be appreciated.
code:
ComboBox mCombo = null;
private void Windows_Loaded(object sender, RoutedEventArgs e)
{
mCombo = new ComboBox;
mGrid.Children.Add(mCombo);
Grid.SetRow(mCombo,0);
int count = VisualTreeHelpar.GetChildrenCount(mCombo);
}
Call the ApplyTemplate method of ComboBox. Then, you should be able to find what you need.

Maintain scroll position on updating the ItemSource of a silverlight datagrid

I'm using a DataGrid in my silverlight application to display some data that's refreshed on a timer. My problem is that when this happens the vertical scrollbar in the grid resets to the top, whereas I want it to stay in the same position. Does anyone know how I can make this happen?
I've tried overriding the ItemsSource property on the grid to store the vertical scroll position and then reset it, but this only affects the scrollbar and doesn't force the correct rows to be displayed. Is there a way to force this behaviour?
Here is a similar question about Setting the scroll bar position on a ListBox
After rebinding Silverlight Listbox control how do you get it listbox to scroll to back to the top?
Since the DataGrid also supports a ScrollIntoView method, you should be able to use a similar technique such as
theDataGrid.ItemsSource = data;
theDataGrid.UpdateLayout();
theDataGrid.ScrollIntoView(theDataGrid.SelectedItem, theDataGrid.Columns[0]);
I couldn't find a decent answer last time I looked. I wanted to keep the current element selected in the grid but that wouldn't work on an ICollectionView refresh (I use MVVM and get automatic updates from the server).
ScrollIntoView() was not an option for me because the currently selected item may NOT be in view. Having the CurrentChanged event firing out of control was also quite a bother.
In the end, I used the Infragistics grid and it does just that out of the box. Problem solved for me.
You may have a look at the DevExpress free grid. I think it had the same nice behaviour (I tested it but I can't remember the outcome).
You could try setting the SelectedItem thro the UI thread, so that the UI can refresh itself,
like so
private void Button_Click(object sender, RoutedEventArgs e)
{
Person p = new Person() { Name="sss",Age=11}; //datagird's itemsSource is Collection<person>
people.Add(p);
dg.SelectedItem = p; //dg is my datagrid name
Dispatcher.BeginInvoke(() => { dg.SelectedItem = p; });
}
Im assuming that new rows are loaded thro the ViewModel, so thats why it makes sense to place the BeginInvoke there. Since the ViewModel operations run on a different thread, and just setting the SelectedItem on its own might not work, this has worked for someone else
I've also had issues with this. I solved it by remembering the item I want to scroll to, then re-binding the DataGrid. I handle the LayoutUpdated event in order to implement the desired functionality:
void MyDataGrid_LayoutUpdated(object sender, EventArgs e)
{
// Reference the data item in the list you want to scroll to.
object dataItem = yourDataItem;
// Make sure the item is not null and didn't already scroll to the item.
if (dataItem != null && this.dataItemScrolledTo != dataItem)
{
// Remember the item scrolled to.
this.dataItemScrolledTo = dataItem;
// Scroll datagrid to the desired item.
MyDataGrid.ScrollIntoView(dataItem, MyDataGrid.Columns[0]);
}
}
I've modified CodeMaster's solution so that you don't need a class level variable. Put this code in the method that updates the ItemsSource. It will dynamically create the eventhandler, attach it, then detach it.
EventHandler MyDataGrid_LayoutUpdated = null;
MyDataGrid_LayoutUpdated = (s, e) =>
{
MyDataGrid.ScrollIntoView(dataItem, MyDataGrid.Columns[0]);
MyDataGrid.LayoutUpdated -= MyDataGrid_LayoutUpdated;
};
MyDataGrid.LayoutUpdated += MyDataGrid_LayoutUpdated;

C# WPF - Adding a child node to a selected node in treeview

In WPF treeview control, I need to add a child node to a parent node i select using mousedoubleclick event.
http://msdn.microsoft.com/en-us/library/system.windows.controls.treeview.selecteditem.aspx
I followed the step in the MSDN, but i get invalidCastException when i do this.
TreeViewItem newChild =
(TreeViewItem)treeView1.SelectedItem;
How can i solve this?
Thanks
SelectedItem returns the selected data item, not the visual representing it.
If you need to access the selected TreeViewItem, use the ItemContainerGenerator :
TreeViewItem item = treeView1.ItemContainerGenerator.ContainerFromItem(treeView1.SelectedItem) as TreeViewItem;
Not sure it works for nested items though... you might have to use the ItemContainerGenerator of the parent TreeViewItem, which wouldn't be very convenient
EDIT: just tested, indeed it only works for root nodes...
Anyway, the best way to add a node is to use bindings and HierarchicalDataTemplates. You just need to add the object to the data source, and the corresponding TreeViewItem will be added automatically (provided the containing collection implements INotifyCollectionChanged...)
What type of Items do you Add() to the Tree? The same type will be returned.
If it is mixed, use
TreeViewItem newChild = treeView1.SelectedItem as TreeViewItem;
if (newChild != null) { ... }

Event handler that will be called when an item is added in a listbox

Is there an event handler that will be called when an item is added in a listbox in WPF?
Thanks!
The problem is that the INotifyCollectionChanged interface which contains the event handler is explicitly implemented, which means you have to first cast the ItemCollection before the event handler can be used:
public MyWindow()
{
InitializeComponent();
((INotifyCollectionChanged)mListBox.Items).CollectionChanged +=
mListBox_CollectionChanged;
}
private void mListBox_CollectionChanged(object sender,
NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
// scroll the new item into view
mListBox.ScrollIntoView(e.NewItems[0]);
}
}
Ref.
Josh's advice about the observable collection should also be considered.
Take a different approach. Create an ObservableCollection (which does have such an event) and set the ItemsSource of the ListBox to this collection. In other words, in WPF you should think about the problem differently. The control isn't necessarily what is being modified ... the collection behind it is.
UPDATE
Based on your comment to Mitch's answer which indicates your binding source is actually an XML document, I suggest looking into hooking up to the XObject.Changed event of the XML document/element/etc. This will give you change information about the XML structure itself - not the ItemCollection which is an implementation detail you shouldn't need to consider. For example, ItemCollection (or any INotifyCollectionChanged) doesn't guarantee an individual event for every change. As you noted, sometimes you'll just get a generic reset notification.

Resources