We are using a TreeView control in an MVVM C# WPF application.
I have successfully added Expand/Collapse All functionality to the View - iterating through all the Items in the TreeView, setting IsExpanded and then calling Focus on the TreeView to refresh the changes.
I have also successfully added functionality to clear the currently selected Item in the TreeView when clicking the empty space in the TreeView.
My current task and problem is to automatically select any newly added Items in the TreeView. The IsSelected and IsExpanded properties of the TreeViewItems are bound to matching properties in the ViewModel and setting IsSelected on the ViewModel works as long as the parent node is expanded.
I have tried setting IsExpanded on the parent ViewModel but as far as I am aware I need to then call Focus on the TreeView to force it to update and expand its representation of the tree. Since I am in the ViewModel, I have no connection to the TreeView without breaking MVVM.
How can I make this work?
Edit: Source added for pushpraj
The tree items are stored like this:
private ObservableCollection<NodeViewModel> _children = new ObservableCollection<NodeViewModel>();
public ObservableCollection<NodeViewModel> Children
{
get { return _children; }
set
{
_children = value;
OnPropertyChanged("Children", this);
}
}
The Children property of our root node is bound to the ItemsSource property of the TreeView. The child nodes are added programmatically to the Children property of the root node and each other.
The source I am working with is not my own and I am not overly experienced with WPF so please forgive me if I am giving you the wrong information. I have also noticed that we are using some Drag/Drop functionality in our TreeView from Gong Solutions. Could this be the cause of our problem? Setting IsExpanded to true in our ViewModel does not expand the entry in the tree view.
add the following style in the resources
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="FocusManager.FocusedElement"
Value="{Binding RelativeSource={RelativeSource Self}}" />
</Style>
this will enable every new TreeViewItem to focus itself when created
try it and see if this is what you are looking for
Related
We have a requirement to display the images in a GridView incrementally. So to find the selected items in GridView , IsSelected property of the GridView item has bind with corresponding binding objects property of CLR object (property of the GridView's ItemSource type). Since, UWP does not support RelativeSouce and setter binding in style, so after doing some search on internet we found the below code.
public class GridViewEx : GridView
{
protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var gridItem = element as GridViewItem;
var binding = new Binding { Mode = BindingMode.TwoWay, Source = item, Path = new PropertyPath("IsSelected") };
gridItem.SetBinding(SelectorItem.IsSelectedProperty, binding);
}
}
But it seems that there is a flaw with the above approach. Whenever the page is being scrolled down to load next set of photos then the previous selected items are losing their selection.
Is anyone has experienced this issue before or any suggestions to solve the above problem?
Move IsSelected to the model class and bind it to a user control. The user control actually is the cell and will be put in your data template.
<GridView.ItemTemplate>
<DataTemplate>
<controls:MyCustomControl IsSelected="{Binding IsSelected}"/>
</DataTemplate>
</GridView.ItemTemplate>
In MyCustomControl, you will handle difference visual state to show the selected status of item. This way your ViewModel dont have to "know" the list at all, when you need just get list of selected item from your list of models.
I've got a WPF MultiSelectTreeView (downloaded from here: http://unclassified.software/en/source/multiselecttreeview).
Now I want to control, which items the user selects. A simple example is that he shouldn't be able to select child nodes of different parents. But there are also more ViewModel-specific use cases.
It's easy to achieve this in code-behind of the Window by using the PreviewSelectionChanged event, checking the conditions directly and setting the Cancel-flag accordingly. But since I want to obtain the separation of View and ViewModel, I am looking for a way of doing this in my WindowViewModel.
Of course you could also extract the check to the ViewModel and call it from the view, but it looks wrong:
WindowViewModel _viewModel;
void PreviewSelectionChanged(object sender, PreviewSelectionChangedEventArgs e)
{
e.Cancel = !this._viewModel.CanSelect(e.Item as TreeItemViewModel);
}
I hope that anybody has an idea.
- timnot90
Typically, when data binding a hierarchical collection to a TreeView in WPF, the custom data items should have an IsSelected property defined in their class. If they do, then it can be data bound to the IsSelected property of each TreeViewItem:
<TreeView ItemsSource="{Binding YourCollection}" ... >
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
When this is done, you can just set that property to true to select an item and to false to deselect an item.
// Select Item
dataObject.IsSelected = true;
// Deselect Item
dataObject.IsSelected = false;
You can add a handler to the PropertyChanged event of each item to detect when the IsSelected property changes (if they implement the INotifyPropertyChanged interface as expected).
I have model objects set up like so:
public class Model
{
public ObservableCollection<Model> Children{get;set;}
public string Name{get;set;}
}
This gives me a tree of model objects of arbitrary size and depth. I display these in a WPF TreeView using a hierarchical datatemplate. Because of the ObservableCollection, I can add model objects anywhere in the model tree and the UI will update accordingly.
The annoyance comes because even though a new model object might be added at the third level, for example, it might not be immediately visible.
When a new model object is added anywhere in the tree, I would like the TreeView to automatically select the new node, and expand its parents to that it is immediately visible to the user. This doesn't seem to be immediately obvious.
What I don't want to do is pollute the Model objects with properties which only make sense in a particular WPF control. Nor do I want to add a parent property to the Models.
It seems that this must have been tackled before by someone. Does anyone have an idea on how to tackle this?
Create 2 properties in your model as below
IsExpanded and IsSelected
and in your treeview ItemContainerStyle add the below setters
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
<Setter Property="IsExpanded" Value="{Binding IsExpanded}"/>
Noe everytime you set these 2 properties in your viewmodels, the corresponding treeviewitem will be selected/expanded
In WinRT App (C#) I have List<Item> items, which bind to ListBox.
Class Item has 2 fields: string Name and bool IsSelected. As you already understood, I want to bind IsSelected field to IsSelected Property of ListBoxItem.
Why I need this? Why I didn't use SelectedItems property of my ListBox?
When ListBox just loaded, I already have some Items, which must be IsSelected = true
I don't want to create another collection to store all selected items.
What I'm looking for?
I'm looking for elegant solution, like in WPF:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
But we all know, that WinRT doesn't support bindings in setters at all.
I also check nice post in Filip Skakun blog - and this is one of solution, but I need to write some of the BindingBuilder/BindingHelper by my self.
And now, I know two way to solve my problem:
Bind SelectedItems property of ListBox and store another collection of items. - I do not like this way
Do it like Filip Skakun - if I find nothing I use this.
In ideal situation I want to use native solution for this, or maybe someone already wrote/tested nested BindingBuilder for my situation - it's will be helpful too.
How about creating a derived ListBox:
public class MyListBox : ListBox
{
protected override void PrepareContainerForItemOverride(
DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
if (item is Item)
{
var binding = new Binding
{
Source = item,
Path = new PropertyPath("IsSelected"),
Mode = BindingMode.TwoWay
};
((ListBoxItem)element).SetBinding(ListBoxItem.IsSelectedProperty, binding);
}
}
}
I build an MVVM pattern TreeView with
-Root
--Item
---Subitem
When clicking on any of the TreeViewItems, I would like to display the details of the actual Object (Model) in an separate Window.
But I'm not sure how to access the data of the object.
private void TreeView_OnSelectedItemChanged(object sender, RoutedEventArgs e)
{
TreeViewItem tvi = e.OriginalSource as TreeViewItem;
MessageBox.Show(tvi.ToString());
}
I would not recommend of using TreeView_OnSelectedItemChanged in MVVM styled WPF applicaiton.
Define on on your ModelView binded a binding to IsSelected property of TreeeViewItem and you wil be always aware of selection,a nd can select the item of interest from the code, as well.
My prior answer was addressing more than what was asked.
Since you want to react on selection changing in the TreeView by displaying the details of the TreeViewItem's bound object, you could use Caliburn Micro's Action mechanism. You can hook up the SelectedItemChanged event of your TreeView to a method in your ViewModel.
For Example in your View:
<TreeView
ItemsSource="{Binding YourDataObjects}"
cal:Message.Attach="[Event SelectedItemChanged] = [Action OnSelectedItemChanged($this)]"/>
And in your ViewModel you will have this method:
public void OnSelectedItemChanged(YourDataObject selectedItem)
{
//Do something with the selected item here
}
If you have problems setting this up let me know.
In an MVVM pattern, the data associated to a a control is supposed to be in the DataContext dependency property.
In your ViewModel, create a dependency property of type TreeViewItem, and in the View bind the SelectedValuePath property of the TreeView to your new dependency property.