How to set SelectedItem in listview control? - wpf

I have UserControl(AllCustomerView) in project and its Viewmodel as ALlCustomerViewModel consists a property as SearchText.
SearchText is selected value for TextBox inside a listview in UserControl.
SearchItem is set as customerViewmdel for SearchText.
But in listview , SearchItem is not set as selected
in AllCustomerView.xaml
<TextBlock>
<TextBox
Width="150"
Text="{Binding Path=SearchText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</TextBox>
<Button
Command="{Binding Path=SearchCommand}"
Content=" Search ">
</Button>
</TextBlock>
<ListView
SelectedValue="{Binding Path=SearchText}"
ItemsSource="{Binding Path=AllCustomers}"
FontFamily="Tahoma"
FontSize="14"
ItemContainerStyle="{StaticResource CustomerItemStyle}"
IsSynchronizedWithCurrentItem="True">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button
Command="{Binding ElementName=Root, Path=DataContext.DeleteCommand}"
Content="x"
FontFamily="Tahoma"
FontSize="10"
/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button
FontFamily="Tahoma"
FontSize="10"
Content="Edit"
Command="{Binding Path=DataContext.Editcommand,ElementName=Root}"
/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn
Header="CustomerID"
Width="130"
DisplayMemberBinding="{Binding Path=CustomerID}"/>
<GridViewColumn
Header="Contact Name"
Width="130"
DisplayMemberBinding="{Binding Path=ContactName}"/>
<GridViewColumn
Header="Company Name"
Width="130"
DisplayMemberBinding="{Binding Path=CompanyName}"/>
<GridViewColumn
Width="130"
Header="Contact Name"
DisplayMemberBinding="{Binding Path=ContactTitle}"
/>
</GridView>
</ListView.View>
</ListView>
and its viewmodel(AllcustomerViewModel.cs)
public ICommand SearchCommand
{
get
{
if (_search == null)
_search = new Relaycommand(SearchCustomer);
return _search;
}
}
public void SearchCustomer(object o)
{
System.Windows.Forms.MessageBox.Show(SearchItem.ToString());
}
public string searchText;
public string SearchText
{
get { return searchText; }
set
{
searchText = value;
var customerExsits = AllCustomers.Where(q => q.CustomerID.Trim().Equals(searchText));
if (customerExsits.Any())
{
SearchItem = customerExsits.Single();
}
}
}
public CustomerViewModel SearchItem
{
get;
set;
}
what should i set in SelectedValue in ListView, whether to set Customerviewmodel(as SelectedItem) or to set CustomerID(as SearchText)?

You should do the following:
Use this binding in your ListView: SelectedItem="{Binding Path=SearchItem }". Don't use SelectedValue.
Implement INotifyPropertyChanged in your ViewModel and raise the PropertyChanged event in the setter of the SearchItem property. Obviously, you need to change this property from an automatic one to a classical one with a backing field:
public CustomerViewModel SearchItem
{
get { return _searchItem; }
set
{
if(value == _searchItem)
return;
_searchItem = value;
RaisePropertyChanged("SearchItem");
}
}

Related

How can i bind data in ListViewItem?

I have a ListView with some Columns. One of Column contains two RadioButton.When i checked Radiobutton,i want change the binding value of the last column(it's header Name is Fee).I have no idea,can someone help me ?
<ListView x:Name="lvTimeView">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="ID" Width="175" DisplayMemberBinding="{Binding serverId}"></GridViewColumn>
<GridViewColumn Header="IP" Width="175" DisplayMemberBinding="{Binding serverIp}"></GridViewColumn>
<GridViewColumn x:Name="gvcTime" Header="Time" Width="400">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="30">
<RadioButton x:Name="rbOneMonth" Tag="1" Checked="rbChecked" Style="{StaticResource BoxRadioButton}" HorizontalAlignment="Left">OneMonth</RadioButton>
<RadioButton x:Name="rbTwoMonth" Tag="2" Margin="8,0,0,0" Checked="rbChecked" Style="{StaticResource BoxRadioButton}" HorizontalAlignment="Left">TwoMonth</RadioButton>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Fee" Width="100" DisplayMemberBinding="{Binding fee}">
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
First, forget about events in this example. This is a perfect example to leverage WPF binding...
Change your XAML to this.
<ListView ItemsSource="{Binding Items}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="ID" Width="175" DisplayMemberBinding="{Binding ServerId}"></GridViewColumn>
<GridViewColumn Header="IP" Width="175" DisplayMemberBinding="{Binding ServerIp}"></GridViewColumn>
<GridViewColumn Header="Time">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<RadioButton GroupName="{Binding ServerId}" IsChecked="{Binding OneMonth}">OneMonth</RadioButton>
<RadioButton GroupName="{Binding ServerId}" IsChecked="{Binding TwoMonth}" Margin="5,0,0,0">TwoMonth</RadioButton>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Fee" Width="100" DisplayMemberBinding="{Binding Fee}">
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
Pay attention to RadioButtons GroupName property. This is just so the radio buttons know they are a part of a radio button group so when you select one the other one unselects. We have to set this to something unique per row so I chose serverId. I hope this is unique in your case otherwise you will have to generate some Guid or something...
Next is the model...
public class Item : INotifyPropertyChanged
{
private string _serverId;
private string _serverIp;
private bool _oneMonth;
private bool _twoMonth;
private decimal _fee;
public event PropertyChangedEventHandler PropertyChanged;
public string ServerId
{
get => _serverId;
set
{
_serverId = value;
OnPropertyChanged(nameof(ServerId));
}
}
public string ServerIp
{
get => _serverIp;
set
{
_serverIp = value;
OnPropertyChanged(nameof(ServerIp));
}
}
public bool OneMonth
{
get => _oneMonth;
set
{
_oneMonth = value;
if(value)
Fee = 100;
OnPropertyChanged(nameof(OneMonth));
}
}
public bool TwoMonth
{
get => _twoMonth;
set
{
_twoMonth = value;
if(value)
Fee = 200;
OnPropertyChanged(nameof(TwoMonth));
}
}
public decimal Fee
{
get => _fee;
set
{
_fee = value;
OnPropertyChanged(nameof(Fee));
}
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Instantiate the model and add it to the data context...
public partial class MainWindow : Window
{
public IEnumerable<Item> Items => new Item[]
{
new Item { ServerId = "asia", ServerIp = "10.1.1.45" },
new Item { ServerId = "europe", ServerIp = "45.1.4.211" },
new Item { ServerId = "australia", ServerIp = "37.2.1.181" }
};
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
}

List View Selected Item Binding in wpf mvvm

I am using a ListView in wpf mvvm pattern whose SelectedItem binding is done to the ViewModel. The problem what I am facing is as soon as I check the checkbox, The SelectedItem binding is not working immediately. It work only when I click again somewhere outside the checkbox and its respective content.
My ListView is like this:
<Grid>
<Grid.Resources>
<DataTemplate x:Key="checkboxHeaderTemplate">
<CheckBox IsChecked="{Binding Path=DataContext.AllSelected,RelativeSource={RelativeSource AncestorType=UserControl },Mode=TwoWay}">
</CheckBox>
</DataTemplate>
<DataTemplate x:Key="CheckBoxCell">
<!--<CheckBox Checked="CheckBox_Checked" />-->
<CheckBox IsChecked="{Binding Path=IsSelected, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
</CheckBox>
</DataTemplate>
<DataTemplate x:Key="TextCell">
<TextBlock Text="Usecasename">
</TextBlock>
</DataTemplate>
<DataTemplate x:Key="ButtonCell">
<Button Content="{Binding Path=UsecaseName, Mode=TwoWay}" >
</Button>
</DataTemplate>
</Grid.Resources>
<ListView SelectedItem="{Binding SelectedSection}" ItemsSource="{Binding Path=UsecaseListItems}" >
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn HeaderTemplate="{StaticResource checkboxHeaderTemplate}"
CellTemplate="{StaticResource CheckBoxCell}" Width="auto">
</GridViewColumn>
<GridViewColumn HeaderTemplate="{StaticResource TextCell}"
CellTemplate="{StaticResource ButtonCell}" Width="auto">
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</Grid>
The HomeViewModel with which I am binding the Selected itm of List View is like this:
private UseCase _selectedSection;
public UseCase SelectedSection
{
get { return _selectedSection; }
set
{
_selectedSection = value;
if (this.SelectedSection.UsecaseName == ("CCS01") && (this.SelectedSection.IsSelected == true))
{
this.ContentWindow = new CCS01();
}
else if (this.SelectedSection.UsecaseName == ("CCS02") && (this.SelectedSection.IsSelected == true))
{
this.ContentWindow = new CCS02();
}
else if (this.SelectedSection.UsecaseName == ("ECS52") && (this.SelectedSection.IsSelected == true))
{
this.ContentWindow = new ECS52();
}
else
this.ContentWindow = new Default();
OnPropertyChanged("SelectedSection");
}
}
and The UseCase class is this:
public class UseCase: BaseNotifyPropertyChanged
{
public string UsecaseName { get; set; }
private bool _IsSelected;
public bool IsSelected
{
get { return _IsSelected; }
set
{
_IsSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
Please suggest what correction should I do so that, It should hit the binding directly as I check the Checkboxes.
You should change the checkbox binding of the CheckBoxCell to something like this :
<DataTemplate x:Key="CheckBoxCell">
<!--<CheckBox Checked="CheckBox_Checked" />-->
<CheckBox IsChecked="{Binding Path=IsSelected,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListViewItem}},
Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
</CheckBox>
</DataTemplate>
And for this work you need to put SelectionMode to Single
<ListView SelectedItem="{Binding SelectedSection}"
ItemsSource="{Binding Path=UsecaseListItems}" SelectionMode="Single">
And do this in your viewmodel
private UseCase _selectedSection;
public UseCase SelectedSection
{
get { return _selectedSection; }
set
{
DeSelectAll();
_selectedSection = value;
_selectedSection.IsSelected = true;
OnPropertyChanged("SelectedSection");
}
}
private void DeSelectAll()
{
foreach (var item in UsecaseListItems)
{
item.IsSelected = false;
}
}

MVVM light listview selecteditem

How can I get the selecteditem from my listview in mvvm light wpf?
I have a collection and created a selected item property but I cant get the binding right for the selected item.
This is my viewmodel:
ObservableCollection<DTO.Dossier.Dossier> _dossiers;
public ObservableCollection<DTO.Dossier.Dossier> Dossiers
{
get { return _dossiers; }
set
{
_dossiers = value;
RaisePropertyChanged("Dossiers");
}
}
private DTO.Dossier.Dossier _selectedDossier;
public DTO.Dossier.Dossier SelectedDossier
{
get { return _selectedDossier; }
set
{
if (_selectedDossier != value)
_selectedDossier = value;
RaisePropertyChanged("SelectedDossier");
}
}
And this is the xaml for the listview:
<ListView ItemsSource="{Binding Dossiers}" Margin="0,5,0,0" Name="LstDossiers" SelectedItem="{Binding SelectedDossier, Mode=OneWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding SelectDossierCommand}"
CommandParameter="{Binding SelectedDossier,
ElementName=LstDossiers}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListView.View>
<GridView>
<GridViewColumn Header="Id"
DisplayMemberBinding="{Binding Id}" />
<GridViewColumn Header="Omschrijving"
DisplayMemberBinding="{Binding Omschrijving}" />
</GridView>
</ListView.View>
</ListView>
The command is working but the binding to the SelectedDossier isn't.
You have to use mode TwoWay in your binding:
<ListView ItemsSource="{Binding Dossiers}" Margin="0,5,0,0" Name="LstDossiers" SelectedItem="{Binding SelectedDossier, Mode=TwoWay}">

WPF - Listview with button

i have a listview template and one column is a button. I need selected item when i click in this button. How i can do this ??
To cature the selected ListView item inside a button pressed event you can leverage the MVVM pattern. In my ListView, in the XAML, I bind the ItemsSource and SelectedItem to a ViewModel class. I also bind my button Command in the template to RunCommand in the ViewModel.
The tricky part is getting the binding correct from the template to the active DataContext.
Once you do this you can capture the SelectedCustomer inside the RunCommand that
gets executed when the button gets pressed.
I've included some of the code to help get you started.
You can find implementations of ViewModelBase and DelegateCommand via Google.
Here is the XAML:
<Window x:Class="ListViewScrollPosition.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main Window" Height="400" Width="400">
<Grid>
<ListView ItemsSource="{Binding Path=Customers}"
SelectedItem="{Binding Path=SelectedCustomer}"
Width="Auto">
<ListView.View>
<GridView>
<GridViewColumn Header="First Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Margin="6,2,6,2">
<TextBlock Text="{Binding FirstName}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Last Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Margin="6,2,6,2">
<TextBlock Text="{Binding LastName}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Address">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Margin="6,2,6,2">
<Button Content="Address"
Command="{Binding
Path=DataContext.RunCommand,
RelativeSource=
{RelativeSource FindAncestor,
AncestorType={x:Type ItemsControl}}}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Here is the ViewModel:
using System.Collections.ObjectModel;
using System.Windows.Input;
using ListViewScrollPosition.Commands;
using ListViewScrollPosition.Models;
namespace ListViewScrollPosition.ViewModels
{
public class MainViewModel : ViewModelBase
{
public ICommand RunCommand { get; private set; }
public MainViewModel()
{
RunCommand = new DelegateCommand<object>(OnRunCommand, CanRunCommand);
_customers = Customer.GetSampleCustomerList();
_selectedCustomer = _customers[0];
}
private ObservableCollection<Customer> _customers =
new ObservableCollection<Customer>();
public ObservableCollection<Customer> Customers
{
get
{
return _customers;
}
}
private Customer _selectedCustomer;
public Customer SelectedCustomer
{
get
{
return _selectedCustomer;
}
set
{
_selectedCustomer = value;
OnPropertyChanged("SelectedCustomer");
}
}
private void OnRunCommand(object obj)
{
// use the SelectedCustomer object here...
}
private bool CanRunCommand(object obj)
{
return true;
}
}
}
Here is where I link in the ViewModel to the View:
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
DataContext = new ViewModels.MainViewModel();
}
}
Example with a regular click event in the code behind:
<ListView Height="167.96" VerticalAlignment="Top" ItemsSource="{Binding FulfillmentSchedules}" SelectedItem="{Binding SelectedFulfillmentSchedule}">
<ListView.View>
<GridView>
<GridViewColumn Header="Request">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}-{1}-{2}">
<Binding Path="Template.ProjectNumber" />
<Binding Path="Template.JobNumber" />
<Binding Path="Template.RequestId" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Template" DisplayMemberBinding="{Binding Template.Name}"/>
<GridViewColumn Header="Start Date" DisplayMemberBinding="{Binding StartDate}"/>
<GridViewColumn Header="Records" DisplayMemberBinding="{Binding Parameters.Records}"/>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Name="BtnYourButton" Content="Your Button" Click="BtnYourButton_Click" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Code behind:
private void BtnYourButton_Click(object sender, RoutedEventArgs e)
{
var boundData= (YourBoundDataType)((Button)sender).DataContext;
//do what you need to do here, including calling other methods on your VM
}
Note: While I certainly appreciate MVVM, I've come to accept that there is a pretty steep slope of dimminishing returns once you cross into actions and messaging between the form and the VM, so I use it only in cases of complex relationships between VMs or large singular VMs. For CRUD style data-centric applications I prefer to handle actions and message relay with the code behind.

WPF: Progress bar in ListView

I'm trying to display information from my ObservableCollection<MyData> in a ListView. MyData has:
string Name
string Location
int Progress
Using DataBinding, I'm able to display the Name and Location for all the items in my ObservableCollection<MyData> in their own column. But how can I add a Progress column with a ProgressBar inside? Progress is a percentage.
<ListView ItemsSource="{Binding PersonList}">
<ListView.View>
<GridView>
<GridViewColumn Width="140" Header="GameName" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Width="140" Header="Progress">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ProgressBar Maximum="100" Value="{Binding Progress}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Your ListView in XAML:
<ListView x:Name="DataView">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Path=Name}" />
<ProgressBar Height="20" Width="100" Value="{Binding Path=Progress}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Code-behind:
internal class MyData
{
public string Name { get; set; }
public int Progress { get; set; }
}
...
var items = new ObservableCollection<MyData>();
items.Add(new MyData() { Name = "Some", Progress = 25 });
items.Add(new MyData() { Name = "Another", Progress = 91 });
DataView.ItemsSource = items;
Just bind Progress property in MyData to the ProgressBar.Value and set the expected MAX value as the ProgressBar.Maximum for example 50 below
<ProgressBar Maximum="50" Value="{Binding Progress}" ..

Resources