Clickable ItemsControl item in WPF - wpf

I'm using an ItemsControl to display a list of databound items, for each the core of the DataTemplate is a Grid upon which I've placed all the bound controls.
I would like to be able to click on the entire area for each item in the list. But I cannot figure out how to make the area clickable.
Any suggestions of how to make an entire grid area clickable would be great.

To make something clickable you can usually wrap it in a Button, if it should be "invisible" you can change the template:
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<ContentPresenter />
</ControlTemplate>
</Button.Template>

You can use the AttachedCommandBehavior classes from C# Disciples to achieve this.
Define a command in the ViewModel, and then on the Grid object use the ACB AttachedProperties to bind the MouseLeftButtonUp event to the command.
Some code to get you started:
<Grid Name="grid" Height="30" ForceCursor="True" Cursor="Hand">
<acb:CommandBehaviorCollection.Behaviors>
<acb:BehaviorBinding Event="MouseLeftButtonUp" Command="{Binding Path=DataContext.EditEventCommand, RelativeSource={RelativeSource AncestorType={x:Type self:Dashboard}}}" CommandParameter="{Binding}" />
</acb:CommandBehaviorCollection.Behaviors>
</Grid>
Edit for non-MVVM solution.
The above code snippet will still work when you have not designed your application following the MVVM guide-lines as you are essentially just binding to a command in the code-behind.
However, if you don't want to go to the trouble of defining commands, you can simply specify an event to hook to, like so:
<Grid MouseLeftButtonUp="Grid_MouseLeftButtonUp"> in the XAML file.
and in the code-behind:
private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
}

Related

Triggers, commands not firing in custom ListBox's ItemTemplate

I've got a custom ListBox control with a style set up in my Themes/Generic.xaml. I then have a button in the ListBox's ItemTemplate, and it's Click event isn't firing and I've got no idea why. Same goes for the button's Commands (I'm confident the Command issue isn't DataContext related) and interaction triggers. While attempting to debug, I noticed that using the default ListBox instead of my own stopped the problem, but I need to use the custom control.
This is essentially what I've got (fluff removed for brevity). The button:
<controls:CustomListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Click=MyHandler/>
</DataTemplate>
</ListBox.ItemTemplate>
</controls:CustomListBox>
And the custom control's style in Themes/Generic:
<Style TargetType="{x:Type controls:CustomListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:CustomListBox}">
<Border>
<ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How can I get this event to fire?
I think this should probably be a usercontrol rather than a custom control.
Are you really going to change the template out of this for something else?
If you use an event handler like that then how are you planning on using the delegate? It's a very inflexible way of working you're headed in.
You mentioned command, which is probably rather more like it.
If you use a button in an item template with a command bound like
<Button Command="{Binding RowCommand}"
Then the datacontext of that Button is the content of the row.
If you bind ItemsSource to a collection Items of ItemVM then it's looking in the ItemVM that is presented to that row.

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

How to bind to parent position changes in WPF using XAML and no code behind

I have a visio-like interfact but have actual model data behind some of the elements. The elements can be moved by the user.
I use a contentcontrol on a canvas whereby the viewmodels of the elements are places in the content which can then be displayed differently depending on their type but using the same contentcontrol. It is simple to bind the view to the different properties in the viewmodel. However, I have to save the position in the model, and I cannot find a binding solution.
1) The Application.Save Command is handled in the main view model, so I do not have access to the view there. That means I must save the postion data when the elements are moved, or is there a better approach?
2) Assuming that I am right with 1), I am looking to avoid code behind, i.e. I do not want the contentcontrol to deal with the elements that they have in their content. However, so far the code behind version is all I could come up with:
My code behind solution so far:
All model elements implement an interface:
public interface IViewElement
{
String Position { get; set; }
}
And in the contentcontrol:
void ContentControl_LayoutUpdated(object sender, EventArgs e)
{
IViewElement content = this.Content as IViewElement;
content.Position = new Point(Diagram.GetLeft(this), Diagram.GetTop(this)).ToString();
}
The XAML:
<Style TargetType="{x:Type diagram:Item}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type diagram:Item}">
<Grid Canvas.Top="{Binding ElementName=PART_ContentPresenter, Path=Content.Position, Mode=TwoWay}" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
ContextMenu="{x:Null}">
<!-- PART_ContentPresenter -->
<ContentPresenter x:Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<DataTemplate DataType="{x:Type local:ViewModel}">
<StackPanel>
...
</StackPanel>
Just encapsulate the codebehind you've used in a Behavior
Why are you using a string to store the position? Use either a Point or two decimal values, and then bind your ContentControl's Canvas.Top and Canvas.Left position to these values using two-way binding.
It will automatically update the model when the Top and Left positions change.
Edit:
Here's an example:
<ContentControl Canvas.Top="{Binding ContentModel.Top, Mode=TwoWay}"
Canvas.Left="{Binding ContentModel.Left, Mode=TwoWay}"
Content="{Binding ContentModel}" />

How to bind a image source in ControlTemplate from Code behind?

I need to bind the image source that defined in code behind into ControlTemplate. I have problem making it work correctly. I am wondering if someone can help.
The image is in ControlTemplate of the Button style. I instantiate that Button style in multiple XAML pages. My goal is to define 'Icon' instance in each code behind of these xaml pages to place the icon that is unique for each XAML page. Perphaps, it can be a better way to define unique icons that should be displayed in each XAML page where the button style will be loaded.
Below is the link to the solution I've created. It is a simple sample where I have 2 xaml pages with the buttons using the same style. I need to be able to load icon1.png into the button while mainWindow.xaml is opened and icon2.png when Page1.xaml is opened.
http://cid-0c29483cf3a6a14d.office.live.com/self.aspx/WPF%5E_Tests/BindingImageFromCode.zip
I see a couple of solutions to your problem. You'll never know what the parent of your Button really is so a code behind Binding to a property for Window, Page etc. will be hard to accomplish without to much hardcoding.
Approach 1
Subscribe to the Loaded event for the Button, find the Image in the Button Template with FindName and set the Source from there. This can also be done with an Attached Behavior
Xaml
<Button Style="{DynamicResource ButtonStyle1}"
Loaded="Button_Loaded"
...>
Code behind
private void Button_Loaded(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
Image imgIcon2 = button.Template.FindName("imgIcon2", button) as Image;
Uri uri = new Uri("Resources/icon1.png", UriKind.Relative);
ImageSource imgSource = new BitmapImage(uri);
imgIcon2.Source = imgSource;
}
Approach 2
Create a subclassed Button called e.g ImageButton where you add a new Property UriSource which you can bind to inside the Template.
<Style x:Key="ButtonStyle1" TargetType="{x:Type local:ImageButton}">
<!--...-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<!--...-->
<Image x:Name="imgIcon2"
Source="{Binding RelativeSource={RelativeSource self},
Path=UriSource}"
.../>
<!--...-->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usable like
<local:ImageButton Style="{DynamicResource ButtonStyle1}"
UriSource="Resources/icon1.png"
...>
Also, I thought that you could bind to an Attached Property from inside of the ControlTemplate but that doesn't seem to be working..

How to wire up a click event for a custom usercontrol button? Should I use CustomControl?

I wanted to create a button that had an image and a textblock as content. So I went about looking for an answer and found a post (Reusable Custom Content for Buttons) which told me to create a usercontrol.
I did this and it works great. I can set the image source and text through dependency properties. However, I am stuck as there is no click event for my control.
I did a little more digging and concluded that I probably need a CustomControl derived from Button. Is this correct? Or would it be better to wire up a click event to my UserControl?
Here's my UserControl:
<UserControl x:Class="Client.Usercontrols.MyButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" MinHeight="30" MinWidth="40"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Button Width="Auto" HorizontalAlignment="Center">
<Border CornerRadius="5" BorderThickness="1" BorderBrush="Transparent" >
<Grid>
<Image Name="tehImage" Source="{Binding ImageSource}" />
<TextBlock Name="tehText" Text="{Binding Text}"
Style="{DynamicResource ButtonText}" />
</Grid>
</Border>
</Button>
</UserControl>
Implementation
<my:MyButton ImageSource="../Images/MainSyncButton.png" ImageWidth="141" Text="Synchronise" Click="btnSynchronise_Click" />
The easiest option would be to just make your UserControl expose a click event, and pass through your Button's click event.
In MyButton's xaml:
<Button Width="Auto" HorizontalAlignment="Center" Click="onButtonClick">
In MyButton's code:
public event RoutedEventHandler Click;
void onButtonClick(object sender, RoutedEventArgs e)
{
if (this.Click != null)
{
this.Click(this, e);
}
}
You can then leave your "implementation" code as-is.
The answer really depends on what your goals are for the control. You may be able to get away with not creating a user or custom control if you can manipulate the data that you are binding to. If all you want to do is display a dynamic image and text, then you could create an ImageText object that contains two properties. You could then bind the default Button control's Content property to this object and use a DataTemplate to define the layout of the content.
If you cannot control the data type that you are binding to, or if you're really set on the idea of creating a control then I would recommend creating a custom control. Custom controls allow you to utilize the built-in capabilities of a standard button. Generally you would only want to create a User Control if you wanted to hide or encapsulate the default functionality of the visual controls contained within the control.
Good luck.

Resources