Binding ListView SelectedItem between two or more ListViews - c#

I have a Dictionary that contains an ObservableCollection like this:
Dictionary<string, ObservableCollection<Person>> MyDictionary
Now in my xaml, I'm creating an itemscontrol that uses the dictionary's key for an expander and the person's collection in a listview something like this:
<ItemsControl ItemsSource="{Binding MyDictionary}" VerticalAlignment="Center" HorizontalAlignment="Left">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Name="expander" IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Margin="5" VerticalAlignment="Stretch" HorizontalAlignment="Left" Text="MyString:"/>
<TextBlock Margin="5" VerticalAlignment="Stretch" HorizontalAlignment="Left" Text="{Binding Key}"/>
</StackPanel>
</Expander.Header>
<Expander.Content>
<ListView SelectionMode="Single" ItemsSource="{Binding Value}">
<ListView.View>
<GridView AllowsColumnReorder="True">
<GridViewColumn>
<GridViewColumn.Header>
<TextBlock Text="Name" Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.Header>
<TextBlock Text="Last Name" Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding LastName}" Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Expander.Content>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now, as you can see it creates a collection of expanders and each expander has a listview within it's content...
I want to let only one listview have a selected item, how can I do it?
If I wasn't clear: I will have 3 Expanders, each one has 1 ListView, each ListView has 4-5 item's, i want that when a user click on a listviewitem all other ListViews selected items will be unselected.
Thanks !

Why don't you subscribe to the selection event of each list view and call UnselectAll on the others.

Hey I have an idea to do this . Bind the SelectedItem of all the listBox to same SelectedItemProperty of ViewModel like
>xaml here I have two ListBox whose SelectedItem is bound to same property of VM
<StackPanel Height="500" Width="500">
<ListBox Height="200" ItemsSource="{Binding StudentList1}" DisplayMemberPath="Name" SelectedItem="{Binding SelectedStudent}"></ListBox>
<ListBox Height="200" ItemsSource="{Binding StudentList2}" DisplayMemberPath="Name" SelectedItem="{Binding SelectedStudent}"></ListBox>
</StackPanel>
xaml.cs
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
ViewModel
public class ViewModel:INotifyPropertyChanged
{
public ViewModel()
{
StudentList1 = new ObservableCollection<Student>();
StudentList1.Add(new Student() { Name = "abc", Age = 20 });
StudentList1.Add(new Student() { Name = "abc", Age = 20 });
StudentList1.Add(new Student() { Name = "abc", Age = 20 });
StudentList2 = new ObservableCollection<Student>();
StudentList2.Add(new Student() { Name = "xyz", Age = 30 });
StudentList2.Add(new Student() { Name = "xyz", Age = 30 });
StudentList2.Add(new Student() { Name = "xyz", Age = 30 });
}
public ObservableCollection<Student> StudentList1 { get; set; }
public ObservableCollection<Student> StudentList2 { get; set; }
Student selectedStudent;
public Student SelectedStudent
{
get { return selectedStudent; }
set { selectedStudent = value; Notify("SelectedStudent"); }
}
public void Notify(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Student Class
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
}
I hope you got an idea .SelectedItem works on the basis of References when you will select one Listbox item the SelectedStudent property of VM will get updated and hence all othe ListBox SelectedItem will get deselected because they dont have this reference in there itemssource. The power of MVVM :)
>Update
Student selectedStudent1;
public Student SelectedStudent1
{
get { return selectedStudent1; }
set {
selectedStudent=null;
selectedStudent1 = value;
Notify("SelectedStudent1"); }
}
Student selectedStudent;
public Student SelectedStudent
{
get { return selectedStudent; }
set {
selectedStudent1=null;
selectedStudent = value;
Notify("SelectedStudent"); }
}

Related

WPF Binding Listbox into ListView (different Source / datacontext)

i have these two classes.
classes:
public class OuterList
{
public string Process { get; set; }
public OuterList(string _process)
{
Process = _process;
}
}
public class InnerList
{
public string Order { get; set; }
public string Product { get; set; }
public InnerList(string _order, string _product)
{
Order = _order;
Product = _product;
}
}
And i will get a Listbox in a ListView.
XAML:
<ListView x:Name="Container" ItemsSource="{Binding OuterList}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Margin="5" FontSize="24" FontFamily="Arial" Background="Red" Text="{Binding Process}"/>
<ListBox x:Name="Orders" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=DataContext.InnerList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Product}"/>
<TextBlock Text="{Binding Order}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Code:
public partial class MainWindow : Window
{
ObservableCollection<OuterList> myContainers = new();
ObservableCollection<InnerList> myItems = new();
public MainWindow()
{
InitializeComponent();
myContainers.Add(new OuterList("Start"));
myContainers.Add(new OuterList("Middle"));
myContainers.Add(new OuterList("End"));
Container.ItemsSource = myContainers;
myItems.Add(new InnerList("34545","SD5"));
myItems.Add(new InnerList("45654", "SD5"));
myItems.Add(new InnerList("65775", "SD5"));
myItems.Add(new InnerList("78677", "SD5"));
myItems.Add(new InnerList("35887", "SD5"));
Orders.ItemsSource = myItems; //<- The name "Orders" does not exist in the current context.
}
}
All the Content of the OuterList will shown, but no Item from InnerList
How can i add Content from two different ItemSources? Or, what am I doing wrong?
Got an issue in Code: The name "Orders" does not exist in the current context.
Thanks for you Help!
Because your DataContext is incorrect.
Assuming the Grid that wraps both of them its DataContext contains Outer and Inner
You can do this:
<Grid>
<ListView x:Name="Container" ItemsSource="{Binding OuterList}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Margin="5" FontSize="24" FontFamily="Arial" Background="Red" Text="{Binding Process}"/>
<ListBox x:Name="Orders" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=DataContext.InnerList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Product}"/>
<TextBlock Text="{Binding Order}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>

Binding a list of object to a WPF listviewitem

I've the following classes in a WPF project
public class part
{
public string number { get; set; }
public string name { get; set; }
public List<department> departments { get; set; }
}
public class department
{
public string name { get; set; }
public double hours { get; set; }
}
Each part contains a list of hours for different departments. What i'm trying to achieve is to view this in a WPF listview. My problem is that i'm not finding a good example on how i could bind the a list of objects to a listviewitem. I had a similiar case in a windows forms app. There i iterated through the objects in the list and created subitems by code. While this would also possible here by creating gridviewcolumns in code i do believe that it should also be achievable via binding or am i mistaken?
Example:
public void Test()
{
List<part> list_parts = new List<part>();
List<department> list_departments = new List<department>();
department d = new department();
d.name = "Sawing";
d.hours = 0.3;
list_departments.Add(d);
d = new department();
d.name = "Miling";
d.hours = 12.3;
list_departments.Add(d);
part Test = new part();
Test.name = "Block";
Test.number = "123";
Test.departments = list_departments;
list_parts.Add(Test);
d = new department();
d.name = "Sawing";
d.hours = 1.2;
list_departments.Add(d);
d = new department();
d.name = "Turning";
d.hours = 5.8;
list_departments.Add(d);
d = new department();
d.name = "Finishing";
d.hours = 5.6;
list_departments.Add(d);
d = new department();
d.name = "QA";
d.hours = 0.5;
list_departments.Add(d);
Test = new part();
Test.name = "Cylinder";
Test.number = "234";
list_parts.Add(Test);
lv_parts.ItemsSource = list_parts;
}
}
My XAML of the listview without binding for the child list
<ListView x:Name="lv_parts" ItemsSource="{Binding list_parts}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50px"/>
<RowDefinition Height="50px"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="{Binding number}"/>
<Label Grid.Row="1 " Content="{Binding name}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The expected outcome would look something like this:
Without any visual styling like e.g. background and foreground colors, your ListView should look like shown below. It uses an ItemsControl with a horizontal StackPanel to show the Departments collection.
<ListView ItemsSource="{Binding Parts}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.Header>
<TextBlock>
<Run Text="Part Number"/>
<LineBreak/>
<Run Text="Part Name"/>
</TextBlock>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Number}"/>
<LineBreak/>
<Run Text="{Binding Name}"/>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.Header>
<TextBlock>
<Run Text="Departement Name"/>
<LineBreak/>
<Run Text="Hours"/>
</TextBlock>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Departments}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Name}"/>
<LineBreak/>
<Run Text="{Binding Hours}"/>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Note that the XAML above use a view model like shown below, with proper casing of class and property names.
public class Part
{
public string Number { get; set; }
public string Name { get; set; }
public List<Department> Departments { get; set; }
}
public class Department
{
public string Name { get; set; }
public double Hours { get; set; }
}
public class ViewModel
{
public ObservableCollection<Part> Parts { get; }
= new ObservableCollection<Part>();
}
An instance of the view model would be assigned to the DataContext of the view:
public MainWindow()
{
InitializeComponent();
var vm = new ViewModel();
DataContext = vm;
vm.Parts.Add(new Part
{
Name = "Block",
Number = "123",
Departments = new List<Department>
{
new Department { Name = "Sawing" , Hours = 0.3 },
new Department { Name = "Milling" , Hours = 12.3 },
}
});
vm.Parts.Add(new Part
{
Name = "Cylinder",
Number = "456",
Departments = new List<Department>
{
new Department { Name = "Sawing" , Hours = 1.2 },
new Department { Name = "Turning" , Hours = 5.8 },
new Department { Name = "Finishing" , Hours = 5.6 },
new Department { Name = "QA" , Hours = 0.5 },
}
});
}
I made changes to your xaml code. You can change the stack panel to grid and add style as per your requirements
<Grid IsSharedSizeScope="True">
<ListView x:Name="lv_parts" ItemsSource="{Binding list_parts}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" SharedSizeGroup="Parts"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" Grid.Column="0" Background="CornflowerBlue">
<Label Content="{Binding number}" Foreground="AliceBlue"/>
<Label Content="{Binding name}" Foreground="AliceBlue"/>
</StackPanel>
<ListView Grid.Row="0" Grid.Column="1" Margin="0" ItemsSource="{Binding departments}" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Background="Coral">
<Label Content="{Binding name}" Foreground="AliceBlue"/>
<Label Content="{Binding hours}" Foreground="AliceBlue"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
The output is as below for the data that you gave:

Drag and Drop between listbox

I want to Drag and Drop Item between Listbox. One is located in MainWindow and one in UserControl. Tab control items are dynamically coded.([VideoListing], [AddTab, AddItem]) I'm curious if this works and then give me directions.
This is what I wanted.
enter image description here
And, this is my Codes
Mainwindow.xaml
<TabControl x:Name="scenarioCB" ItemsSource="{Binding}" Grid.Row="1" HorizontalAlignment="Stretch" Margin="5,0,5,5" VerticalAlignment="Stretch" SelectionChanged="ScenarioCB_SelectionChanged">
<TabControl.ItemTemplate>
<DataTemplate DataType="local:AddTab">
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="local:AddTab">
<ListBox x:Name="listBox" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch" ItemsSource="{Binding Data}" AllowDrop="True" Drop="ListBox_Drop" DragEnter="ListBox_DragEnter" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Visible">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<MediaElement Margin="3" Source="{Binding Path}" Height="64" Stretch="Uniform" IsMuted="True"/>
<TextBlock Margin="3" Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Mainwindow.xaml.cs
var tabs = new ObservableCollection<AddTab>();
for (int i = 0; i < DateListCount; i++)
{
var tab = new AddTab();
tab = new AddTab() { Header = DateList[i] + " - " + TimeList[i] };
tab.Data.Add(new AddData() { TIME = TimeList[i] });
Console.WriteLine("i = {0}, Header = {1}, Time = {2}", i, DateList[i], TimeList[i]);
tabs.Add(tab);
}
DataContext = tabs;
AddTab.cs
class AddTab
{
public string Header { get; set; }
public string Time { get; set; }
public ObservableCollection<AddData> Data { get; } = new ObservableCollection<AddData>();
}
AddData.cs
class AddData
{
public string NAME { get; set; }
public string PATH { get; set; }
}
VideoPanel.xaml
<ListBox Grid.Row="0" x:Name="listBox" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch" AllowDrop="True" Drop="ListBox_Drop" DragEnter="ListBox_DragEnter" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<MediaElement Margin="3" Source="{Binding Path}" Height="64" Stretch="Uniform" IsMuted="True"/>
<TextBlock Margin="3" Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
VideoPanel.xaml.cs
list.Add(new VideoListing()
{
Name = file_name,
Path = file,
});
VideoListing.cs
class VideoListing
{
public string Name { get; set; }
public string Path { get; set; }
}
If you have an example, please post the link.
Drag and Drop between listbox(MainWindow - UserControl)
Tabitems are dynamic coded
I solved this problem. Using by DragDrop.DoDragDrop
When I ran DragDrop.DoDragDrop(DependencyObject dragSourse, object data, DragDropEffects allowedEffects) when I pressed the left mouse button. And when I drop an item into another listbox, generate ListBox_Drop event. In LisetBox_Drop event, Just pull the data out of DragEventArgs.
Listbox - drag
private void ListBox_MouseMove(object sender, MouseEventArgs e)
{
DataObject dataObj = new DataObject();
dynamic Booth = listBox.SelectedItem as dynamic;
if (sender is ListBox && e.LeftButton == MouseButtonState.Pressed)
{
dataObj.SetData("Name", Booth.Name);
dataObj.SetData("Path", Booth.Path);
DragDrop.DoDragDrop(listBox, dataObj, DragDropEffects.Move);
}
}
Listbox - drop
private void ListBox_Drop(object sender, DragEventArgs e)
{
string name = (string)e.Data.GetData("Name");
string path = (string)e.Data.GetData("Path");
}

C# WPF ListView add icon near item

How add near item in listview on left side small icon?
WPF Code:
<ListView Name="listView1" x:FieldModifier="public" HorizontalAlignment="Left" Height="501" Margin="10,10,0,0" VerticalAlignment="Top" Width="312">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
Add item to listview
foreach (var item in found)
{
MainWindow.mainWindow.Dispatcher.Invoke(new Action(delegate ()
{
listView1.Items.Add(item);
}));
}
You just need to amend the DataTemplate as shown below.
POCO List View Item (Data Model):
public class MyListItem
{
public string Icon { get; set; }
public string Text { get; set; }
}
Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var listItemObj = myListView.Items.Add(new MyListItem {Icon = "Default Icon", Text = "Default Text"});
}
}
XAML:
<ListView Name="myListView">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Icon}"/>
<TextBlock Text="{Binding Path=Text}" Padding="5 0 0 0"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Result:

can listbox show both key and value from dictionary

I have a dictionary, there's a int property in MyClass
public Dictionary<MyClass, BitmapImage> MyList = new Dictionary<MyClass, BitmapImage>();
I want listbox to show images from this collection (like ItemsSource?) and to show each property in textboxes near each image, is that possible?
Code behind example:
C#:
public MainWindow()
{
InitializeComponent();
PopulatePeople();
}
private void PopulatePeople()
{
List<Person> persons = new List<Person>();
for (int i = 0; i < 10; i++)
{
persons.Add(new Person() { Name = "Bob " + i.ToString(), ImageAddress = "Images/peach.jpg" });
}
listBox.ItemsSource = persons;
}
XAML:
<ListBox Name="listBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=ImageAddress}" Width="50" Height="50"/>
<TextBox Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
MVVM example:
Download the example from OneDrive.
It is better to use DataTemplate in ListBox to show Image and TextBox.
Model:
public class Person
{
public int IDPerson { get; set; }
public string Name { get; set; }
public string ImageAddress { get; set; }
}
You can fill your collection in constructor of ViewModel:
ViewModel:
public class YourViewModel:INotifyPropertyChanged
{
private ObservableCollection<Person> persons = new ObservableCollection<Person>();
public ObservableCollection<Person> Persons
{
get { return persons; }
set
{
persons = value;
OnPropertyChanged("Persons");
}
}
public YourViewModel()
{
FillThePersons();
}
private void FillThePersons()
{
for (int i = 0; i < 10; i++)
{
persons.Add(new Person() { Name = "Bob " + i.ToString(),
ImageAddress="Images/peach.jpg" });// Images is the name folder in your project
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<ListBox ItemsSource="{Binding Persons}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=ImageAddress}" Width="50" Height="50"/>
<TextBox Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The following example one image to all items:
<ListBox ItemsSource="{Binding Persons}">
<ListBox.Resources>
<BitmapImage x:Key="AnImage" UriSource="Images/yourImage.png" />
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource AnImage}" Width="50" Height="50"/>
<TextBox Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The Result:

Categories