WPF MVVM - Binding highlighted item in Combobox - wpf

Is there a way to bind a highlighted ComboBox item in WPF/MVVM?
The reason I want this behavior is that I want to be able to right-click on a ComboBox item and pass the argument (Item Id for that matter) in the CommandParameter.
Since no item is actually selected until I left-click on the item, I have no args to pass...
Thoughts please?
<ComboBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete Expense Category" Command="{Binding DeleteMenuItemCommand}"
CommandTarget="{Binding HighlightedExpenseCategory}" CommandParameter="{Binding Id}"></MenuItem>
</ContextMenu>
</ComboBox.ContextMenu>

First a question: what is your ultimate goal and can you approach it without having the user right click on a context menu item? Perhaps what you may want is a hierarchical combobox like in the image here Any ideas how to implement ComboBox to represent hierarchical data? . Though that may not suit you if you indeed just want to perform an action on an item in your combo box.
With that aside, then HighCore put you on the right track (sorry HighCore, I'm going to steal a little of your thunder here, but as a consolation I'm giving you a couple votes). The key thing is that the ContextMenu is a property of the ComboBoxItem and not the ComboBox itself.
This code snippet may help you get a step closer, though I haven't tested beyond XamlPad. You'll likely have an additional step of making sure your bindings can reach your view model with the command as well as your menu item's Id property, wherever you've defined that. Try the Binding's RelativeSource, but come back if you need help with that. Good luck!
<ComboBox>
<ComboBox.Resources>
<ContextMenu x:Key="contextMenu">
<MenuItem Header="Delete Expense Category" Command="{Binding DeleteMenuItemCommand}"
CommandTarget="{Binding HighlightedExpenseCategory}" CommandParameter="{Binding Id}" />
</ContextMenu>
</ComboBox.Resources>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Setters>
<Setter Property="ContextMenu" Value="{StaticResource contextMenu}" />
</Style.Setters>
</Style>
</ComboBox.ItemContainerStyle>
<ComboBoxItem Content="1" />
<ComboBoxItem Content="2" />
</ComboBox>

Related

Track where I clicked in ListView MVVM

I am writing a folder browser and I want to open folders on double click.
My folders are binded to ListView with GridView inside and I am tracking double click like this:
<i:EventTrigger EventName="MouseDoubleClick">
<Custom:EventToCommand Command="{Binding FolderOpenedCommand, Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=FolderView}"/>
</i:EventTrigger>
But I have an annoying issue: if I double click on gridview splitter to autosize column, it will also open selected folder, which I am not want to.
So, I have several options for now: put event handler inside style and use it with code behind or leave it as is, but in my case I want to do implement it with MVVM scenario because codebehind is not suitable for me.
My question is: how I can send my parameter as SelectedItem only if I click on the item and null when I click on something else?
I want to track this to make a proper behavior as far as I cannot apply double click to gridview on some reason.
Could someone please help me with this problem?
EDIT:
Lets clarify one thing to be sure we speak about the same things:
I can define something like this
<Style x:Key="itemStyle" TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource MetroListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="FolderView_OnMouseDoubleClick"></EventSetter>
</Style>
Bu I cannot do like this:
<Style x:Key="itemStyle" TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource MetroListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="{Binding OpenFilesCommand}"></EventSetter>
</Style>
Because It will lead to exception. Now I want to understand how I can apply command here if Handler does not accept command? Do I need to write some attached property?
Avoid using listview. Use DataGrid instead. Then you can add eventtrigger to row style. ListView is obsolete class introduced in wpf 3 that was replaced by datagrid in wpf 4 an there no reason to use it anymore.
Another option is to use use custom behaviour implemented as attached property, e.g. InvokeCommandOnRowDoubleClick attached to Grid. To learn more about attached behaviours read this: http://blogs.msdn.com/b/dgartner/archive/2009/11/11/wpf-attached-behavior-example-watermark-text.aspx
There is a MouseDoubleClick property on the ListViewItem control. You can rework your style to contain a correct event when double clicking an item, apply it only to the ListViewItems and it won't listen to that event when not double clicking the gridview splitter.
You can read more about this here.
Well, to solve this issue I had to use InputBindings property for each control in each gridview column. I put Grid over controls and made like this:
<GridViewColumn Header="Size (Bytes)">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.FolderOpenedCommand}"
CommandParameter="{Binding ElementName=FolderView, Path=SelectedItem}"></MouseBinding>
</Grid.InputBindings>
<TextBlock Text="{Binding Path=Size, StringFormat='{}{0:#,#.}'}"/>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Unfortunately for my case I didnt find better solution

Using the selected tab in a TabControl to clear a variable - WPF

I have a window which contains a a TabControl with 2 TabItems inside it. Inside the second tab, there is a TextBox with a binding to a string property inside my view model. I want to be able to clear this property (or the TextBox since it is bound it will will by association, clear the property) whenever the user clicks on a different tab (and also if the user closes the window). I'm hoping I can achieve this with only the XAML since I am trying to follow the MVVM pattern (so no code behind and my view model shouldn't know of the TabControl or TextBox that is in my view).
I have tried a few different things and can't quite get it working and while I'm getting better at WPF, I'm still fairly new to it. Inside my tabs I also have ListViews and ComboBoxes, which from my understanding will bubble the SelectionChanged event (same as when the tab selection is changed) so I should probably stay away from that. Any help would be appreciated.
Here was one of my attempts (snippet showing the gist):
<TabControl>
<TabItem>
...Stuff in first tab goes here
</TabItem>
<TabItem Header="Rules">
<TabItem.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{IsSelected}" Value="True">
<Setter TargetName="UserBox" Property="Text" Value="" />
</DataTrigger>
</Style.Triggers>
</Style>
</TabItem.Style>
<GroupBox>
<UniformGrid>
<ListView ItemsSource="{Binding Source={StaticResource rulesViewModel}, Path=RulesList}">
<ListView.View>
<GridView>
<GridViewColumn Header="Rules"/>
</GridView>
</ListView.View>
</ListView>
<DockPanel>
<TextBox Name="UserBox" Text="{Binding Source={StaticResource rulesViewModel}, Path=User}" />
</DockPanel>
</UniformGrid>
</GroupBox>
</TabItem>
I'm pretty sure the line that reads Binding="{IsSelected}" is wrong. But I'm not sure how to target the correct property. Any ideas?
You need to set the SelectedIndex = -1 of the tab whose content you want to clear on other tabs click.
SelectedIndex = -1 is of the default tab so basically we are making this tab as the default one. Now the condition becomes that
if SelectedIndex ! = -1, text should be cleared
or at application shutdown, text should be cleared
I've provided you with the logic, code can now be written :)
I think the best way would be to use the interactivity library to catch the SelectionChanged property and change the property. It would look something like this:
<TabControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<ei:ChangePropertyAction TargetObject="{Binding Path=Path.To.ViewModel}" PropertyName="MyProperty" Value="" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TabControl>
You can also choose to change the value of the TextBox - in which case you'd need to modify the ChangePropertyAction object to look like this:
<ei:ChangePropertyAction TargetName="UserBox" PropertyName="Text" Value="" />

DataGrid to Observable collection of custom class object issue

I'm somewhat of a novice, so I'm pretty flaky when it comes to binding. I've searched for quite some time, but I simply cannot find any examples that get me to where I need.
I have a DataGrid bound to an observable collection. It is a collection of a custom class that contains several properties (one of which is, itself an observable collection). The user inputs text into the DataGrid and it updates the Observable collection. I would like users to be able to right click on a row in the DataGrid and get a context menu with items generated from the observable collection property of the item in the parent observable collection.
For simplicity, my DataGrid is bound to InputItemList, which is an Observable Collection of InputItem.
InputItemList as ObservableCollection(Of InputItem)
InputItem has properties:
Part_Number as String
Drawing_List as ObservableCollection(Of DrawingItem)
DrawingItem has properties:
Revision as String
Drawing_Path as String
The DataGrid is bound via ItemsSource to InputItemList, and is working properly.
For the life of me, I cannot create a series of menu items that are bound to the Drawing_List collection
Here's a simplified version of my XAML with ??? where I need help with the binding:
<DataGrid x:Name="mw_DataGrid" ItemsSource="{Binding Source={StaticResource InputItemList}}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Part_Number}" CanUserReorder="False" CanUserResize="False" CanUserSort="False" Header=""/>
<DataGrid.Columns>
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Open Obsolete Revision" ItemsSource=????>
<MenuItem.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.Header" Value="{Binding Path=Revision}"/>
<Setter Property="MenuItem.Command" Value="{StaticResource cmdOpenObsPDF}" />
<Setter Property="MenuItem.CommandParameter" Value="{Binding Path=Drawing_Path}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
Create a property on your view model, of type InputItem, that represents the currently selected row. Name it for example CurrentlySelectedInputItem
Then bind that to the DataGrids SelectedItem.
Then bind the itemsource you are wanting to:
<MenuItem Header="Open Obsolete Revision" ItemsSource="{Binding CurrentlySelectedItem.DrawingList}">
Not 100% sure of the menu Item usage - but that is how to bind to the drawing_List you are after, you should be able to modify it to suit your needs.
Ok, I figured it out.
I actually had to do this once already and totally forgot!
Apparently there's some difficulty searching back through the tree with context menus.
I do not remember the full explanation, but here's the XAML that works for me:
<MenuItem Header="Open Obsolete Revision" ItemsSource="{Binding Path=PlacementTarget.SelectedItem.DrawingList, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}">

How to add a click handler to MenuItems created by a data binding?

and now the details...
First if all this is my first encounter with WPF so please forgive I am missing something obvious etc.
What I have is a Menu with Menu Items bound to a DataView each menu item is a Row in DataView MenuItem Text is set to a Field in Row... menu is populated well and I can see all rows listed there! but when clicked nothing happens.
My Problem is how I can assign a click event to Menu Item, below is the XAML of ItemPresenter of parent menuitem
<MenuItem Name="mnuRowsDropDown" Header="All Rows ▼"
Height="23"
Loaded="mnuNotesDropDown_Loaded" ItemsSource="{Binding Path=Title, Mode=OneWay}" >
<MenuItem.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.Header" Value="{Binding Title}"/>
<!--<Setter Property="MenuItem.Click" Value="" />-->
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</MenuItem>
As you can see here I commented the Click Code because designed said it is an error
There's a special setter for that called EventSetter, use that instead. (When creating MenuItems dynamically it may be of interest to use commands instead as those can easily be included in the object you use for the binding along with the Header)

WPF ContextMenu with ItemsSource - how to bind to Command in each item? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Specify Command for MenuItem in a DataTemplate
I have a collection of objects (viewmodels) that represent menu items. Each of them have a command that I would like to execute when a MenuItem is clicked.
If I wanted to do the menu statically, I do it like this:
<ContextMenu>
<MenuItem Header="{Binding Text1}" Command={Binding Command1}>
<MenuItem Header="{Binding Text2}" Command={Binding Command2}>
</ContextMenu>
but when I don't know the items in advance (they come from a collection), I need to assign ContextMenu.ItemsSource - and put a text into a ItemTemplate.
<ContextMenu ItemsSource="{Binding MyMenuItems}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text2}" /> <!-- But where to put Command binding? TextBlock.Command makes no sense, and we have no access to MenuItem! -->
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
This way, however, I have no place to bind a Command to - because I can't get the MenuItem for every row!
Any advice, please? Thank you, guys!
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding AssociatedCommand}" />
</Style>
</ContextMenu.ItemContainerStyle>
where AssociatedCommand is the property on the viewmodel object that holds the ICommand.

Resources