Find item in listview with gridview and WPF - wpf

I am looking for a way to know if my listview contains a value. Below is my code.
public class OnlineUserList
{
public string Name { get; set; }
public string Color { get; set; }
}
<ListView x:Name="lvOnlineUsers" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" SelectionMode="Single" SelectionChanged="lvOnlineUsers_SelectionChanged">
<ListView.View>
<GridView x:Name="lvOnlineUsersGridView" AllowsColumnReorder="False">
<GridViewColumn Header="Online Users" Block.TextAlignment="Center" TextOptions.TextFormattingMode="Display" TextBlock.FontWeight="Bold">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Name="tbOnlineUsersGridView" Text="{Binding Path=Name}" Foreground="{Binding Path=Color}" HorizontalAlignment="Center" VerticalAlignment="Center" TextOptions.TextFormattingMode="Display" Style="{StaticResource ResourceKey=lblLabel}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
public void AddUserToList(string username)
{
lvOnlineUsers.Items.Add(new OnlineUserList { Name = username, Color = "Black" });
}
Now this is where am having issue
public void RemoveUserFromList(string username)
{
if(lvOnlineUsers.Items.Contains(username))
lvOnlineUsers.Items.Remove(username);
}

You should learn MVVM.
In the mean time, put the items in an ObservableCollection and assign it to the listview's ItemsSource property in your codebehind. Thereafter, repeat after me: Never, ever touch lvOnlineUsers.Items. Never, never, never. Forget that it exists. Everything you do, you interact with the ObservableCollection. Search it, add items to it, remove items from it. The UI will magically and mysteriously update itself.
I'm going to assume this is in MainWindow. If this is in a different view, the constructor will have a different name.
public MainWindow()
{
InitializeComponent();
lvOnlineUsers.ItemsSource = _onlineUsers;
}
private ObservableCollection<OnlineUserList> _onlineUsers
= new ObservableCollection<OnlineUserList>();
public void AddUserToList(string username)
{
_onlineUsers.Add(new OnlineUserList { Name = username, Color = "Black" });
}
public void RemoveUserFromList(string username)
{
// We don't search _onlineUsers for the string username, because
// _onlineUsers doesn't contain strings. It contains your user class.
// So instead, we look for the user class instance that has the name
// we want.
var found = _onlineUsers.FirstOrDefault(ou => ou.Name == username);
if (found != null)
{
_onlineUsers.Remove(found);
}
}

Until you have looked into MVVM, try this:
for(int i = lvOnlineUsers.Items.Count - 1; i >= 0; --i)
{
OnlineUserList item = lvOnlineUsers.Items[i] as OnlineUserList;
if (item != null && item.Name == username)
lvOnlineUsers.Items.Remove(lvOnlineUsers.Items[i]);
}

Related

An ObservableCollection of StackPanel as ItemsSource of ComboBox in WPF

I am having problem binding ObservableCollection as ItemsSource to a combo box (this combobox is in a listview of having some rows).
I followed A collection of StackPanel as ItemsSource of ComboBox but I did not get any clues for resolving my problem.
Problem:
I was able to add items to a combobox which is at the top on the form.
I have created a listview containing 3 text blocks and 1 combobox.
I am successful in populating data for the text blocks in listview.
But the problem lies with Combobox. First time, it shows all the items for each row in ListView. Once I select item or click on the combobox again to see the items, my list disappears. Only one combobox in the listview row shows all items. Other comboboxes shows blank.
Also, I was trying to save the index of the selected item and show the selected panel next time. But I did not get how to bind the stackpanel with selecteditem and selecteditemvalue.
I tried many ways of directly binding the items to the combobox in listview. But nothing worked. Request someone to help me on this.
Details of the code is given below:
I have XAML like below:
<Grid>
<Grid Height="40">
<ComboBox x:Name="cbList" />
</Grid>
<Grid Margin="0,56,0,168"></Grid>
<ListView x:Name="lvFirst" Margin="0,195,0,12">
<ListView.View>
<GridView >
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Width="50" x:Uid="tbListView1" Text="{Binding FirstName}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Width="50" x:Uid="tbListView2" Text="{Binding LastName}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Width="50" x:Uid="tbListView1" Text="{Binding ID}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="100" x:Uid="cbListView" ItemsSource="{Binding Path=SPForComboBox}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
In the code behind I have a Contact class as below:
public class Contact : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void Notify(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
private string _fn;
private string _ln;
public string FirstName
{
get
{ return _fn; }
set
{ _fn = value; Notify("FirstName"); }
}
public string LastName
{
get
{ return _ln; }
set
{ _ln = value; Notify("LastName"); }
}
private int _id;
public int ID
{
get { return _id; }
set { _id = value; Notify("ID"); }
}
public StackPanel sp;
public override string ToString()
{
return FirstName + " " + LastName;
}
private ObservableCollection<StackPanel> _sp;
public ObservableCollection<StackPanel> SPForComboBox
{
get { return _sp; }
set { _sp = value; Notify("SPForComboBox"); }
}
}
To populate cbList Items, I am repeatedly calling below function after Initialization() function:
private void AddColours(string name, byte hexcolor)
{
//Add this ShiftType to Combo box
SolidColorBrush rectangleBrush = new SolidColorBrush();
Color color = new Color();
color.A = hexcolor;
rectangleBrush.Color = color;
System.Windows.Controls.StackPanel stkPanel = new StackPanel(); //stack panel to hold rectangle + text
stkPanel.Orientation = Orientation.Horizontal;
cbList.Items.Add(stkPanel);
Rectangle colorRect = new Rectangle(); //rectangle showing colour for shift
colorRect.Height = 12;
colorRect.Width = colorRect.Height;
colorRect.Fill = rectangleBrush;
stkPanel.Children.Add(colorRect);
System.Windows.Controls.TextBlock cboText = new TextBlock(); //Name of shift
cboText.Text = name;
cboText.Margin = new Thickness(5, 5, 5, 5);
stkPanel.Children.Add(cboText);
}
In the main window class, I have a created an ObservableCollection object as public static (object name is contacts).
public static ObservableCollection<Contact> contacts = new ObservableCollection<Contact>();
After the Initialization() function, I am populating contacts as below:
AddColours("First", 100);
AddColours("Second", 50);
AddColours("Third", 20);
AddColours("Fourth", 0);
AddColours("Fifth", 80);
Contact c1 = new Contact();
c1.FirstName = "Digo";
c1.LastName = "Maradona";
c1.ID = 0;
c1.SPForComboBox = new ObservableCollection<StackPanel>();
foreach (StackPanel sp in cbList.Items)
{
c1.SPForComboBox.Add(sp);
}
Contact c2 = new Contact();
c2.FirstName = "Brian";
c2.LastName = "Lara";
c2.ID = 1;
c2.SPForComboBox = new ObservableCollection<StackPanel>();
foreach (StackPanel sp in cbList.Items)
{
c2.SPForComboBox.Add(sp);
}
Contact c3 = new Contact();
c3.FirstName = "Sachin";
c3.LastName = "Tendulkar";
c3.ID = 2;
c3.SPForComboBox = new ObservableCollection<StackPanel>();
foreach (StackPanel sp in cbList.Items)
{
c3.SPForComboBox.Add(sp);
}
contacts.Add(c1);
contacts.Add(c2);
contacts.Add(c3);
lvFirst.ItemsSource = contacts;
HighCore, Thank you very much for the links. I have existing implementation and adding combobox to that.
I too felt that the method followed is not good. I shall certainly look at the alternatives provided by you and suggested by pushpraj.
Answer:
I thought referring objects in other combobox will work till the items exist in that combobox. But I need to create rectangle and textblock for reach combobox I am creating and for each entry in that combobox.
So certainly I need to do it in foreach loop.
Also, once I do this I can use SelectedIndex referring to the integer and SelectedItem to sp (individual stackpanel in that class).
This method is not good to follow but might be helpful for somebody.
Thanks.

Gong Solutions Drag drop + using ItemsControl + query

I am using the sample application provided along with the Gong solutions drag drop library.
The solution includes a listbox having itemssource and displaymemberpath set.
I have modified the application to include an itemscontrol and itemTemplate.
But the solution no longer works. There is an exception in the DragInfo.cs file.
Not sure if posting it here is correct.
But can someone help me with this. The sample code is pretty basic.
<ItemsControl
dragDropFramework:DragDrop.IsDragSource="True"
Grid.Column="0" ItemsSource="{Binding Pupils}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding FullName}" BorderBrush="Brown" BorderThickness="2" Margin="2"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding Schools}"
dragDropFramework:DragDrop.DropHandler="{Binding}"
dragDropFramework:DragDrop.IsDropTarget="True"
Grid.Column="1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}" BorderBrush="Brown" BorderThickness="2" Margin="2"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
class PupilViewModel
{
public string FullName { get; set; }
}
internal class WindowViewModel : IDropTarget
{
public ICollectionView Schools { get; private set; }
public ICollectionView Pupils { get; private set; }
public SideWindowViewModel()
{
var pupils = new ObservableCollection<PupilViewModel>
{
new PupilViewModel { FullName = "Alex Thompson" },
new PupilViewModel { FullName = "Tabitha Smith" },
new PupilViewModel { FullName = "Carl Pederson" },
new PupilViewModel { FullName = "Sarah Jones" },
new PupilViewModel { FullName = "Paul Lowcroft" }
};
this.Pupils = CollectionViewSource.GetDefaultView(pupils);
var schools = new SchoolViewModel { Name = "FirstSchool", Pupils = new ObservableCollection<PupilViewModel>() };
this.Schools = CollectionViewSource.GetDefaultView(schools);
}
public void DragOver(DropInfo dropInfo)
{
if (dropInfo.Data is PupilViewModel)// && dropInfo.TargetItem is SchoolViewModel)
{
dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight;
dropInfo.Effects = DragDropEffects.Move;
}
}
public void Drop(DropInfo dropInfo)
{
throw new NotImplementedException();
}
The Window's dataContext, is set to an instance of WindowViewModel.
This code comes with Gong library also as a part of code project.
http://www.codeproject.com/Articles/43614/Drag-and-Drop-in-WPF
original code looks like this
<ListBox Grid.Column="1" ItemsSource="{Binding Schools.CurrentItem.Pupils}" DisplayMemberPath="FullName"
dd:DragDrop.IsDragSource="True" dd:DragDrop.IsDropTarget="True"/>
In case you haven't figured this one out yet - it looks like you commented out where it checks to see if the thing you are dragging over is a SchoolView. Since you are using DropTargetAdorners.Highlight it is trying to highlight what you are dragging over. Since there isn't anything you're getting the null reference error. So maybe go back to this?
public void DragOver(DropInfo dropInfo)
{
if (dropInfo.Data is PupilViewModel) && dropInfo.TargetItem is SchoolViewModel)
{
dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight;
dropInfo.Effects = DragDropEffects.Move;
}
}

Bind NameValueCollection toTextBlock

I have ItemsControlwhich use template to bind data.
<ItemsControl ItemsSource="{Binding MyCollection}" x:Name="MyCollectionControl" ItemTemplate="{DynamicResource MyCollectionTemplate}" />
MyCollection is type of NameValueCollection and The following binding does not work. It is populating correct number of pairs but TextBlock does not get the bounded value.
Template
<DataTemplate x:Key="MyCollectionTemplate">
<Grid>
<TextBlock Text="{Binding Path=Value, Mode=OneWay}"/>
<TextBox Name="CValue"/>
</Grid>
</DataTemplate>
mainWindow
string[] dataCollection=new string[5];
....
....
Student studentObject=new Student("1",dataCollection);
this.dataContext = studentObject;
Student Class
public class Student
{
public string Id;
public NameValueCollection MyCollection {get; set;}
public Student(string id, params string[] additionalInfo)
{
Id = id;
if (additionalInfo != null)
{
MyCollection=new NameValueCollection();
foreach (string s in MyCollection)
{
string[] tokens = s.Split('|');
if (tokens.Length == 2)
MyCollection.Add(tokens[0], tokens[1]);
}
}
}
}
What is the wrong I am doing when binding NameValueCollection.
Please advice me.
OK a couple of things, one you probably want to change your DataTemplate a bit since you are overlaying a textbox directly over your textblock, for my test I just changed it to a stack panel:
<StackPanel>
<TextBlock Text="{Binding}"/>
<TextBox Name="CValue"/>
</StackPanel>
Also notice I changed to simply Text="{Binding}" since the items within the NameValueCollection are simply strings and don't have a value attribute.
Also not sure if this was just another typo, but this:
foreach (string s in MyCollection)
{
string[] tokens = s.Split('|');
if (tokens.Length == 2)
MyCollection.Add(tokens[0], tokens[1]);
}
should probably say:
foreach (string s in additionalInfo)
{
string[] tokens = s.Split('|');
if (tokens.Length == 2)
MyCollection.Add(tokens[0], tokens[1]);
}
otherwise you are just iterating over an empty collection.

Failing to add items in listbox on button Click

I am a newbie to MVVM. I have two grid's inside by main window where one grid contains a listbox towards left side and other grid on the right side contains list view and 2 buttons.
I am able to add items to listview and even figure out how to get the selected item from list view. Once I select the item on list view and click a button(Connect), listbox towards left shud get updated with items which I have added from viewmodel class.
View below shows Listbox towards my left:
<Grid Grid.Column="0" Name="BoardTabSelect" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ListBox Name="ButtonPanel"
ItemsSource="{Binding BoardTabs, Mode=TwoWay}"
SelectedItem="{Binding SelectedTab, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Name="BoardtabChanger"
Margin="53,27,0,0"
Text="{Binding TabOperation}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
View below shows listview towards Right along with 2 Buttons:
<Grid Grid.Row="0" Name="MainConnectGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<ListView Name="listView"
ItemsSource="{Binding Products}"
SelectedItem="{Binding SelectedProduct, Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Width="300"
Header="Name"
DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Width="283"
Header="Connection Status"
DisplayMemberBinding="{Binding Connection_Status}" />
</GridView>
</ListView.View>
</ListView>
<Button Content="Connect"
Height="23" Width="100"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="55,519,0,0"
Name="ConnectBtnGrid"
Command="{Binding Path=ConnectCommand}" />
<Button Content="Disconnect"
Height="23" Width="100"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="446,519,0,0"
Name="DisconnectBtn"
Command="{Binding Path=DisconnectCommand}" />
</Grid>
VIEW MODEL:
public class ProductViewModel : INotifyPropertyChanged
{
public List<Product> m_Products;
public List<Product> m_BoardTabs;
public ProductViewModel()
{
m_Products = new List<Product>()
{
new Product() {Name = "Bavaria", Connection_Status = "Disconnected"},
new Product() {Name = "Redhook", Connection_Status = "Disconnected"},
};
m_BoardTabs = new List<Product>()
{
new Product() {TabOperation = "Connect"}
};
}
public List<Product> Products { get; set; }
public List<Product> BoardTabs { get; set; }
private Product m_SelectedItem;
public Product SelectedProduct
{
get { return m_SelectedItem; }
set
{
m_SelectedItem = value;
NotifyPropertyChanged("SelectedProduct");
}
}
private Product m_SelectedTab;
public Product SelectedTab
{
get { return m_SelectedTab; }
set
{
m_SelectedTab = value;
NotifyPropertyChanged("SelectedTab");
}
}
private ICommand mUpdater;
public ICommand ConnectCommand
{
get
{
if (mUpdater == null)
mUpdater = new DelegateCommand(new Action(SaveExecuted), new Func<bool>(SaveCanExecute));
return mUpdater;
}
set { mUpdater = value; }
}
public bool SaveCanExecute()
{
return true;
}
public void SaveExecuted()
{
if (SelectedProduct.Connection_Status == "Disconnected" && SelectedProduct.Name == "Bavaria")
{
SelectedProduct.Connection_Status = "Connected";
m_BoardTabs.Add(new Product() { TabOperation = "I2C" });
m_BoardTabs.Add(new Product() { TabOperation = "Voltage" });
m_BoardTabs.Add(new Product() { TabOperation = "Codec" });
}
}
}
Inside the Save Executed method I am trying to add the items in listbox but when I run the application and select item from listview and press CONNECT Btn, the list does not get updated. My model class is complete with all three properties (Name, Connection Status and TabOperation)
BoardTabs and Products need to be an ObservableCollection<Product>. Otherwise WPF doesn't get informed about new items in the lists and thus can't update the UI.
Check properies of ProductViewModel. It implements INotifyPropertyChanged , and in Products, BoardTabs, you not notify the change .
public List<Product> Products
{
get
{
return m_Products;
}
set
{
m_Products = value;
NotifyPropertyChanged("Products")
}
}
public List<Product> BoardTabs
{
get
{
return m_BoardTabs;
}
set
{
m_BoardTabs = value;
NotifyPropertyChanged("BoardTabs")
}
}

Why the databinding fails in ListView (WPF)?

I have a ListView of which ItemSource is set to my Custom Collection.
I have defined a GridView CellTemplate that contains a combo box as below :
<ListView
MaxWidth="850"
Grid.Row="1"
SelectedItem="{Binding Path = SelectedCondition}"
ItemsSource="{Binding Path = Conditions}"
FontWeight="Normal"
FontSize="11"
Name="listview">
<ListView.View>
<GridView>
<GridViewColumn
Width="175"
Header="Type">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox
Style="{x:Null}"
x:Name="TypeCmbox"
Height="Auto"
Width="150"
SelectedValuePath="Key"
DisplayMemberPath="Value"
SelectedItem="{Binding Path = MyType}"
ItemsSource="{Binding Path = MyTypes}"
HorizontalAlignment="Center" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</ListView>
public MyType : INotifyPropertyChanged
{
string Key;
string Value;
public string Key { get { return _key; }
set { _key = value; this.OnPropertyChanged("Key"); } }
public string Value { get { return _value; }
set { _value = value; this.OnPropertyChanged("Value"); } }
public MyType ()
{
}
public MyType (string key, string value)
{
_key = key;
_value = value;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
public void MoveUpExecuted()
{
int oldIndex = this.Conditions.IndexOf(_selectedCondition);
//Check if the selected item is not the first item
if (oldIndex != 0)
{
int newIndex = oldIndex - 1;
this.Conditions.Move(oldIndex, newIndex);
}
}
My Custom collection is the ObservableCollection.
I have a two buttons - Move Up and Move Down on top of the listview control . When user clicks on the Move Up or Move Down button I call Move method of Observable Collection.
But when I Move Up and Move Down the rows then the Selected Index of a combo box is -1.
I have ensured that selectedItem is not equal to null when performing Move Up and Move Down commands.
Please Help!!
I don't get this part:
I call MoveUp and MoveDown methods of
Observable Collection.
ObservableCollection does not have such methods, at least not to my knowledge? Neither has it a notion of a current item or similar.
My apologies if I have missed something that could possibly render this post as ignorant.
What you could do is instead of binding your ListView to an ObservableCollection, you could bind it to a ICollectionView (derived from your ObservableCollection). If you set IsSynchronizedWithCurrentItem=True on ListView, you won't need to bind SelectedItem, it will be automatically bound to CurrentItem on ICollectionView.
ICollectionView also implements MoveCurrentToNext and MoveCurrentCurrentToPrevious which can be bound from your buttons (via ICommand).
EDIT:
Now that new information is on the table, my above answer is not really relevant anymore. But I don't (yet) know the SO convention how to handle this, if I should delete the post entirely, edit out the above or just add the "new" reply. For now I'll edit this post.
I tried to recreate your project and your problem. Hopefully I have understood your problem right, and recreated it similarly at least.
As in the problem being the combobox not holding its value when it is being moved in the listview, it works for me.
Below are the relevant code (some of it hidden to avoid too much noise).
Does this help you?
public class MainWindowViewModel:INotifyPropertyChanged
{
private Condition _selectedCondition;
public ObservableCollection<Condition> Conditions { get; set; }
public Condition SelectedCondition
{
get
{
return _selectedCondition;
}
set
{
if (_selectedCondition != value)
{
_selectedCondition = value;
OnPropertyChanged("SelectedCondition");
}
}
}
...
public void MoveUpExecuted()
{
int oldIndex = this.Conditions.IndexOf(_selectedCondition);
//Check if the selected item is not the first item
if (oldIndex != 0)
{
int newIndex = oldIndex - 1;
this.Conditions.Move(oldIndex, newIndex);
}
}
And the condition class:
public class Condition : INotifyPropertyChanged
{
private MyType myType;
public ObservableCollection<MyType> MyTypes { get; set; }
public MyType MyType
{
get { return myType; }
set
{
if (myType != value)
{
myType = value;
OnPropertyChanged("MyType");
}
}
}
public Condition()
{
MyTypes = new ObservableCollection<MyType>() { new MyType() { Key = "1", Value = "One" }, new MyType() { Key = "2", Value = "Two" } };
MyType = MyTypes[1];
}
... etc
<ListView
SelectedItem="{Binding Path=SelectedCondition}"
ItemsSource="{Binding Path=Conditions}"
FontWeight="Normal"
FontSize="11"
Name="listview" Margin="0,32,0,0">
<ListView.View>
<GridView>
<GridViewColumn
Width="175"
Header="Type">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox
Width="150"
SelectedValuePath="Key"
DisplayMemberPath="Value"
SelectedItem="{Binding Path=MyType}"
ItemsSource="{Binding Path=MyTypes}"
HorizontalAlignment="Center" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<Button Content="Move up"
Command="{Binding MoveUpCommand}"
/>
Swap these lines:
SelectedItem="{Binding Path = SelectedCondition}"
ItemsSource="{Binding Path = Conditions}"
ItemSource needs to be before the SelectedItem
Did you try ItemsSource="{Binding Conditions}"

Resources