Keep selected items from list box after redrawing box - c#

I have a dev express (MVVM) check box - list box edit (with Caliburn Micro) that allows for multiple select that I am attempting to attach a search function to, it is working as expected, however when I have items selected, then search, the previously selected items are lost. heres my XAML:
<layout:LayoutItem Label="label : " Foreground="White" LabelPosition="Top">
<DockPanel>
<TextBox Text="{Binding Path=SeachItems, UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="Top" Width="400">
</TextBox>
<dxe:ListBoxEdit DockPanel.Dock="Bottom" Foreground="Black" Margin="10" Width="400" DisplayMember="Name" MaxHeight="200" MinHeight="200" ItemsSource="{Binding Path=Items}" EditValue="{Binding Path=SelectedItems}" SelectionMode="Multiple" >
<dxe:ListBoxEdit.StyleSettings>
<dxe:CheckedListBoxEditStyleSettings />
</dxe:ListBoxEdit.StyleSettings>
</dxe:ListBoxEdit>
</DockPanel>
</layout:LayoutItem>
The declarations for the list box (I think this may be part of my issue, as I can not figure out a way to bind my selections to an ObservableCollection, I can only seemt to bind them to a generic list):
public ObservableCollection<Items> Items { get; set; }
public List<object> SelectedItems { get; set; }
And here is my search method:
private string _searchItems;
public string SeachItems
{
get { return _searchItems; }
set
{
_searchItems = value;
var tempItems = SelectedItems;
var items = //fetch all items from collection;
Items = (from p in items where p.Name.ToLower().StartsWith(SeachItems.ToLower()) orderby p.Name select p).ToObservableCollection();
NotifyOfPropertyChange(()=>Items);
SelectedItems = tempItems;
NotifyOfPropertyChange(()=>SelectedItems);
}
}
Although SelectedItems does have a count upon calling the NotifyOfPropertyChange(), upon viewing the actual listbox no items are checked, and the next search clears out the SelectedItems any pointers?
Edit:
In the Setup Model I am populating Items and creating am instance of SelectedItems
Items = //populate observable collection from database
SelectedItems = new List<object>();

You should filter the items with CollectionViewSource. Here is example.
Very basic:
ICollectionView myCollectionVIew = CollectionViewSource.GetDefaultView(items);
myCollectionVIew.Filter = p => { return p.Name.ToLower().StartsWith(SeachItems.ToLower(); };

You can simply bind the selected value. When you redraw, the value will be the same.
<dxe:ListBoxEdit DockPanel.Dock="Bottom" Foreground="Black" Margin="10"
Width="400" DisplayMember="Name" MaxHeight="200" MinHeight="200"
ItemsSource="{Binding Path=Items}"
EditValue="{Binding Path=SelectedItems}"
SelectionMode="Multiple"
SelectedItem = {"Binding myNewVar"}>

Related

In wpf Clear ListBox items at Runtime

I am using wpf listbox, i cannot able to clear the list when am calling the reload data function, i just want to reload new data at runtime,while page loading it loads the data correctly, when i refresh the new data is fetched in itemsource i can see that in debug mode, but no new data in listbox, old data remains in the list, i cant even clear, when i call list.items.clear(), i tried lot ways, is there any problem in my XAML binding, the following is my code.
XAML:
<ListBox ItemsSource="{Binding}" HorizontalContentAlignment="Left" x:Name="lstbxindex" Foreground="White" FontSize="20px" Height="400" BorderBrush="#555555" Margin="10,34,16,0" VerticalAlignment="Top" Width="322" Background="#555555" >
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel Orientation="Horizontal" Margin="5" >
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="txtblckroundhour" Height="40px" Width="55px" Text="{Binding RoundedHours}" FontSize="14" Background="#555555" Loaded="txtblckroundhour_Loaded" Foreground="White"></TextBlock>
<Label x:Name="items" MouseDoubleClick="items_MouseDoubleClick" Content="{Binding ProjectRow.Name}" Background="#555555" FontSize="20" Loaded="items_Loaded" Visibility="Visible" Margin="35,0,0,0" Width="230" Foreground="White"></Label>
</StackPanel>
<StackPanel Orientation="Vertical">
<ComboBox Height="40px" Width="290" Margin="-230,0,0,0" Loaded="ComboBox_Loaded" Visibility="Hidden" IsEditable="True" FontSize="20" Background="White" Foreground="Black"></ComboBox>
</StackPanel>
<!--<ComboBox x:Name="ComboBox_AddItem" Height="40px" Width="290" Margin="-35,35,0,0" Loaded="ComboBox_AddItem_Loaded" IsEditable="True" FontSize="20" Background="White" Visibility="Hidden" Foreground="Black"></ComboBox>-->
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Get the list of values
private List<ProjectInformation> projectInformationList1 = new List<ProjectInformation>();
// Here define the actual binding of the userinterface listbox to the in-memory list of objects.
foreach (DtoProjectsRow row in projectsTable.Rows)
{
projectInformationList1.Add(new ProjectInformation(row));
}
lstbxindex.DataContext = projectInformationList1;
In SO I tried some solution but unfortunately it is not work for me. Last I tried,
XAML.cs page
public static readonly DependencyProperty MyListProperty = DependencyProperty.Register("MyList", typeof(ObservableCollection<String>), typeof(Window));
public ObservableCollection<String> MyList
{
get
{
return (ObservableCollection<String>)GetValue(MyListProperty);
}
set
{
SetValue(MyListProperty, value);
}
}
XAML:
<ListBox ItemsSource="{Binding **ElementName=Window**}" HorizontalContentAlignment="Left" x:Name="lstbxindex" Foreground="White" FontSize="20px" Height="400" BorderBrush="#555555" Margin="10,34,16,0" VerticalAlignment="Top" Width="322" Background="#555555" >
Using this above solution listitems are clear but when pageloading the listboxitems are clear but I don't want to clear the iistboxitems, after updating the values from user it will reload the updated value in listbox.
lstbxindex.ItemsSource = null;
But its not work.For pageload listbox loaded all items,every 15 min interval it will call the load function for firsttime it will reload the updatedvalues but second time it will reload the updated values and previous values remains in listbox again.
I misunderstood initially thinking you were using MVVM, instead you're populating the ListView datasource from code behind.
Your line lstbxindex.DataContext = projectInformationList1; does not set the Data as you'd think. Instead try lstbxindex.DataContext = this; which means you're telling your view to look for the data source in code behind.
As such, I suggest adding using System.ComponentModel; and using BindingList, a comparison is here.
private BindingList<ProjectInformation> projectInformationList1 = new BindingList<ProjectInformation>();
And you just need this once:
foreach (DtoProjectsRow row in projectsTable.Rows)
{
projectInformationList1.Add(new ProjectInformation(row));
}
lstbxindex.DataSource = projectInformationList1;
As mentioned in the comments, if you did not use ItemsSource="{Binding projectInformationList1}" as I suggested in a comment to your question, this is the alternative:
private ObservableCollection<ProjectInformation> projectInformationList1 = new ObservableCollection<ProjectInformation>();
foreach (DtoProjectsRow row in projectsTable.Rows)
{
projectInformationList1.Add(new ProjectInformation(row));
}
lstbxindex.DataContext = projectInformationList1;
You should have a view model class with a collection property, e.g. like this:
public class ViewModel
{
public ObservableCollection<ProjectInformation> Projects { get; }
= new ObservableCollection<ProjectInformation>();
}
Set the DataContext of your Window or Page in XAML like this:
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
and bind the ListBox like this:
<ListBox ItemsSource="{Binding Projects}">
...
</ListBox>
To clear all items in the source collection, access the DataContext in code behind:
var vm = (ViewModel)DataContext;
vm.Projects.Clear();
Edit: Instead of assigning the DataContext in XAML, you may as well do it in code behind, even before the Page or Window is initialized:
public MainWindow()
{
DataContext = new ViewModel();
InitializeComponent();
}
Added the line in loadfunction, Initially set null for ItemSource and then set null to the list object
lstbx.ItemsSource=null;
lstbx.Items.Clear();
ProjectInfoList1=null;
it will clear the listboxitems and reload with updated values only.
private BindingList<ProjectInfo> projectInfoList1 = new BindingList<ProjectInfo>();
Public void loadfunction()
{
lstbx.ItemsSource=null;
lstbx.Items.Clear();
ProjectInformationList1=null;
foreach (DtoProRow row in table.Rows)
{
projectInfoList1.Add(new ProjectInfo(row));
}
lstbx.DataContext = projectInfoList1;
}

Unable to reselect single item in listview

I have a listview in WPF in an MVVM/PRISM app which may contain 1-to-many elements. When the listview contains only 1 element, and I select it, I cannot subsequently reselect it even though I set the SelectedIndedx value to -1. Worse, if I make the app update the listview with a different single element, I can't select that one either. The only way I can achieve selection of an item when it is the only item in the listview is to make the app display multiple items and select something other than the first. Then, when I make the app display a listview containing a single item, I can select it again - but only once.
In those cases where I cannot select the single item in the listview, the servicing routine never fires.
I tried implementing a XAML suggestion I found here using "Listview.Container.Style" and the IsSelected property, but that did not work.
My listview is fairly straightforward:
<ListView Name="lstEditInstance"
Grid.Row="5"
ItemsSource="{Binding Path=InstanceList,Mode=TwoWay}"
Width="488"
FontFamily="Arial" FontSize="11"
HorizontalAlignment="Left" VerticalAlignment="Stretch"
Margin="10,96,0,28"
SelectedIndex="{Binding Path=InstanceSelectedIndex}">
</ListView>
The servicing routine is:
private void OnInstanceSelectedIndexChanged()
{
// Handle case where user hits Enter without making a selection:
if (_instanceIndex == -1) return;
// Get the instance record for the row the user clicked on as a
// ResourceInstance class named "InstanceRecord".
InstanceRecord = _instanceList[_instanceIndex];
_instanceNumber = InstanceRecord.Instance;
FormInstName = InstanceRecord.InstName;
FormInstEnabled = InstanceRecord.Enabled;
FormInstState = InstanceRecord.InitialState;
FormInstIPAddress = InstanceRecord.IPAddress;
FormInstPort = InstanceRecord.Port.ToString();
FormInstSelectedURL = InstanceRecord.UrlHandler;
} // End of "OnResourceSelectedIndexChanged" method.
"InstanceList" is an observable collection.
I'd appreciate some suggestions. Thanks in advance for any help.
In a MVVM scenario, I'd use a ViewModel that contains the selected item instead:
class MyViewModel {
private IList<Item> instanceList= new List<Item>();
public IList<Item> List
{
get {return list; }
set {
list = value;
RaisePropertyChanged(() => List);
}
}
private Item selectedItem;
public Item SelectedItem {
get {return selectedItem;}
set {
selectedItem = value;
RaisePropertyChanged(() => SelectedItem);
}
}}
And the XAML:
<ListView Name="lstEditInstance"
Grid.Row="5"
ItemsSource="{Binding Path=InstanceList}"
Width="488"
FontFamily="Arial" FontSize="11"
HorizontalAlignment="Left" VerticalAlignment="Stretch"
Margin="10,96,0,28"
SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}}">
Notice that observableCollection is not required unless you have to modify the list items, in the same way the binding should be the default one for the list.
The SelectedItem / SelectedIndex should be TwoWay or Onewaytosource, the latter if you think you don't need to change the selectedItem programmatically
The service routine should be called from the ViewModel
EDIT:
your code of the service routine should be placed there:
set {
selectedItem = value;
// your code
RaisePropertyChanged(() => SelectedItem);
}
Another valid approach is to use Blend on XAML, by invoking a command on changed index and process under the ViewModel.
To do this, first add reference to System.Windows.Interactivity in your project and in XAML add
xmlns:interactivity="http://schemas.microsoft.com/expression/2010/interactivity
Then modify ListView with the following:
<ListView Name="lstEditInstance"
Grid.Row="5"
ItemsSource="{Binding Path=InstanceList}"
Width="488"
FontFamily="Arial" FontSize="11"
HorizontalAlignment="Left" VerticalAlignment="Stretch"
Margin="10,96,0,28"
SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}}">
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="SelectionChanged">
<interactivity:InvokeCommandAction Command="{Binding YourCommand}"
CommandParameter="{Binding YourCommandParameter}" />
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>

secondary listbox not show data

i've two listbox binded with properties of my viewmodel, the first listbox shows LstEmpresas and work fine, when I select a item, the property SelectedCompany sets fine, all ok.
In SelectedCompany's set of my viewmodel, I call a method than pupulate a secondary list (LtsEjercicios) and work fine too (LtsEjercicios populate perfectly depends that item i've selected in the first listbox).
The secondary listbox binds his ItemSource from LtsEjercicios object that, across viewmodel, is updated.
But the secondary listbox NOT SHOW any data, i'm crazy yet.
This viewModel code
public class frmEmpresasVM : ViewModelBase
{
//propiedades
EmpresasDataAccess empresasController = new EmpresasDataAccess();
private ObservableCollection<EmpresasTable> lstEmpresas;
private ObservableCollection<EmpresasEjerciciosTable> ltsEjercicios;
public ObservableCollection<EmpresasTable> LstEmpresas
{
get
{
return lstEmpresas;
}
set
{
lstEmpresas = value; RaisePropertyChanged("LstEmpresas");
}
}
public ObservableCollection<EmpresasEjerciciosTable> LtsEjercicios
{
get
{
return ltsEjercicios;
}
set
{
ltsEjercicios = value; RaisePropertyChanged("LtsEjercicios");
}
}
//selected company in listbox
private int selectedCompany;
public int SelectedCompany
{
get
{
return selectedCompany;
}
set
{
selectedCompany = value;
LtsEjercicios = empresasController.SelectExercicesById(selectedCompany.ToString());
RaisePropertyChanged("SelectedCompany");
}
}
//main constructor, default values for lists
public frmEmpresasVM()
{
LstEmpresas = empresasController.SelectOnlyNames();
LtsEjercicios = empresasController.SelectExercicesById("0");
}
and, XAML for the view
<ListBox x:Name="companyList" HorizontalAlignment="Left" Height="205" Margin="20,30,0,0" VerticalAlignment="Top" Width="450" ItemsSource="{Binding LstEmpresas, Mode=OneWay}" SelectedValue="{Binding SelectedCompany, Mode=TwoWay}" SelectedItem="{Binding LtsEjercicios, Mode=OneWay}" SelectedIndex="0" DisplayMemberPath="Nombre" SelectedValuePath="Id" IsSynchronizedWithCurrentItem="True" SelectionChanged="companyList_SelectionChanged_1">
<ListBox.ItemBindingGroup>
<BindingGroup/>
</ListBox.ItemBindingGroup>
<ListBox.DataContext>
<InnovaCommerceHosteleryViewModels:frmEmpresasVM/>
</ListBox.DataContext>
</ListBox>
<TextBlock x:Name="lblEjercicio" HorizontalAlignment="Left" Height="20" Margin="475,10,0,0" TextWrapping="Wrap" Text="Ejercicios" VerticalAlignment="Top" Width="95"/>
<ListBox x:Name="excercicesList" HorizontalAlignment="Left" Height="205" Margin="475,30,0,0" VerticalAlignment="Top" Width="110" ItemsSource="{Binding LtsEjercicios, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Ejercicio" SelectedValuePath="Id" SelectedIndex="0" IsSynchronizedWithCurrentItem="True">
<ListBox.DataContext>
<InnovaCommerceHosteleryViewModels:frmEmpresasVM/>
</ListBox.DataContext>
Al Data provides from a MySQL Database from both tables (empresas y empresas_ejercicios).
My goal is, when user select a item in listbox1 (empresas) show exercices in listbox2 (empresas_ejercicios).
If exist other path to determine this operation, i'm all eyes!!!
Thanks in advance.
Not sure if this is the only problem in your code, but what you're doing by setting
<ListBox.DataContext>
<InnovaCommerceHosteleryViewModels:frmEmpresasVM/>
</ListBox.DataContext>
for both list boxes is creating a separate instance of the ViewModel for each list box. Now I guess if you make any changes in the first list box' ViewModel instance, these changes will not be reflected in the second list box because this one binds to a totally different ViewModel instance.
Instead, try globally binding the ViewModel to the whole window / page / user control / etc. (depending on whether you;re doing WPF, Windows Phone, etc.), and let the list boxes inherit it to ensure that only one ViewModel instance is involved:
<Window.DataContext>
<InnovaCommerceHosteleryViewModels:frmEmpresasVM/>
</Window.DataContext>
EDIT:
Alternatively, you might as well instantiate the ViewModel once and store it as global resource:
<Window.Resources>
<InnovaCommerceHosteleryViewModels:frmEmpresasVM x:Key="Viewmodel" />
</Window.Resources>
and in the listboxes' DataContext just reference this global resouce:
<ListBox DataContext="{StaticResource Viewmodel}" ... />

get checked items of listview using checkbox windows store app c#

I am developing one Windows store application. I have implemented one listview. listview contains image , textblock and checkbox controls. my listview gets the data from internet i have done xml parsing with listview and binded data to listview. i want to get all the data from listview where checkboxes are checked in listview.
my xaml code is:
<ListView Name="display" ItemsSource="{Binding}" SelectionMode="Single"
SelectionChanged="display_SelectionChanged"
ScrollViewer.HorizontalScrollMode="Enabled" ScrollViewer.HorizontalScrollBarVisibility="Visible"
ItemContainerStyle="{StaticResource ListViewItemStyle12}" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel x:Name="stak2" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Image Source="{Binding Path=Image}" Width="450" Tapped="image_taped" />
<CheckBox Tag="{Binding Path=tag}" Visibility="{Binding Path=visichk}" Height="40" Name="addremove"
HorizontalAlignment="Center" Checked="add_checked" Unchecked="sub_checked" Opacity="0.5"
Background="White" VerticalAlignment="Top" Template="{StaticResource CheckboxImageTemplate}" >
</CheckBox>
<TextBlock Text="{Binding Image_code}" FontSize="25" Foreground="Gray" HorizontalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
datasource for listview :
XDocument xmlDoc = XDocument.Parse(responseString);
var Categories = xmlDoc.Descendants("product").ToArray();
List<ProductData> displaylst = new List<ProductData>(); //ProductData is my Class.
foreach (var cat in Categories)
{
string prId = cat.Elements("id_products").Select(r => r.Value).FirstOrDefault();
List<string> Image = cat.Descendants("images").Elements("src").Attributes("largimage").Select(r => r.Value).ToList();
List<string> Image_code = cat.Descendants("images").Elements("src").Select(r => r.LastAttribute.Value).ToList();
int i = 0;
foreach (string img in Image)
{
displaylst.Add(new ProductData { Id = prId, Image = img, Image_code = Image_code[i] });
i++;
}
}
display.ItemsSource = displaylst;
Now on one button click i want to get the data of Product like prId,Image,Image_code where checkbox are checked from listview and put it into the simple list.
how can i did this please help me. thanks in advance.
First let's add a property to your ProductData class
public class ProductData
{
public string Id { get; set; }
public string Image { get; set; }
// I dont know exactly what's in this class
// ... more properties
// Add this one
public bool IsSelected { get; set; }
}
Now that we have a boolean IsSelected in our ProductData class we can know which are selected.
In the second foreach change this line
// Set IsSelected to false by default
displaylst.Add(new ProductData { IsSelected = false, Id = prId, Image = img, Image_code = Image_code[i] });
And bind the "IsChecked" property of your checkbox to IsSelected
<CheckBox IsChecked="{Binding Path=IsSelected}" Tag="{Binding Path=tag}" Visibility="{Binding Path=visichk}" Height="40" Name="addremove"
HorizontalAlignment="Center" Checked="add_checked" Unchecked="sub_checked" Opacity="0.5"
Background="White" VerticalAlignment="Top" Template="{StaticResource CheckboxImageTemplate}" >
With binding when you check one of the checkbox, the associed productData IsSelected property will become "true" automatically.
So now you just have to do a new list and select only ProductData where IsSelected is true:
List<ProductData> listOfSelectedProducts = (from product in displaylst
where product.IsSelected == true
select product).ToList();
Here you go you got a list of ProductData with only selected products.

binding data - inotifypropertychanged does not work

I have a listBox1 in which data are binding from the list. Then I want to when I select any item from listBox1 in listBox2 will binding data from another list.
private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Teams teams = (Teams)listBox1.SelectedItems[0];
getH2hResults("//td[#class='hell']", teams.Team1, teams.Team2); // add elements to list
getH2hResults("//td[#class='dunkel']", teams.Team1, teams.Team2); // and here also
listBox2.ItemsSource = lists.h2hList;
}
On the first time this work, but for the twice time listBox2 doesn't displays new data.
public class Lists : BindableBase
{
public Lists()
{
_teamsList = new List<Teams>();
_h2hList = new List<H2H>();
}
private List<Teams> _teamsList;
public List<Teams> teamsList
{
get
{
return _teamsList;
}
set
{
if (value != _teamsList)
{
_teamsList = value;
RaisePropertyChanged("teamsList");
}
}
}
private List<H2H> _h2hList;
public List<H2H> h2hList
{
get
{
return _h2hList;
}
set
{
if (value != _h2hList)
{
_h2hList = value;
RaisePropertyChanged("h2hList");
}
}
}
}
And XAML
<ListBox Name="listBox1" Width="300" Height="300"
VerticalAlignment="Top"
HorizontalAlignment="Left"
ItemsSource="{Binding teamsList}" SelectionChanged="listBox1_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="#FF4273CD" Text="{Binding Team1, Mode=TwoWay}"></TextBlock>
<TextBlock Text=" vs " FontWeight="Bold"></TextBlock>
<TextBlock Foreground="#FF4273CD" Text="{Binding Team2, Mode=TwoWay}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Name="listBox2" Grid.Column="1" Width="300" Height="300"
VerticalAlignment="Top"
HorizontalAlignment="Left"
ItemsSource="{Binding h2hList}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding date, Mode=TwoWay}"></TextBlock>
<TextBlock Text="{Binding result, Mode=TwoWay}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
RaisePropertyChanged("teamList");
is Wrong your propery is named 'teamsList' with an S,
change to:
RaisePropertyChanged("teamsList");
It is the public property you bind to and notify changes of,
edit:
also change your binding:
ItemsSource="{Binding teamList}"
to
ItemsSource="{Binding teamsList}"
Edit 2:
listBox2.DataContext = xxx
Not itemsource = xxx
With the line (in listBox1_SelectionChanged)
listBox2.ItemsSource = lists.h2hList;
you are effectively removing the binding from the ItemsSource property of listBox2.
Instead, you should only update the h2hList property in your Lists class (which presumably happens in getH2hResults) and remove the above line from your code.
Note however that it is not sufficient to clear and re-fill that list. You need to set the h2hList property in order to get a property change notification raised:
var newList = new List<H2H>();
// fill newList before assigning to h2hList property
lists.h2hList = newList;
If you want to keep the list and just change its elements, you would need to use ObservableCollection<H2H> instead of List<H2H> as collection type. This would be the better approach anyway, as you would not have to care for when exactly you add elements to a newly created collection.

Categories

Resources