I have an object MyResult that contains a list "List MyItems".
I would like to add this to a c# WPF TreeView.
What would be the best way to do so?
Is there a step-by-step tutorial for this? Since I'm getting confused with all the MVVM Classes etc.
Thanks
I'm assuming your objects in your list have some sort of list.
if thats the caseyou should look at using the hierarchicaldatatemplate.
a simple example might be something like the following. This is from a segment I used with a radtreeview but it should work the same.
<!-- xaml -->
<UserControl.Resources>
<Style x:Key="_treeViewItemStyle" TargetType="telerik:RadTreeViewItem">
<Setter Property="Visibility" Value="{Binding IsVisible, Converter={StaticResource VisibilityConverter}}" />
<Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
<UI:HierarchicalDataTemplate x:Key="_treeViewTemplate"
ItemsSource="{Binding NodeItems}"
>
<TextBlock Margin="3,0,0,0" Text="{Binding Header}">
</TextBlock>
</UI:HierarchicalDataTemplate>
</UserControl.Resources>
<telerik:RadTreeView x:Name="_pageNavigator" ItemsSource="{Binding InspNavList}" ItemDoubleClick="SendFocusToPage" SelectedItem="{Binding SelectedNavItem, Mode=TwoWay}" ItemTemplate="{StaticResource _treeViewTemplate}" ItemContainerStyle="{StaticResource _treeViewItemStyle}" />
You can use
treeview.ItemsSource = MyList.
Here is a tutorial how to do it using MVVM.
There's no best way to do things. Well, actually there is, but you need a bit more data about the system itself and the current situation (as well as some profound knowledge) to know the best way to do things. Well, putting that aside.
If you'd like to use binding, you could do the following:
a. In your page/window/usercontrol set DataContext property to point to your object (MyResult).
b. In your XAML file use following snippet to bind treeView items to that list:
<TreeView ItemsSource={Binding MyItems}>
....
</TreeView>
c. Enjoy the result.
There are several things you need to consider, though:
1. You should implement DataTemplate for your MyItems objects. Default implementation would just take ToString() result to put into the tree view.
2. If you'd like to use hierarchical data (meaning the one that has levels), you should implement HierarchicalDataTemplate and point where to get children for every node in the tree.
3. You should consider using ObservableCollection for correct binding - this way every addition/deletion of an item in the list would invoke changes in the UI.
Here are several links to get you started: first, second, third.
Related
Trying to change one button to other, I have stuck in a problem with commands handling. It is difficult to describe its source, because nearly all are the same, but when I pass the command via setter it is not working.
My previous code (it is working like a charm):
it is just a split button that passes its menu item header as parameter to executing command.
<xctk:SplitButton.DropDownContent>
<ItemsControl ItemsSource="{Binding Instance.InstanceVM.Names}" x:Name="NamesCtr">
<ItemsControl.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding}" Command="{Binding ElementName=NamesCtr, Path=DataContext.Instance.InstanceVM.Load}" CommandParameter="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</xctk:SplitButton.DropDownContent>
And here is for my dropdown button:
<controls:DropDownButton.DropDownContextMenu>
<ContextMenu x:Name="NamesCtr" ItemsSource="{Binding Instance.InstanceVM.Names}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Template" Value="{StaticResource DropDownMenuItemTemplate}"/>
<Setter Property="Header" Value="{Binding}"/>
<Setter Property="Command" Value="{Binding ElementName=Namestr, Path=DataContext.Instance.InstanceVM.Load}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</controls:DropDownButton.DropDownContextMenu>
This shows a button, a dropdown with correct menu items (so the source, template and header were bound correctly, but the command does not hit its function)
Found a solution here :
How do you bind a command to a MenuItem (WPF)?
As you are binding through a static singleton, you can modify the Binding like this:
<Setter Property="Command" Value="{Binding Source={x:Static <xmlnamespace>:<yourType>.Instance}, Path=InstanceVM.Load}"/
Don't forget to replace the placeholders with valid values, i.e. with an xml namespace prefix that corresponds to the appropriate CLR namespace and the type declaring the Instance property.
Please note that ElementName bindings and RelativeSource bindings are less trustworthy if placed in DataTemplates / Styles and also in case of using Popups in your visual tree.
I'm attempting to make a TreeView which uses an ItemTemplate, and also is bound to a view model which has a IsSelected property.
I'm not exactly sure why this isn't working, so I apologize if the question seems vague.
I've also reduced the code to what I think is the problem, as my app has a lot more XAML, so I hope what I've included is sufficient.
Here's how I define my DataTemplate:
<DataTemplate x:Key="ElementDataTemplate">
<Label Content="{Binding Path=DisplayText}"></Label>
</DataTemplate>
Here's how I use it:
<TreeView ItemsSource="{Binding Elements}"
ItemTemplate="{DynamicResource ElementDataTemplate}">
<TreeView.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected"
Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.Resources>
</TreeView>
My ViewModel has DisplayText and IsSelected properties.
I know that I'm binding correctly to the ViewModel because I am seeing the same number of items as in my Elements property of my data context, and my Label's Coutent is properly being set to DisplayText- I've verified this through breakpoints on the getter and visually when I run the app.
However, the IsSelected property's getter on the ViewModel never gets called (never hits a breakpoint), so it's clear I've messed up something on the binding of the IsSelected property.
I should point out that when I run the application I can select items with the mouse and they reflect the selection visually, so the TreeView item itself is getting selected, it's just not binding to the ViewModel's IsSelected property. Any help would be much appreciated!
Ahh, I'm foolish! I spent forever trying to figure out what it was, came here after a few hours, then finally figured it out:
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</TreeView.ItemContainerStyle>
I was using "ListBoxItem" instead of "TreeViewItem". Copy/paste got the best of me.
I have the following setup:
<ListBox ItemSource="{Binding Targets}">
<ListBox.ItemTemplate>
<DataTemplate>
<view:ViewName />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
What I am trying to accomplish is to dynamically decide which view to use at runtime, based on a property within the DataContext of the ListBox. In simple terms, I want to replace <view:ViewName> with a data binding that returns the proper view.
I use MEF to provide plug-ins for my app that may need to provide a custom view to display the items when appropriate. At design time I won't know all the possible view types (they may be dynamically loaded from a DLL) so a simple DataTemplateSelector won't do.
I have researched solutions but have come up empty.
Since you want to change templates based on a bound value, you can use a DataTrigger to determine the ContentTemplate of the ListBoxItem
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}"/>
<Style.Triggers>
<DataTrigger Property="{Binding SomeProperty}" Value="A">
<Setter Property="ContentTemplate" Value="{StaticResource TemplateA}"/>
</DataTrigger>
</Style.Triggers>
</Style>
I find this better than using a DataTemplateSelector because it gets re-evaluated if the bound property changes, while a DataTemplateSelector does not.
If you want to change templates based on an object type, you can use Implicit DataTemplates. These are DataTemplates that define a DataType, but no x:Key, and they will be used anytime WPF tries to draw an object of the specified type.
For example, if you had this template defined in your <X.Resources> somewhere
<DataTemplate DataType="{x:Type models:ActionA}">
<views:ActionAView />
</DataTemplate>
you could then insert your Model object directly into the UI and WPF would draw it using the template you specified
<ContentControl Content="{Binding SomeIActionObject}" />
<ItemsControl ItemsSource="{Binding CollectionOfIActionObjects}" />
Update
You mentioned that you would be allowing users to create modules with additional Templates that get imported using MEF, so in that case you would probably be better off using an IValueConverter that look up the matching template within Application.Resources
For example, if the bound value equals "A", then the converter might search Application.Resources for a template named "TemplateA" and return it to the binding
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ContentTemplate"
Value="{Binding SomeProperty,
Converter={StaticResource MyTemplateConverter}}"/>
</Style>
Using the DataTemplateManager from this post You can do something like:
DataTemplateManager.RegisterDataTemplate<ViewModelType1, ViewType1>();
DataTemplateManager.RegisterDataTemplate<ViewModelType2, ViewType2>();
DataTemplateManager.RegisterDataTemplate<ViewModelType3, ViewType3>();
then you would remove the ItemTemplate from the ListBox:
<ListBox ItemSource="{Binding Targets}"/>
and in the ListBox ViewModel you could:
public void AddTargets()
{
Targets.Add(new ViewModelType1());
Targets.Add(new ViewModelType2());
Targets.Add(new ViewModelType3());
}
Then, each DataTemplate will be automatically used by WPF to render each corresponding ViewModel.
Also note that you can call DataTemplateManager.RegisterDataTemplate() at any time before showing the ListBox, so you can theoretically do that when loading the MEF parts.
Edit:
Based on your comment, you could create a single DataTemplate with a ContentPresenter to display the selected View according to a property in the ViewModel:
<DataTemplate DataType="{x:Type local:TargetViewModel}">
<ContentPresenter x:Name="MainContentPresenter" Content="{Binding}" ContentTemplate="{Binding YourProperty, Converter=SomeConverter}"/>
and inside the SomeConverter you should use the same technique as demonstrated in the post, to dynamically generate a DataTemplate.
I have what should be a really simple binding, but the problem I'm seeing is that instead of displaying the three companies (company_list is a List, where Company contains a company_id to bind to), I see the window pop up with only the first company_id in company_list. I have other bindings which seem to work fine, and in some other cases I see that I've used ItemSource instead of DataContext, but when I use that I get "Items collection must be empty before using ItemsSource". I've searched extensively for a simple answer to this in stackoverflow, msdn and elsewhere, and have seen mostly really complex solutions that I haven't been able to understand/apply.
When my window appears, it has:
CompanyA
where it should have:
CompanyA
CompanyB
CompanyC
which is the content of the company_list (yes, verified in debugger). Suggestions appreciated! Code and XAML follow.
ReadMasterCompanyList(); // populates a_state.company_list with 3 companies
// display company list dialog
CompanySelect cs_window = new CompanySelect();
cs_window.CompanyListView.DataContext = a_state.company_list;
// fails: cs_window.CompanyListView.ItemsSource = a_state.company_list;
cs_window.Show();
And the XAML from CompanySelect:
<Grid>
<ListView IsSynchronizedWithCurrentItem="True"
x:Name="CompanyListView"
SelectionMode="Single" SelectionChanged="CompanyListView_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Height" Value="30"/>
</Style>
</ListView.ItemContainerStyle>
<ListViewItem Content="{Binding Path=company_id}"></ListViewItem>
</ListView>
</Grid>
I would set the ItemsSource of the ListView, rather than the DataContext, either in codebehind:
cs_window.CompanyListView.ItemsSource = a_state.company_list;
or with binding:
<ListView ItemsSource="{Binding company_list}">
And then set the ItemTemplate of the ListView instead.
...
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding company_id}" />
</DataTemplate>
</ListView.ItemTemplate>
...
I would also look into using the MVVM design pattern for testability and separation of concerns, and look at using PascalCase for your property names.
Also, unless you specifically wanted a ListView, I would use a ListBox.
First, set the DataContext only after cs_window.Show().
Second, the ListViewItem you have as a child in your ListView's XAML is why you're only seeing one.
Third, might work better (and would be more MVVM-ish) if you define ItemsSource in the XAML, like this:
<ListView ItemsSource="{Binding Path=company_list}" ...>
That's after making a_state the DataContext of the ListView's container or some other ancestor element.
The problem is, that you define one ListViewItem in your XAML code. You shouldn't do this.
Try something like this:
<Grid>
<ListView IsSynchronizedWithCurrentItem="True"
x:Name="CompanyListView"
SelectionMode="Single" SelectionChanged="CompanyListView_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Height" Value="30"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Content={Binding Path=company_id}/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
I would like to avoid having to build a menu manually in XAML or code, by binding to a list of ICommand-derived objects. However, I'm experiencing a bit of a problem where the resulting menu has two levels of menu-items (i.e. each MenuItem is contained in a MenuItem):
My guess is that this is happening because WPF is automatically generating a MenuItem for my binding, but the "viewer" I'm using actually already is a MenuItem (it's derived from MenuItem):
<ContextMenu
x:Name="selectionContextMenu"
ItemsSource="{Binding Source={x:Static OrangeNote:Note.MultiCommands}}"
ItemContainerStyleSelector="{StaticResource separatorStyleSelector}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<Viewers:NoteCommandMenuItemViewer
CommandParameter="{Binding Source={x:Static OrangeNote:App.Screen}, Path=SelectedNotes}" />
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
(The ItemContainerStyleSelector is from http://bea.stollnitz.com/blog/?p=23, which allows me to have Separator elements inside my bound source.)
So, the menu is bound to a collection of ICommands, and each item's CommandParameter is set to the same global target (which happens to be a collection, but that's not important).
My question is, is there any way I can bind this such that WPF doesn't automatically wrap each item in a MenuItem?
Unfortunately, the best way I've found to work around this issue is to use a style for the MenuItems, rather than an ItemTemplate. Then each property in the style can be bound to properties on your object. Something like this, for example:
<Style x:Key="SelectionContextMenuStyle" TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Path=Text}" />
<Setter Property="Command" Value="{Binding Path=Command}" />
<Setter Property="CommandParameter" Value="{Binding Path=Parameter}" />
</Style>
It really seems like an ItemTemplate should work, and it would be the better way to go, but this is the only way I've found that actually works properly.
I would be inclined to subclass ContextMenu and override GetContainerForItemOverride:
public class ContextMenuWithNoteCommands : ContextMenu
{
protected virtual DependencyObject GetContainerForItemOverride()
{
return new NoteCommandMenuItemViewer();
}
}
Then set the CommandParameter binding in the NoteCommandMenuItemViewer style, or in ContextMenu.ItemContainerStyle, whichever is more appropriate.
This presumes you can't simply use ItemContainerStyle on a regular MenuItem to get the effect you want:
<ContextMenu ...>
<ContextMenu.ItemContainerStyle>
<Style>
...
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>