When I expand TreeViewItem with ~3000 items, application will be stuck for about 5 seconds. After searching for solution, I realised that write a custom virtualizing panel is the only way but is difficult for me.
Here is xaml I'm using (simplified):
<controls:FileMapPresenter ItemsSource="{Binding RootSource, Mode=OneWay}"
ItemTemplateSelector="{StaticResource templateSelector}">
<controls:FileMapPresenter.Resources>
<DataTemplate x:Key="folder_template">
<controls:FolderDataPersenter ItemsSource="{Binding Source, Mode=OneWay}"
ItemTemplateSelector="{StaticResource templateSelector}">
<controls:FolderDataPersenter.Header>
<TextBlock Text="{Binding Name, Mode=OneWay}" />
</controls:FolderDataPersenter.Header>
</controls:FolderDataPersenter>
</DataTemplate>
<DataTemplate x:Key="file_template">
<TextBlock Text="{Binding Name, Mode=OneWay}" />
</DataTemplate>
</controls:FileMapPresenter.Resources>
</controls:FileMapPresenter>
controls:FileMapPresenter is derived from ItemsControl, and controls:FolderDataPersenter is derived from TreeViewItem.
FolderDataModel (simplified):
public class FolderData : ViewModelBase
{
public FolderData(string name)
{
this.name = name;
}
private List<FileData> files = new List<FileData>();
public List<FileData> Files
{
get { return this.files; }
}
private List<FolderData> subFolders = new List<FolderData>();
public List<FolderData> SubFolders
{
get { return this.subFolders; }
}
private string name;
public string Name
{
get { return this.name; }
}
private AutoInvokeObservableCollection<object> source =
new AutoInvokeObservableCollection<object>();
/// <summary>
/// ObservableCollection which contains all the files and subFolders
/// </summary>
public AutoInvokeObservableCollection<object> Source
{
get { return this.source; }
}
}
FileDataModel (simplified):
public class FileData
{
public FileData(string name)
{
this.name = name;
}
private string name;
public string Name
{
get { return this.name; }
}
}
in controls:FolderDataPersenter, I try to cancel expanding call and put it into asynchronous, then add a expanding progress reporter to notify UI:
public class FolderDataPersenter : TreeViewItem
{
public FolderDataPersenter()
{
VirtualizingStackPanel.SetIsVirtualizing(this, true);
VirtualizingStackPanel.SetVirtualizationMode(this, VirtualizationMode.Recycling);
this.Dispatcher.BeginInvoke(
(Action)this.setExpandingEventListener,
System.Windows.Threading.DispatcherPriority.Loaded);
}
private void setExpandingEventListener()
{
if (this.Template != null)
{
var expander = this.Template.FindName("Expander", this) as ToggleButton;
if (expander != null)
{
expander.PreviewMouseLeftButtonDown += this.onExpanderPreviewMouseLeftButtonDown;
}
}
}
private void onExpanderPreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
this.Dispatcher.BeginInvoke((Action)delegate
{
this.IsExpanded = !this.IsExpanded; // no work here
}, System.Windows.Threading.DispatcherPriority.Background);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Business.TreeViewExpandingCounter.StepForward();
}
protected override void OnExpanded(RoutedEventArgs e)
{
var folder = this.DataContext as Models.FolderData;
if (folder != null)
{
if (folder.SubFolders.Count > 100)
{
Business.TreeViewExpandingCounter.Finish += this.onExpandingCounterDone;
Business.TreeViewExpandingCounter.Set(folder.SubFolders.Count);
}
}
base.OnExpanded(e);
}
private void onExpandingCounterDone()
{
Business.TreeViewExpandingCounter.Finish -= this.onExpandingCounterDone;
}
}
expanding progress reporter worked well but expanding asynchronously failed, application still stuck while expanding node.
so it seems I have returned to the first question "how to expand TreeViewItem faster?" which I have searched for half a day...
any suggestion?
Related
Basically I want to use TreeView to represent a folder structure. So there maybe be any number of subfolders to navigate into. Can this be done via databinding, or does such logic have to be done all in code? Thanks.
The TreeView control supports binding to a hierarchical data source. You could define a custom class for binding usage.
I made a code sample for you reference:
<Grid>
<TreeView x:Name="treeview" ItemsSource="{x:Bind storageFolders,Mode=OneWay}">
<TreeView.ItemTemplate>
<DataTemplate x:DataType="local:FolderInfo">
<TreeViewItem ItemsSource="{x:Bind subFolders}" Content="{x:Bind FolderName}"/>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<Button Content="folders" Click="Button_Click"></Button>
</Grid>
public class FolderInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(PropertyName));
}
}
private string _FolderName;
public string FolderName
{
get { return _FolderName; }
set
{
if (_FolderName != value)
{
_FolderName = value;
RaisePropertyChanged("FolderName");
}
}
}
public ObservableCollection<FolderInfo> subFolders { get; set; } = new ObservableCollection<FolderInfo>();
public override string ToString()
{
return FolderName;
}
}
public sealed partial class MainPage : Page
{
public ObservableCollection<FolderInfo> storageFolders { get; set; } = new ObservableCollection<FolderInfo>();
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
FolderPicker folderPicker = new FolderPicker();
folderPicker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
folderPicker.FileTypeFilter.Add(".txt");
var folder = await folderPicker.PickSingleFolderAsync();
var Folders = await GetFoldersAsync(folder);
foreach (var f in Folders)
{
storageFolders.Add(f);
}
}
private async Task<ObservableCollection<FolderInfo>> GetFoldersAsync(StorageFolder storageFolder)
{
var folders = await storageFolder.GetFoldersAsync();
ObservableCollection<FolderInfo> folderInfos = new ObservableCollection<FolderInfo>();
foreach (var f in folders)
{
folderInfos.Add(new FolderInfo() {FolderName=f.DisplayName,subFolders=await GetFoldersAsync(f) });
}
return folderInfos;
}
}
Then, when you have new sub folders, you just need to add it to the storageFolders collection.
So I planning to bind label from two files or more, because I place the label and the cs file in separate way. For example:
SettingServicesPhone.xaml
<Label x:Name="sipLoginStatus"
Width="106"
Height="27"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Content="{Binding SipLoginStatusContent}"
FontSize="13" />
For the SettingServicePhone.xaml.cs I declared public String sipLoginStatusContent;
And I use Settings.xaml and Setting.xaml.cs as a container of all functions.
I've declared public static SettingsServicesPhone setCall = new SettingsServicesPhone(); on Setting.xaml.cs. And also write get set.
public String SipLoginStatusContent
{
get { return setCall.sipLoginStatusContent; }
set
{
if (setCall.sipLoginStatusContent != value)
{
setCall.sipLoginStatusContent = value;
OnPropertyChanged("SipLoginStatusContent"); // To notify when the property is changed
}
}
}
And here the example of onclick button that I stated on Settings.xaml.cs
public void applyBtn_Click(object sender, RoutedEventArgs e)
{
SipLoginStatusContent = "Logging In";
}
It's work fine if I included them in one file. But seems like it doesn't running if I make it separate. Am I doing it wrong way? Thank you.
Set the DataContext of the window where the Label is defined to an instance of the class where the SipLoginStatusContent property is defined:
public partial class Settings : Window
{
public static SettingsServicesPhone setCall = new SettingsServicesPhone();
public Settings()
{
InitializeComponent();
DataContext = this; //<--
}
public String SipLoginStatusContent
{
get { return setCall.sipLoginStatusContent; }
set
{
if (setCall.sipLoginStatusContent != value)
{
setCall.sipLoginStatusContent = value;
OnPropertyChanged("SipLoginStatusContent"); // To notify when the property is changed
}
}
}
public void applyBtn_Click(object sender, RoutedEventArgs e)
{
SipLoginStatusContent = "Logging In";
}
}
I have some problem with a custom renderer. My sample is based on a sample from the official Xamarin site but I can't get the OnElementPropertyChanged method in the renderer is not triggered when I add an Item to the bound list.
Cell:
public class NativeiOSListViewCell : UITableViewCell
{
UILabel name;
public NativeiOSListViewCell(NSString cellId) : base(UITableViewCellStyle.Default, cellId)
{
SelectionStyle = UITableViewCellSelectionStyle.Gray;
ContentView.BackgroundColor = UIColor.FromRGB(218, 255, 127);
name = new UILabel()
{
Font = UIFont.FromName("Cochin-BoldItalic", 22f),
TextColor = UIColor.FromRGB(127, 51, 0),
BackgroundColor = UIColor.Clear
};
ContentView.Add(name);
}
public void UpdateCell(string caption)
{
name.Text = caption;
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
name.Frame = new CoreGraphics.CGRect(5, 4, ContentView.Bounds.Width - 63, 25);
}
}
Renderer
[assembly: ExportRenderer(typeof(MyNativeListView), typeof(Blabla.iOS.NativeiOSListViewRenderer))]
namespace Blabla.iOS
{
public class NativeiOSListViewRenderer : ListViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ListView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// Unsubscribe
}
if (e.NewElement != null)
{
Control.Source = new NativeiOSListViewSource(e.NewElement as MyNativeListView);
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == MyNativeListView.ItemsProperty.PropertyName) //Only triggered for stuff like Height, Width and not Items
{
Control.Source = new NativeiOSListViewSource(Element as MyNativeListView);
}
}
}
}
XAML
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Blabla.PlayGroundPage" xmlns:local="clr-namespace:Blabla;assembly=Blabla">
<ContentPage.Resources>
<ResourceDictionary>
<local:Inverter x:Key="inverter" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<local:MyNativeListView x:Name="nativeListView" VerticalOptions="FillAndExpand" Items="{Binding LocalItems}" />
<Button Text="add" Command="{Binding Add}" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
Code Behind:
public partial class PlayGroundPage : ContentPage
{
public PlayGroundPage()
{
InitializeComponent();
PlayGroundViewModel viewModel = new PlayGroundViewModel();
BindingContext = viewModel;
}
}
ViewModel
public class PlayGroundViewModel : INotifyPropertyChanged
{
public ICommand Add { get; private set; }
private ObservableCollection<ListItem> _localItems;
public ObservableCollection<ListItem> LocalItems { get { return _localItems; } set { _localItems = value; SetChangedProperty("LocalItems"); } }
public PlayGroundViewModel()
{
Add = new Command(() => { AddItem(); });
LocalItems = new ObservableCollection<ListItem>();
}
private void AddItem()
{
ListItem item = new ListItem("a", "b", true); //Just to get something to pop up in the list.
LocalItems.Add(item);
SetChangedProperty("LocalItems");
}
public event PropertyChangedEventHandler PropertyChanged;
private void SetChangedProperty(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
ListItem
public class ListItem
{
public string SelectedType { get; set; }
public string SelectedOption { get; set; }
public bool IsChecked { get; set; }
public ListItem(string selectedType, string selectedOption, bool isChecked)
{
SelectedType = selectedType;
SelectedOption = selectedOption;
IsChecked = isChecked;
}
}
I have verified that SetChangedProperty is triggered, but nothing seems to happen after that. I would appreciate if anyone has a clue to why.
The PropertyChanged event is triggered when the property really has changed. Your list doesn't change, only its contents, so the property with the list itself still refers to the same object (the list).
You're using an ObservableCollection, which is the way to go. The thing is that you have to subscribe to the CollectionChanged event in your renderer.
Also, it might be easier to use the default ListView and just create a custom renderer for the cells.
New to WPF and C# from VB web forms, so sorry for this poorly structured question I will add to as needed to improve. I am trying to implement an example by adding database calls to MySQL to populate an On-Demand Tree View control. Here is the link to the sample code...
sample code
Got my db connection working and data is populating my dataset. I iterate to place in a List. But can not seem to figure out the issue with passing the List to the Class to populate the control...
public class Level1
{
public Level1(string level1Name)
{
this.Level1Name = level1Name;
}
public string Level1Name { get; private set; }
readonly List<Level2> _level2s = new List<Level2>();
public List<Level2> Level2s
{
get { return _level2s; }
}
}
I have a database class that queries the db and parses the data....
List<string> level1s = new List<string>();
DataSet ds = new DataSet();
foreach (DataTable table in ds.Tables)
{
foreach (DataRow row in table.Rows)
{
level1s.Add((string)row["name"]);
}
}
**UPDATE**: Trying to return the list...
return new Level1[]
{
foreach(DataRow row in level1s)
{
// iterate here
}
};
My level1s List is properly populated, I am just drawing a blank on returning the values.
thanks,
UPDATE - I am including the ViewModel code here as well....
using BusinessLib;
namespace TreeViewWithViewModelTOC.LoadOnDemand
{
public class Level1ViewModel : TreeViewItemViewModel
{
readonly Level1 _level1;
public Level1ViewModel(Level1 level1)
: base(null, true)
{
_level1 = level1;
}
public string Level1Name
{
get { return _level1.Level1Name; }
}
protected override void LoadChildren()
{
foreach (Level2 level2 in Database.GetLevel2s(_level1))
base.Children.Add(new Level2ViewModel(level2, this));
}
}
}
Try like this below,
List<Level1> L1=new List<Level1>();
foreach(var row in level1s)
{
Level1 L=new Level1();
// L.Level1Name = row.ToString(); here add items as you need
L1.Add(L);
}
return L1.ToArray();
You should be using MVVM design pattern to solve this. There aren't many requirements listed in your questions so I will assume my own, which should lead you along the right path.
First thing is determining whether or not you're records are going to be ready/pulled at run-time--before the TreeView is rendered and if they will be changed/updated/added/removed from the structure during the lifecycle of the application. If the structure isn't going to be changed, you can continue to use List as your collection. If you're (or a user is) going to be adding/removing from the collection, ultimately changing the structure, then you need to notify the UI that a change occurred on the collection; so you would use the built in ObservableCollection for that. Here is a MVVM-purist solution, with the assumption that your data will be pulled at application startup and you will be modifying the collection:
Note: RelayCommand implementation was taken from here
Models
public class First
{
public string Name
{
get;
set;
}
public readonly List<Second> Children;
public First(string name)
{
Name = name;
Children = new List<Second>
{
new Second(1),
new Second(2),
new Second(3),
};
}
public void AddChild(Second child)
{
Children.Add(child);
ChildAdded(this, new ChildAddedEventArgs(child));
}
public EventHandler<ChildAddedEventArgs> ChildAdded;
}
public class ChildAddedEventArgs //technically, not considered a model
{
public readonly Second ChildAdded;
public ChildAddedEventArgs(Second childAdded)
{
ChildAdded = childAdded;
}
}
public class Second
{
public int Number
{
get;
set;
}
public Second(int number)
{
Number = number;
}
}
ViewModels
public class MainViewModel : INotifyPropertyChanged
{
private readonly ObservableCollection<FirstViewModel> _items;
private readonly ICommand _addFirstFirstChildCommand;
private readonly ICommand _addSecondFirstChildCommand;
private readonly ICommand _toggleExpandCollapseCommand;
private bool _firstAddedFlag;
public MainViewModel(IEnumerable<First> records)
{
_items = new ObservableCollection<FirstViewModel>();
foreach(var r in records)
{
_items.Add(new FirstViewModel(r));
}
_addFirstFirstChildCommand = new RelayCommand(param => AddFirst(), param => CanAddFirst);
_addSecondFirstChildCommand = new RelayCommand(param => AddSecond(), param => CanAddSecond);
_toggleExpandCollapseCommand = new RelayCommand(param => ExpandCollapseAll(), param =>
{
return true;
});
}
public ObservableCollection<FirstViewModel> Items
{
get
{
return _items;
}
}
public ICommand AddFirstFirstChildCommand
{
get
{
return _addFirstFirstChildCommand;
}
}
public ICommand AddSecondFirstChildCommand
{
get
{
return _addSecondFirstChildCommand;
}
}
public ICommand ToggleExpandCollapseCommand
{
get
{
return _toggleExpandCollapseCommand;
}
}
public bool CanAddFirst
{
get
{
return true;
}
}
public bool CanAddSecond
{
get
{
//Only allow second to be added if we added to first, first
return _firstAddedFlag;
}
}
public void AddFirstChild(FirstViewModel item)
{
Items.Add(item);
}
private void AddFirst()
{
_items[0].AddChild(new Second(10));
_firstAddedFlag = true;
}
private void AddSecond()
{
_items[1].AddChild(new Second(20));
}
private void ExpandCollapseAll()
{
foreach(var i in Items)
{
i.IsExpanded = !i.IsExpanded;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class FirstViewModel : INotifyPropertyChanged
{
private readonly First model;
private readonly ObservableCollection<SecondViewModel> _children;
private bool _isExpanded;
public FirstViewModel(First first)
{
_children = new ObservableCollection<SecondViewModel>();
model = first;
foreach(var s in first.Children)
{
Children.Add(new SecondViewModel(s));
}
model.ChildAdded += OnChildAdded;
}
public string FirstName
{
get
{
return model.Name;
}
set
{
model.Name = value;
NotifyPropertyChanged();
}
}
public ObservableCollection<SecondViewModel> Children
{
get
{
return _children;
}
}
public bool IsExpanded
{
get
{
return _isExpanded;
}
set
{
_isExpanded = value;
NotifyPropertyChanged();
}
}
internal void AddChild(Second second)
{
model.AddChild(second);
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void OnChildAdded(object sender, ChildAddedEventArgs args)
{
if(Children != null)
{
Children.Add(new SecondViewModel(args.ChildAdded));
}
}
}
public class SecondViewModel : INotifyPropertyChanged
{
private readonly Second model;
private bool _isExpanded;
public SecondViewModel(Second second)
{
model = second;
}
public int SecondNumber
{
get
{
return model.Number;
}
set
{
model.Number = value;
NotifyPropertyChanged();
}
}
//Added property to avoid warnings in output window
public bool IsExpanded
{
get
{
return _isExpanded;
}
set
{
_isExpanded = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Model Provider
public class Database
{
public static IEnumerable<First> GetChildren()
{
List<First> firsts = new List<First>();
firsts.Add(new First("John"));
firsts.Add(new First("Roxanne"));
return firsts;
}
}
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private MainViewModel mvm;
public MainWindow()
{
var db = Database.GetChildren();
mvm = new MainViewModel(db);
InitializeComponent();
DataContext = mvm;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//Do not do this, example only
var f = new First("Billy");
mvm.AddFirstChild(new FirstViewModel(f));
//Prove that the event was raised in First, FirstViewModel see & handles it, and
//the UI is updated
f.AddChild(new Second(int.MaxValue));
}
}
MainWindow.xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow">
<Grid>
<TreeView ItemsSource="{Binding Items}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:FirstViewModel}"
ItemsSource="{Binding Children}">
<TextBlock Text="{Binding FirstName}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:SecondViewModel}">
<TextBlock Text="{Binding SecondNumber}" />
</DataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded"
Value="{Binding IsExpanded, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
<StackPanel Orientation="Vertical"
VerticalAlignment="Bottom">
<StackPanel Orientation="Horizontal">
<Button Content="Add Child to first First"
Command="{Binding AddFirstFirstChildCommand}" />
<Button Content="Toggle Expand"
Command="{Binding ToggleExpandCollapseCommand}" />
<Button Content="Add Child to second First"
Command="{Binding AddSecondFirstChildCommand}" />
</StackPanel>
<Button Content="Bad Codebehind Button"
Click="Button_Click"/>
</StackPanel>
</Grid>
</Window>
this returns array of Level1 from first table in DataSet (usually there's only one table)
public void Level1[] GetLevels()
{
DataSet ds = ....
return ds.Tables[0].Rows
.Select(row => new Level1((string)row["name"]))
.ToArray();
}
if you had more than one table in the dataset, you can use this method to loop trough all tables:
public void Level1[] GetLevels()
{
DataSet ds = ....
return ds.Tables
.SelectMany(t => t.Rows)
.Select(row => new Level1((string)row["name"]))
.ToArray();
}
The second code sample does exactly the same as your code in the question.
Understanding linq is extremely useful.
I am developing windows 8 store app. I wants to show the previously selected items in GridView if navigate back and fro, the selected items should be shown selected.I have tried This tutorial
and did exactly as suggested. but its not working in my case. I have also tried with index as
int index = myGridView.SelectedIndex
so that to find index and directly provide
myGridView.SelectedIndex = index ;
but its again not useful because I am not getting changes into the index in
SelectionChanged(object sender, SelectionChangedEventArgs e){};
What works is
myGridView.SelectAll();
it selects all the elements. but I don't want this. Please help me? Thanks in advance
Please refer my code
<GridView x:Name="MyList" HorizontalAlignment="Left" VerticalAlignment="Top" Width="auto" Padding="0" Height="600" Margin="0" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionMode="Multiple" SelectionChanged="names_SelectionChanged" ItemClick="mylist_ItemClick" SelectedItem="{Binding Path=selectedItem}">
<GridView.ItemTemplate>
<DataTemplate>
<StackPanel Width="260" Height="80">
<TextBlock Text="{Binding Path=Name}" Foreground="White" d:LayoutOverrides="Width" TextWrapping="Wrap"/>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
This is The class I am dealing with
public sealed partial class MyClass: MyApp.Common.LayoutAwarePage, INotifyPropertyChanged
{
SQLite.SQLiteAsyncConnection db;
public MyClass()
{
this.InitializeComponent();
Constants.sourceColl = new ObservableCollection<MyModel>();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
getData();
foreach (MyModel item in Constants.sourceColl)
MyList.SelectedItems.Add(item);
}
private async void getData()
{
List<MyModel> mod = new List<MyModel>();
var query = await db.Table<MyModel>().Where(ch => ch.Id_Manga == StoryNumber).ToListAsync();
foreach (var _name in query)
{
var myModel = new MyModel()
{
Name = _name.Name
};
mod.Add(myModel);
Constants.sourceColl.Add(myModel);
}
MyList.ItemsSource = mod;
}
private void names_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
GridView myGridView = sender as GridView;
if (myGridView == null) return;
Constants.sourceColl = (ObservableCollection<MyModel>)myGridView.SelectedItems;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private MyModel _selectedItem;
public MyModel selectedItem
{
get
{
return _selectedItem;
}
set
{
if (_selectedItem != value)
{
_selectedItem = value;
NotifyPropertyChanged("selectedItem");
}
}
}
}
Here is my model
class MyModel
{
[PrimaryKey, AutoIncrement]
public int id { get; set; }
public String Name { get; set; }
}
Hello rahul I have just solved the problem you are facing it is not the perfect way but it will work in your code. try to follow it.
first I made a singleton class which store your previous selected items (lstSubSelectedItems)..like this
public class checkClass
{
static ObservableCollection<Subject> _lstSubSelectedItems = new ObservableCollection<Subject>();
static checkClass chkclss;
public static checkClass GetInstance()
{
if (chkclss == null)
{
chkclss = new checkClass();
}
return chkclss;
}
public ObservableCollection<Subject> lstSubSelectedItems
{
get
{
return _lstSubSelectedItems;
}
set
{
_lstSubSelectedItems = value;
}
}
}
i have filled lstSubSelectedItems on pagenavigationfrom method like this.. here lstsub is selectedsubjects..
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
checkClass obj = checkClass.GetInstance();
obj.lstSubSelectedItems = lstsub;
}
Here is the workaround what I have done in my constructor...
Here I removed the non selected items using removeat function of gridview.selecteditems other function are not doing this this for for (I don't know why). subject class is just like your model class . and also setting of selecteditems is not working that why I choose this way... Hope this help.
public SelectSubject()
{
this.InitializeComponent(); // not required
objselectsubjectViewmodel = new SelectSubjectViewModel(); // not required
groupedItemsViewSource.Source = objselectsubjectViewmodel.Categories; // not required the way set the itemssource of grid.
this.DataContext = this;
checkClass obj = checkClass.GetInstance();
if (obj.lstSubSelectedItems.Count > 0)
{
// List<Subject> sjsfj = new List<Subject>();
// ICollection<Subject> fg = new ICollection<Subject>();
itemGridView.SelectAll();
// int i = 0;
List<int> lstIndex = new List<int>();
foreach (Subject item1 in itemGridView.SelectedItems)
{
foreach (var item3 in obj.lstSubSelectedItems)
{
if (item3.SubjectCategory == item1.SubjectCategory && item3.SubjectName == item1.SubjectName)
{
lstIndex.Add(itemGridView.SelectedItems.IndexOf(item1));
}
}
}
int l = itemGridView.SelectedItems.Count;
for (int b = l-1; b >= 0; b--)
{
if (!lstIndex.Contains(b))
{
itemGridView.SelectedItems.RemoveAt(b);
}
}
}
}
tell me if it works for you...
You can set selectedItems property of gridView for doing this first make observableCollection and the continuously update this collection on selectionchange Event of your gridView . and when you comeback to this page set the GridViewName.SelectedItems = aboveCollection;
private ObservableCollection<Subject> lstsub = new ObservableCollection<Subject>() ;
private void itemGridView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
checkTemp = 1;
GridView tempObjGridView = new GridView();
tempObjGridView = sender as GridView;
lstsub = tempObjGridView.SelectedItems;
}
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
yourGridName.SelectedItems = lstsub ;
}