Wpf listview item object doubleclick - wpf

I have a list of objects that get dynamically created when the window opens. For instance:
//Set content for listview sentitems
inbox.ItemsSource = from email in _dataDC.emails
where email.from == _username
orderby email.time descending
select email;
My xaml:
<TabItem Header="Inbox" Height="30">
<TabItem.Content>
<ListView Name="inbox" BorderThickness="2" Margin="5,0,-5,0">
<ListView.View>
<GridView>
<GridViewColumn Header="Van" Width="70" DisplayMemberBinding="{Binding from}" />
<GridViewColumn Header="Onderwerp" Width="120" DisplayMemberBinding="{Binding subject}" />
<GridViewColumn Header="Op" Width="130" DisplayMemberBinding="{Binding time}" />
</GridView>
</ListView.View>
</ListView>
</TabItem.Content>
</TabItem>
When an item in the list is doubleclicked, I simply want to open a new window. Object gets passed to the new window, where I do something with it. Any simple solution?

Try this out...
XAML
<ListView Name="inbox" BorderThickness="2" Margin="5,0,-5,0" MouseDoubleClick="inbox_OnMouseDoubleClick">
C#
private void inbox_OnMouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
// Assumes your NewWindow class has a constuctor that takes the Email type.
NewWindow window = new NewWindow((Email)inbox.SelectedItem);
window.Show();
}

Use ListView's MouseDoubleClick.
XAML:
<ListView Name="inbox" BorderThickness="2" Margin="5,0,-5,0" MouseDoubleClick="ListView_MouseDoubleClick">
Code Behind:
private void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var item = (sender as ListView).SelectedItem;
if (item != null)
{
//use the item here and pass to the new window
NewModal s = new NewModal(Email)item);
}
}

Related

focus on last line of a listview binded to a collection - wpf mvvm

In a wpf project i've a listview "binded" to a collection
Every time i add an item to the collection i'd like the focus on the listview goes to the last line (to the addeed one)
how to do that?
//XAML
<ListView
x:Name="logListActions"
Height="200"
MinHeight="150"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="WhiteSmoke"
BorderThickness="1"
ItemsSource="{Binding LogMessages}">
<ListView.View>
<GridView AllowsColumnReorder="True">
<GridViewColumn
Width="110"
DisplayMemberBinding="{Binding When}"
Header="Data" />
<GridViewColumn
Width="Auto"
DisplayMemberBinding="{Binding Message}"
Header="Messaggio" />
</GridView>
</ListView.View>
</ListView>
//ViewModel
public ObservableCollection<LogMessage> LogMessages
{
get { return _logMessageList; }
set
{
_logMessageList = value;
OnPropertyChanged("LogMessages");
}
}
After adding new item into Listbox, call below code from code behind:
logListActions.ScrollIntoView(item);
logListActions.SelectedItem = item;
Bind the SelectedItem property of the ListView to a LogMessage property in your view model and set the latter to the last added LogMessage object:
<ListView ... ItemsSource="{Binding LogMessages}" SelectedItem="{Binding SelectedLogMessage}">
private LogMessage _selected;
public LogMessage SelectedLogMessage
{
get { return _selected; }
set { _selected = value; OnPropertyChanged("SelectedLogMessage"); }
}
This will select the last row. You could then handle the SelectionChanged event in the view to focus and highlight it:
private void logListActions_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems != null && e.AddedItems.Count > 0)
{
Dispatcher.BeginInvoke(new Action(() =>
{
ListViewItem lvi = logListActions.ItemContainerGenerator.ContainerFromItem(e.AddedItems[0]) as ListViewItem;
if (lvi != null)
lvi.Focus();
}), System.Windows.Threading.DispatcherPriority.Background);
}
}

how to get column from listview in WPF

If we are using the DataGrid to get the value from the column I can use
var a = datagrid1.Columns[1].GetCellContent(item)
Can anyone please tell me how can I get the same value when using ListView instead of DataGrid. This ListView contains a GridView.
Try this
xaml
<ListView Margin="10" Name="UsersList" SelectionChanged="UsersList_SelectionChanged_1">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid x:Name="colGrid">
<TextBlock Text="{Binding Name}" x:Name="txtBlock"/>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Age" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid x:Name="colGrid">
<TextBlock Text="{Binding Age}" x:Name="txtBlock"/>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Mail" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid x:Name="colGrid">
<TextBlock Text="{Binding Mail}" x:Name="txtBlock"/>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
SelectionChanged Event Handler of List View
private void UsersList_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
var gridView = UsersList.View as GridView;
ListBoxItem myListBoxItem =UsersList.ItemContainerGenerator.ContainerFromItem(UsersList.SelectedItem) as ListBoxItem;
//ContentControl does not directly host the elements provided by the expanded DataTemplate.
//It uses a ContentPresenter to do the work.
//If we want to use the FindName method we will have to pass that ContentPresenter as the second argument, instead of the ContentControl.
ContentPresenter myContentPresenter = myListBoxItem.GetVisualChild<ContentPresenter>();
foreach (var column in gridView.Columns)
{
//Here using FindName we can get the controls of cell template
var grid = column.CellTemplate.FindName("colGrid", myContentPresenter);
}
}
Visual Helper
public static class VisualHelper
{
public static T GetVisualChild<T>(this Visual parent) where T : Visual
{
T child = default(T);
for (int index = 0; index < VisualTreeHelper.GetChildrenCount(parent); index++)
{
Visual visualChild = (Visual)VisualTreeHelper.GetChild(parent, index);
child = visualChild as T;
if (child == null)
child = GetVisualChild<T>(visualChild);//Find Recursively
else
return child;
}
return child;
}
}
I hope this will help.

Filtering a Collection in a ListView in WPF

I'm creating an WPF application that allows a user to enter some details about their Employee, using Entity Framework, CRUD operations and MVVM.
So far, I have two ListViews. One contains a list of employees names (listview1), while the other (listview2) lists their details such as Date of Birth, address etc. The Image below will give you a better picture of what I'm creating;
I am using a CollectionViewSoruce to enable me to filter the results on listview2 when you select a specific name from listbox1. So far I am able to achieve this, but When I add an employee or delete, it throws an exception;
An unhandled exception of type 'System.StackOverflowException' occurred in *.UI.exe
Here are the code snippets that might help
ViewModel:
private EmployeeListViewModel()
: base("")
{
EmployeeList = new ObservableCollection<EmployeeViewModel>(GetEmployees());
this._employeeCol = new ListCollectionView(this.employeeList);
}
private ListCollectionView _employeeCol;
public ICollectionView EmployeeCollection
{
get { return this._employeeCol; }
}
private ObservableCollection<EmployeeViewModel> employeeList;
public ObservableCollection<EmployeeViewModel> EmployeeList
{
get { return employeeList; }
set
{
employeeList = value;
OnPropertyChanged("EmployeeList");
}
}
private EmployeeViewModel selectedEmployee = null;
public EmployeeViewModel SelectedEmployee
{
get
{
return selectedEmployee;
}
set
{
selectedEmployee = value;
OnPropertyChanged("SelectedEmployee");
EmployeeCollection.Filter = new Predicate<object>(o => SelectedEmployee != null && o != null && ((EmployeeViewModel)o).EmployeeID == SelectedEmployee.EmployeeID);
}
}
internal ObservableCollection<EmployeeViewModel> GetEmployees()
{
if (employeeList == null)
employeeList = new ObservableCollection<EmployeeViewModel>();
employeeList.Clear();
foreach (DataObjects.Employee i in new EmployeeRepository().GetAllEmployees())
{
EmployeeViewModel c = new EmployeeViewModel(i);
employeeList.Add(c);
}
return employeeList;
}
ListView2 - EmployeeListView;
<ListView Name="lsvEmpoyeeList" Height="170" Width="700"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Visible"
HorizontalAlignment="Center"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding EmployeeCollection}"
SelectedItem="{Binding SelectedEmployee}">
<ListView.View>
<GridView>
<GridViewColumn Header="Position" DisplayMemberBinding="{Binding Position}" Width="100" />
<GridViewColumn Header="DateOfBirth" DisplayMemberBinding="{Binding DateOfBirth, StringFormat={}\{0:dd/MM/yyyy\}}" Width="100" />
</GridView>
</ListView.View>
</ListView>
ListView1 - EmployeeSetUpView;
<ListView Height="380" HorizontalAlignment="Left" Name="lsNames" VerticalAlignment="Top" Width="170"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Visible"
SelectedItem="{Binding SelectedEmployee}"
ItemsSource="{Binding EmployeeList}" Grid.RowSpan="2" Grid.Row="1">
<ListView.View>
<GridView>
<GridViewColumn Header="FirstName" DisplayMemberBinding="{Binding FirstName}" Width="80" />
<GridViewColumn Header="Surname" DisplayMemberBinding="{Binding Surname}" Width="80" />
</GridView>
</ListView.View>
</ListView>
<ContentControl Grid.Column="1" Grid.Row="1" HorizontalAlignment="Center"
Content="{Binding}" ContentTemplate="{StaticResource EmployeeListView}" />
As you can see, I have put the filter within the setaccessor. I placed it within the constructor but what seems to happen is that none of the details appeared on the ListView2.
Furthermore, if I select a row from listview2 rather then from listview1, it also produces the StackOverFlowException which I am unsure why.
Any help would be appreciated or advice. Also, sorry for the large question!
I don't think the UI knows that EmployeeCollection has changed
Try adding a PropertyChanged event for EmployeeCollection in the SelectedEmployee setter after the filter is applied.
public EmployeeViewModel SelectedEmployee
{
get { return selectedEmployee;}
set
{
selectedEmployee = value;
OnPropertyChanged("SelectedEmployee");
EmployeeCollection.Filter = new Predicate<object>(o => SelectedEmployee != null && o != null && ((EmployeeViewModel)o).EmployeeID == SelectedEmployee.EmployeeID);
// EmployeeCollection view has changed, Notify UI
OnPropertyChanged("EmployeeCollection");
}
}
And as for the StackOverflowException I think this is caused by the fact both ListView have a TwoWay binding on SelectedEmployee, so when one ListView1 changes SelectedItem it causes ListView2 to update its selected item which updates ListView1 and so on, and so on.
Try setting the binding to OneWay for SelectedEmployee on ListView2
SelectedItem="{Binding SelectedEmployee, Mode=OneWay}">

Selected item in a listview doesn't unselect

I can select multiple items in a listview. But if i click on one, it turns blue. That's normal, so that shows it is selected. But if i click again on the same item, it doesnt uncheck. So i can't change my selection. Somebody who knows how to fix this stupid little problem?
Edit: This is my listview:
<ListView Height="155" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible" SelectedItem="{Binding Path=SelectedQuestionDropList, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True" SelectionMode="Multiple" Margin="0,0,542,436" Background="#CDC5CBC5"
dd:DragDrop.DropHandler="{Binding}" Name="DropListView" ItemsSource="{Binding Path=SelectedExaminationQuestions,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" SelectionChanged="ListView_SelectionChanged_1" VerticalAlignment="Bottom">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Verkorte naam" Width="Auto" DisplayMemberBinding="{Binding Path=ShortName}" />
<GridViewColumn Header="Omschrijving" Width="Auto" DisplayMemberBinding="{Binding Path=Description}" />
<GridViewColumn Header="Type" Width="Auto" DisplayMemberBinding="{Binding Path=Type}" />
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
I was facing a similar problem and found that, while left-click always select the item pointed to, you can use Ctrl + left-click to toggle selection in a list view. This is the default behavior.
You can write a wpf behaviour. Something like:
public class ListViewBehaviour
{
/// <summary>
/// Enfoca automaticament el item sel·leccionat
/// </summary>
public static readonly DependencyProperty AutoUnselectItemProperty =
DependencyProperty.RegisterAttached(
"AutoUnselect",
typeof(bool),
typeof(ListViewBehaviour),
new UIPropertyMetadata(false, OnAutoUnselectItemChanged));
public static bool GetAutoUnselectItem(ListView listBox)
{
return (bool)listBox.GetValue(AutoUnselectItemProperty);
}
public static void SetAutoUnselectItem(ListView listBox, bool value)
{
listBox.SetValue(AutoUnselectItemProperty, value);
}
private static void OnAutoUnselectItemChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
var listView = source as ListView;
if (listView == null)
return;
if (e.NewValue is bool == false)
listView.SelectionChanged -= OnSelectionChanged;
else
listView.SelectionChanged += OnSelectionChanged;
}
private static void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
// TODO write custom selection behaviour
}
}
And to apply it to a listview:
<ListView bb:ListViewBehaviour.AutoUnselect="True">
...
</ListView>

WPF - How to center column data in ListView?

I'm still learning WPF, but I'm really confused about something that should be really simple. What I want to do is to center the contents of the 3rd and 4th columns. When I run this, the columns are left justified:
<ListView Margin="0" x:Name="listMonitoredUrls" AlternationCount="1"
ItemsSource="{Binding}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Description" DisplayMemberBinding="{Binding FriendlyDesc}"/>
<GridViewColumn Header="Url" DisplayMemberBinding="{Binding Url}"/>
<GridViewColumn Header="Frequency">
<GridViewColumn.CellTemplate >
<DataTemplate>
<TextBlock Text="{Binding ScanFrequencyMinutes}"
HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Next Scan" >
<GridViewColumn.CellTemplate >
<DataTemplate>
<TextBlock Text="{Binding TimeNextScanStr}"
HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
I"m really starting to like WPF, but some simple things like this seem to be really hard.
Try using the TextAlignment property instead of HorizontalAlignment - should do it.
To my understanding HorizontalAlignment="Center" will center your textblock not the text in it.
This might be a long shot but i've had to do it for listboxes where the items are defined by templates. Try setting the HorizontalContentAlignment="Stretch" on your ListView. If I don't set that the items take only as much space as they need and are left justified.
I've created a solution which works under the common scenario of:
<GridViewColumn Header="Some Property" DisplayMemberBinding="{Binding SomeProperty}" />
where one only wants a simple DisplayMemberBinding with text without having to specify a CellTemplate
the new code uses an attached property and becomes:
<GridViewColumn Header="Some Property" DisplayMemberBinding="{Binding SomeProperty}"
ctrl:GridViewExtensions.IsContentCentered="True" />
attached property code:
public static class GridViewExtensions
{
#region IsContentCentered
[Category("Common")]
[AttachedPropertyBrowsableForType(typeof(GridViewColumn))]
public static bool GetIsContentCentered(GridViewColumn gridViewColumn)
{
return (bool)gridViewColumn.GetValue(IsContentCenteredProperty);
}
public static void SetIsContentCentered(GridViewColumn gridViewColumn, bool value)
{
gridViewColumn.SetValue(IsContentCenteredProperty, value);
}
public static readonly DependencyProperty IsContentCenteredProperty =
DependencyProperty.RegisterAttached(
"IsContentCentered",
typeof(bool), // type
typeof(GridViewExtensions), // containing type
new PropertyMetadata(default(bool), OnIsContentCenteredChanged)
);
private static void OnIsContentCenteredChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
OnIsContentCenteredChanged((GridViewColumn)d, (bool)e.NewValue);
}
private static void OnIsContentCenteredChanged(GridViewColumn gridViewColumn, bool isContentCentered)
{
if (isContentCentered == false) { return; }
// must wait a bit otherwise GridViewColumn.DisplayMemberBinding will not yet be initialized,
new DispatcherTimer(TimeSpan.FromMilliseconds(100), DispatcherPriority.Normal, OnColumnLoaded, gridViewColumn.Dispatcher)
{
Tag = gridViewColumn
}.Start();
}
static void OnColumnLoaded(object sender, EventArgs e)
{
var timer = (DispatcherTimer)sender;
timer.Stop();
var gridViewColumn = (GridViewColumn)timer.Tag;
if (gridViewColumn.DisplayMemberBinding == null)
{
throw new Exception("Only allowed with DisplayMemberBinding.");
}
var textBlockFactory = new FrameworkElementFactory(typeof(TextBlock));
textBlockFactory.SetBinding(TextBlock.TextProperty, gridViewColumn.DisplayMemberBinding);
textBlockFactory.SetValue(TextBlock.TextAlignmentProperty, TextAlignment.Center);
var cellTemplate = new DataTemplate { VisualTree = textBlockFactory };
gridViewColumn.DisplayMemberBinding = null; // must null, otherwise CellTemplate won't be recognized
gridViewColumn.CellTemplate = cellTemplate;
}
#endregion IsContentCentered
}
Here is my example to show a working xaml:
<Window x:Class="WPF_Tutorial.Rich_text_controls.BlockUIContainerCenteredColumnSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:self="clr-namespace:WPF_Tutorial.Rich_text_controls"
Title="BlockUIContainerCenteredColumnSample" Height="275" Width="300"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<x:Array x:Key="UserArray" Type="{x:Type self:User}">
<self:User Name="John Doe" Age="42" />
<self:User Name="Jane May-Anne Josephine Renalds Doe" Age="36" />
</x:Array>
</Window.Resources>
<Grid>
<FlowDocumentScrollViewer>
<FlowDocument>
<Paragraph FontSize="36" Margin="0">Users</Paragraph>
<Paragraph FontStyle="Italic" TextAlignment="Left" FontSize="14" Foreground="Gray">Here's a list of our users, inside our FlowDocument, in a completely interactive ListView control!</Paragraph>
<BlockUIContainer>
<ListView BorderThickness="0" ItemsSource="{StaticResource UserArray}" HorizontalAlignment="Center">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<!-- This stretches out the TextBlock width to the column width -->
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="150" />
<GridViewColumn>
<GridViewColumnHeader Content="Age" Width="75" />
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Age}" TextAlignment="Center" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</BlockUIContainer>
<Paragraph FontStyle="Italic" TextAlignment="Left" FontSize="14" Foreground="Gray">More content can go here...</Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
</Grid>
</Window>
Notice the <ListView.ItemContainerStyle> block. It has the <Setter ....
Without this, as per AndyG's text, nothing will work the way you want.
This has been very frustrating trying to work out.
By the way, here is the backing-code for this xaml:
namespace WPF_Tutorial.Rich_text_controls
{
using System.Windows;
public partial class BlockUIContainerCenteredColumnSample : Window
{
public BlockUIContainerCenteredColumnSample()
{
InitializeComponent();
}
}
public class User
{
public int Age { get; set; }
public string Name { get; set; }
}
}
What you should see when run

Resources