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>
Related
In using WPF for a while, this is actually the first time that I've come across a situation where I have an ItemTemplate for a ListBox that I want to be configurable based on properties outside the item itself. The problem came up when I wanted to have a Font selector dialog where the user could click a chechbox to enable the font previews (I actually changed my mind about this implementation, but I'd still like to know the answer).
It seems, because the DataTemplate may be used in any situation where that type is provided, it's considered good practice, not to bind to any parameters outside the item's configuration (seems like the code to bind to properties of a containing DataTemplate is particularly obtuse.)
I was wondering how I was supposed to implement this kind of situation. The code below works, but the binding is to a visual element, whereas I would rather bind to the property in the ViewModel.
<Window x:Class="ScreenWriter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}}" >
<Grid>
<StackPanel>
<CheckBox Name="ShowPreview" IsChecked="{Binding IsShowPreviewChecked}">
Show Preview
</CheckBox>
<ListBox ItemsSource="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}">
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.FontFamily" Value="Arial" />
<Style.Triggers>
<DataTrigger Value="True" Binding="{Binding IsChecked, ElementName=ShowPreview}">
<Setter Property="TextBlock.FontFamily" Value="{Binding}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
This doesn't seem like an unusual situation, but I can't find any solution that isn't prefixed with "this is a clever way to get around what you're not supposed to do".
Thanks for any help...
There are no 'best practice' solution for this problem.
As you wrote, "the DataTemplate may be used in any situation where that type is provided ... not to bind to any parameters outside the item's configuration".
Binding/DataContext are based on LogicalTree. So your Trigger binding trys to find the closest datacontext on the tree (in this case your ListItem DataContext = FontFamily )
You have to make same 'jump' from this 'LogicalTree Island' to the main Tree - like your control/element did a jump.
So the real question is, how could you change the binding source smoothly.
You could bind Target element DataContext directly (CheckBox has no DataContext, but its grandparent - Windows has):
<DataTrigger Binding="{Binding DataContext.IsShowPreviewChecked, ElementName=ShowPreview}" Value="True" >
or just find ListItem ancestor directly:
<DataTrigger Binding="{Binding DataContext.IsShowPreviewChecked, RelativeSource={RelativeSource AncestorType=ListBox, Mode=FindAncestor}}" Value="True" >
If you will use this jump many times, you will try to use BindingProxy
which will provide a shortcut for your bindings:
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Context
{
get { return (object)GetValue(ContextProperty); }
set { SetValue(ContextProperty, value); }
}
public static readonly DependencyProperty ContextProperty =
DependencyProperty.Register("Context", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
Add this proxy as resouce:
<Window.Resources>
<this:BindingProxy x:Key="Proxy" Context="{Binding}" />
</Window.Resources>
and your binding is:
<DataTrigger Binding="{Binding Context.IsShowPreviewChecked, Source={StaticResource Proxy}}" Value="True" >
Do not forget add INotifyPropertyChanged Interface to your ViewModel
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 a listview with two columns, one contains a textbox and the other a checkbox. These are bound to an ObservableCollection of a custom object containing a string for the textbox and a boolean for the checkbox.
All was working well until I tried having the check event of the checkbox highlight it's the row in the listview as in this article.
My problem is that checkbox no longer binds to the ObservableCollection. The textbox binds okay, but changing the checbox declaration from:
<CheckBox IsChecked="{Binding RestrictedEdit}"/>
to this:
<CheckBox IsChecked="{Binding RestrictedEdit, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}}"/>
stops the checkbox binding and the listview is displayed with the checkboxes all unchecked irrespectivate of status of the boolean. What am I doing wrong?
You are trying to bind to RestrictedEdit property, which ListViewItem doesn't have. This property is declared in view model, which is stored in DataContext, so this should work:
<CheckBox IsChecked="{Binding DataContext.RestrictedEdit,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListViewItem}}}"/>
However, I don't see any reason to use this code instead of simple IsChecked="{Binding RestrictedEdit}". CheckBox inherits DataContext from ListViewItem, so there is no reason to use relative source.
Let the binding as it is (no RelativeSource) and use rather a style or a DataTemplate having your custom object class as TargetType, and with a DataTrigger set on RestrictedEdit.
example with style :
<Style x:Key="MyStyle" TargetType="MyClass">
<Setter Property="BackGround" Value="White" />
<Style.Trigger>
<DataTrigger Binding="{Binding RestrictedEdit}" Value="False">
<Setter Property="BackGround" Value="Gray" />
</DataTrigger>
</Style.Trigger>
</Style>
Define this style, say, in your application resources (in App.xaml).
Then in your listview, use :
ItemContainerStyle="{StaticResource MyStyle}"
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.