Track where I clicked in ListView MVVM - wpf

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

Related

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="" />

WPF MVVM - Binding highlighted item in Combobox

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>

Dynamically show/display wpf content

As the title says, I want to hide or display some content dynamically. I have two buttons working like radio buttons. Each button has to display a specific content, but the two contents must not be visible at the same time (it's a kind of form, and I'm working on two subclasses).
I've seen on SO two interesting things, and I wanted to mix them. Firstly, the use of ContentControl with a ToggleButton (http://stackoverflow.com/questions/7698560/how-to-bind-click-of-a-button-to-change-the-content-of-a-panel-grid-using-xaml). Secondly, using ToggleButtons as RadioButtons (http://stackoverflow.com/questions/2362641/how-to-get-a-group-of-toggle-buttons-to-act-like-radio-buttons-in-wpf second answer with 31)
So I decided to start with something like that :
<ContentControl>
<ContentControl.Template>
<ControlTemplate>
<WrapPanel HorizontalAlignment="Center">
<TextBlock Text="{x:Static Internationalization:Resources.MAINOPTIONSWINDOW_STATUSLINKSUPERVISOR}"/>
<RadioButton Style="{StaticResource {x:Type ToggleButton}}" Content="Alarm" GroupName="Trigger"/>
<RadioButton Style="{StaticResource {x:Type ToggleButton}}" Content="Event" GroupName="Trigger"/>
</WrapPanel>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
But Visual Studio underlines
{StaticResource {x:Type ToggleButton}}
Visually, my buttons appear like radio buttons instead of toggle buttons.
Visual says :
Impossible to resolve the resource "{StaticResource {x:Type
ToggleButton}}" (translated from french ;) )
Anyway this code works fine outside the ContentControl.
Yeah, first of all, fix your eventHandlers. Dont attach them on templates, tricky things can happen.
Second of all, you can modify your ContentControl.Resources, and put this to there:
<Style BasedOn="{StaticResource {x:Type ToggleButton}}"
TargetType="RadioButton"/>
And remove your own style definitions. You can even put that one level more up(parent of ContentControl). One more thing, you're doing this in code-behind, but I can assure you, true power of WPF comes from the fact that you dont need code-behind, atleast for logic like this.
Also the link you gave, it is using triggers. This is no biggie right now but you should know that it's not correct way. If you wanna switch between contents, check out ContentControl Content, DataTemplates, DataTemplateSelector. The idea is to have only one VIEW at memory, not hide things.
That can catch up, if youre doing the other way. It will eventually add more time to startup and memory usage.
In control templates you normally do not add event handlers.
Override the OnApplyTemplate and add the handler there.
I finally found another way
<RadioButton Content="Alarm" GroupName="TriggerStateInfo" Style="{StaticResource {x:Type ToggleButton}}"
Command="{x:Static StateInfoTemplateToAlarmCommand}"/>
<RadioButton Content="Event" GroupName="TriggerStateInfo" Style="{StaticResource {x:Type ToggleButton}}"
Command="{x:Static StateInfoTemplateToEventCommand}"/>
<ContentControl Content="{Binding Path=Trigger, ElementName=Window}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type States:AlarmTrigger}">
<ComboBox ItemsSource="{Binding Path=AlarmTriggersCollection}" />
</DataTemplate>
<DataTemplate DataType="{x:Type States:EventTrigger}">
<ComboBox ItemsSource="{Binding Path=EventTriggersCollection}" />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
With code behind changing type of Trigger

How can I get an event or command to fire when the user clicks on a ListViewItem?

I have a list in my application's UI and I'd like to open a popover when the user clicks on an item in the list. As far as I can tell, there are no Click events on ListViewItem that will help me with this. The SelectionChanged event will not work either, since the user can close the popover and I'd like to be able to open it again without requiring them to select a different item in the list first.
This is what my ListView creation code looks like, for what it's worth:
<ListView x:Name="_resultsListView"
ItemsSource="{Binding AvailableResults}"
SelectedItem="{Binding SelectedResult}"
ItemContainerStyle="{StaticResource LoadResultItemStyle}"
VirtualizingStackPanel.IsVirtualizing="True"
Style="{DynamicResource DefaultListViewStyle}" BorderThickness="0"/>
Thanks! Any suggestions would be appreciated.
What HB suggested works, although I find a more permanent solution for binding Events to Commands is to use an AttachedCommandBehavior that will let you attach a command to just about any event
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="local:CommandBehavior.Event" Value="Click" />
<Setter Property="local:CommandBehavior.Command" Value="{Binding DataContext.MyCommand, ElementName=MyListView}" />
<Setter Property="local:CommandBehavior.CommandParameter" Value="{Binding }" />
</Style>
Put a Button in the ItemTemplate (or ListViewItem.Template set via the ItemContainerStyle) and bind it to a command, you can style it to be invisible if need be.

Applying TextDecoration to an WPF ListViewItem

I can see how to apply a text decoration to a GridViewColumn.
<GridViewColumn Header="Tool" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Entity.ToolId}" TextDecorations="{Binding Path=TextDecoration}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
But if I want the TextDecoration to apply to the entire row (in my case a strikethrough), then I have to duplicate the above code to every GridViewColumn.
What I can not figure out is how to apply the TextDecoration to the entire listview item possibly via the ItemContainerStyle.
Can anyone give me a head up on how this could be done?
You should not define the binding per column, if the cells of each column support TextDecoration, because then you need to repeat it for every column. Better apply it to DataGrid.CellStyle property:
<DataGrid.CellStyle>
<Style TargetType="TextBlock">
<Setter Property="TextDecorations"
Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}},
Path =Item.TextDecoration}" />
</Style>
</DataGrid.CellStyle>
The challenge here is to access the property TextDecoration. Use relative source binding to find the parent DataGridRow which has an Item property which contains the data for that row.
Formatting WPF DataGrid using binding can be rather perplexing. See my article on CodeProject where I cover the most important cases, including applying TextDecoration on single cells (or all cells in your case): Guide to WPF DataGrid Formatting Using Bindings
use GridViewRow.CellTemplate

Resources