WPF - Need a combination of Tree+Grid, with Context Menu - c#

My application is implemented by a GridView inside a TreeList.
Much to my despair, I discovered that the GridView is very primitive, compared to the widely used DataGrid. I am considering these two options:
(1) Somehow, I replace the GridView with a DataGrid (which supports Context Menu).
(2) Somehow, I add the Context Menu capability to the existent GridView.
Which of the 2 approaches (or another?) would you recommend?
Source code is much appreciated.
TIA.

Based on the linked code, here is the solution:
1 - Add the ContextMenu as a Resource:
<Window.Resources>
<ContextMenu x:Key="ItemsContextMenu" x:Shared="False">
<MenuItem>
<MenuItem.Header>
<TextBlock>
<Run>Context Menu Action for Item</Run>
<Run Text="{Binding Tag.Name}"/>
</TextBlock>
</MenuItem.Header>
</MenuItem>
</ContextMenu>
<!-- other stuff here -->
</Window.Resources>
It is recommended that you set x:Shared="False" to prevent Binding issues related to reusing the resource instance.
2 - Define an ItemContainerStyle for your TreeList that sets the ContextMenu for the TreeListItems:
<tree:TreeList ...>
<!-- other stuff here -->
<tree:TreeList.ItemContainerStyle>
<Style TargetType="{x:Type tree:TreeListItem}">
<Setter Property="ContextMenu" Value="{StaticResource ItemsContextMenu}"/>
</Style>
</tree:TreeList.ItemContainerStyle>
</tree:TreeList>
Notice that I'm using DataBinding in the ContextMenu, which means you have a proper, working DataContext in it. You should be able to use Commands and other stuff in it.

Related

XAML Listbox Style Override Control Template for Order Controls

Most of the tutorials and questions I see are about restyling the listbox to look different, but I'm interested in adding additional controls to make it behave differently. I initially started out trying to make the list builder control out of a checkbox list, but found myself too deep. I decided to abstract and start with a smaller problem.
What I am looking to do first, to get a better understanding of how this works is add "up" and "down" buttons next to the control. I think this can all be done in xaml, so to try and pressure myself to stick to that I'm working in Kaxaml.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<!-- ListBox Order Button Style
Col 1
Listbox
Col 2
Buttons Up and Down
-->
<Style x:Key="{x:Type ListBox}" TargetType="{x:Type ListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Rectangle Fill="Yellow"/>
<!--<ListBox></ListBox>-->
</Grid>
<StackPanel Grid.Column="1">
<Button>Up</Button>
<Button>Down</Button>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid>
<ListBox>
<TextBlock>Value 1</TextBlock>
<TextBlock>Value 2</TextBlock>
<TextBlock>Value 3</TextBlock>
<TextBlock>Value 4</TextBlock>
</ListBox>
</Grid>
</Page>
I am currently hung up on a few things.
1) When I try to use a ListBox where the Yellow Rectangle is I start getting infinite loop problems.
2) I'm not sure how to connect the buttons to the listbox once it is there. I think Triggers is the answer, but I don't have much experience with them.
Your infinite loop can be addressed by not relying on the TargetType to apply the style. Instead, apply the style explicitly via a named key (i.e. something other than {x:Type Listbox}). That way the style is applied only when you specifically want it to be applied.
"Connecting" the buttons can be done a variety of ways. The simplest would be to handle the Button.Click event and perform whatever action you want there.
All that said, I think you're going about this the wrong way. Let a ListBox be a ListBox; don't try to make it into something it's not. If you want a reusable control that adds functionality around a ListBox, like buttons to control the contents of the ListBox, you should probably be authoring a UserControl, which is essentially a composite control made up of whatever you want.
Doing so will give you a lot more control over the appearance of the control. You'll also have the opportunity to declare dependency properties on your control that are specific to exactly what that control needs to support (something you can't do just with a Style). Yes, it also means you'll have to expose properties of contained elements via new properties in your UserControl that effectively delegate to the contained elements, but that's a small price to pay for the flexibility and relative simplicity of creating the UserControl in the first place.

usercontrol (?) for WPF reusable menu

I am a beginner at WPF so please excuse me if this question is too simple :)
I have a listbox which I would like to filter by various filter conditions. This listbox I fill with instances of a particular type. Each filter condition is associated with one of the listbox items' properties. (They are like: this or that string property starts with string XXX.)
For this I would need a menu for each property from which users can select the filter conditions they want to filter the items with. Each property of the same type will have the very same set of menu items with the various filter conditions. (For strings: starts with, ends with... for ints: lower than, higher than, etc.)
The menus require some code behind too so I don't want to program these for each property separately.
My problem is that I don't know in what way could I program these. I cannot program them as UserControls because all what I need is MenuItems in a Menu. But I cannot program them as MenuItem derived classes because I would need the XAML for designing them for each type. Could I create a MenuItem derived class with a XAML somehow? Or do you have any other suggestions?
In WPF, we work with data elements whose public properties are data bound to the properties of various UI controls via DataTemplates. Please see the Data Templating Overview page on MSDN for the full story.
In order to do this, we develop custom classes that contain all of the necessary properties that we need to display and then we declare one or more DataTemplates that define the binding connections between the classes and the UI controls, or MenuItems in your case.
The benefit of this is that in order to display a Menu in the UI, you just need to data bind one of your custom menu class objects to a control in the UI and let the DataTemplate do the rest. So if you want to change the menu contents, you just need to change the data item that is data bound to the Menu.
So to answer your question directly, a Menu control would be most suitable for you to use, but you don't store the Menu properties in your code behind... you store the property values in your custom classes that will be data bound to the Menu control properties:
<Menu ItemsSource="{Binding CollectionOfYourCustomClassItems}" ... />
It is worth pointing out that you may need to set the child MenuItem properties in a Style and not a DataTemplate as usual (taken from the accepted answer to the WPF MenuItem : Mix databound items and static content question (which I recommend that you read) here on Stack Overflow):
<MenuItem Header="_Recent Files" ItemsSource="{Binding Commands,Mode=OneWay}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding Path=ShortName}" />
<Setter Property="ToolTip" Value="{Binding Path=FileName}" />
<Setter Property="Command" Value="{Binding Path=OpenCommand}" />
<Setter Property="CommandParameter" Value="{Binding Path=OpenParameter}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSeparator}" Value="true">
<Setter Property="MenuItem.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Separator Style="{DynamicResource {x:Static MenuItem.SeparatorStyleKey}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
You will find many more tutorials and related questions regarding data binding to MenuItems online, so I won't go over everything again here. Please see the following article to start with:
Binding menus using HeirarchicalDataTemplates

Command not executing in row level ContextMenu on DataGrid in WPF C#

I have my template declared like so -
<DataGrid.Resources>
<ContextMenu x:Key="RowMenu" DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Remove" Command="{Binding Cancel}" />
</ContextMenu>
</DataGrid.Resources>
I'm applying the template using row style -
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="ContextMenu" Value="{StaticResource RowMenu}" />
</Style>
</DataGrid.RowStyle>
The menu shows up ok
But the command (on the ItemListViewModel) does not execute when a context menu item is clicked
public class ItemListViewModel : INotifyPropertyChanged
{
public void Cancel()
{
MessageBox.Show("Cancel test");
}
...
}
My binding is otherwise working properly, as I can do things like this -
foreach (ItemListViewModel ul in mylist.SelectedItems)
MessageBox.Show(item.FullDescription);
I've been at this all night trying to figure it out. Just started with WPF today.
Please somebody tell me where I've gone wrong
I don't think you can bind to a simple method. You need to bind to a command that should be an implementation of ICommand interface. In you case you need to create a class that would implement that interface and add property of that class type to your model.
See this example as a reference: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
I decided ListView was better for my needs and I'm using that instead. I'm no longer trying to bind the context menu to the item, but rather have a single context menu for the whole listview and simply enable or disable items in the ContextMenuOpen event where need be.

Using a ValueConverter after a DataContext switch

I want to try out a small, custom ValueConverter in a class which after being instantiated (via default constructor, which only calls InitializeComponents() ), is given another DataContext, specifically an instance of a ViewModel.
Using a StaticResource within the Binding does not work at all (yields a NullReferenceException), since the DataContext has since then been changed (is not this anymore).
I've tried putting DataContext = this; before the InitializeComponents call, no change.
Should I be looking into this MarkupExtension gizmo (as described in this article) ?
I've also tried creating an instance of the custom Value Converter within the ViewModel (the current DataContext), doesn't help either.
I can provide additional details at all times. Thank you in advance !
I'm trying to display a ContextMenu within the TextBlock. The ContextMenu contains a sole MenuItem. The header of the MenuItem can be "Settings" for instance. The Children (rendered as MenuItems as well) of the said MenuItem stem from an Enum, hence the ItemsSource on the MenuItem.
Now all is getting displayed nicely, yet I am trying to make one of the Children (e.g. a member of the Enum) to be selected per default, since there is already a default Setting. Further background info can be found in my other question.
Edit :
...
<UserControl.Resources>
<Helpers:DisplayTypeToDefaultValueConverter x:Key="displayTypeConverter" />
</UserControl.Resources>
...
<TextBlock x:Name="InstructionLabel"
TextWrapping="Wrap" Text="{Binding Path=SelectedNodeText}"
Grid.RowSpan="1">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Settings" Name="SettingsPop"
DataContext="{Binding}"
ItemsSource="{Binding Source={StaticResource DisplayTypeValues}}"
IsCheckable="True"
Click="SettingsType_Click">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding}"/>
<Setter Property="IsChecked">
<Setter.Value>
<Binding Converter="{StaticResource displayTypeConverter}" />
</Setter.Value>
</Setter>
</Style>
</MenuItem.ItemContainerStyle>
</ContextMenu>
</TextBlock>
I've only now realized that it's the dreaded ContextMenu. That's the problem, isn't it ?
The DataContext inside the ItemContainerStyle is a member of the DisplayTypeValues collection. The only thing in the XAML you posted that will be affected by the DataContext of the UserControl changing is the InstructionLabel's text. Setting DataContext="{Binding}" as you're doing on the MenuItem is also redundant as the value will already be inherited from the parent ContextMenu.
It's not clear from your question or code what you're expecting from the DataContext or what you're trying to do with it.
Just several thoughts:
Are you sure you didn't miss setting binding path in <Binding Converter="{StaticResource displayTypeConverter}" />?
Did you check the StackTrace of the thrown exception and all it's InnerExceptions to see, whether there was something interesting?
Used an easier solution, as highlighted in my other related question.
Thank you for your input !

Context Menu on TreeViewItem Calls TreeViewItem's Method?

I have a TreeView setup so that each TreeViewItem has right-click context menu applied as a Style. Something like:
<Grid.Resources>
<ContextMenu x:Key="contextMenu">
<MenuItem Header="Save" IsEnabled="{Binding Path=Saveable}"/>
<MenuItem Header="Copy" IsEnabled="{Binding Path=Copyable}"/>
<MenuItem Header="Remove" IsEnabled="{Binding Path=Removeable}"/>
</ContextMenu>
<Style TargetType="TreeViewItem">
<Setter Property="ContextMenu" Value="{StaticResource contextMenu}" />
</Style>
</Grid.Resources>
Saveable, Copyable and Removeable are properties that come from the object that's used as the TreeViewItem.
What I'm looking for is when the user clicks on a MenuItem, it would click on the appropriate method of the selected object. So clicking on the "Save" MenuItem would call object.Save(), "Copy" calls object.Copy(), etc. But I'm not sure what the syntax would look like, or whether the idea is actually acceptable in terms of typical WPF style. I know I can just create a new event handler in the encompassing window, but I'd prefer the selected item itself to handle the event.
Thoughts?
Thanks!
Unfortunately, I don't think that there is an automated way of doing this. The closest option would be to setup a RoutedUICommand for each item in the ContextMenu, and then create a CommandBinding for each in your class. If you want those to go to the TreeViewItem, you'll probably need to subclass TreeViewItem and set up the CommandBindings there.
The one option that I thought might work would be to add an EventSetter for MenuItem.Click to the TreeViewItem style. However, that did not work - probably because the items in the ContextMenu are in a different visual tree from the TreeViewItems.

Categories