I am developing an application in WPF in which I want to populate a ListBox on selecting a node of a TreeView. I have populated the TreeView using MVVM pattern. It actually contains the the drives (C:\, D:...) and their corresponding sub-folders. The sub-folders are the nodes. On selecting these nodes the respective files should be shown in the ListBox. I know the C# code to get all the files in a folders, I have also implemented the same. However, I am not getting any clue to map them, so that on selecting the nodes the files in them should get reflected in the ListBox.
Can any please help me in this regard? The application is being developed in MVVM pattern and I need in the same pattern itself.
First add a Files collection class to your folder class (used in the tree view)
public class FolderItem
{
// other class code
private ObservableCollection<File> _Files = null;
public ObservableCollection<File> Files
{
get
{
if (_Files == null) _Files = GetFiles();
return _Files;
}
set
{
_Files = value;
}
}
}
Then bind the listbox to the selected treeview item.
<ListBox ItemsSource="{Binding ElementName=myTreeView, Path=SelectedItem.Files}"/>
You might have quite a number of files and folders so I think I would be inclined to lazy load as much as I could.
That means the viewmodel doesn't need to go iterate through the entire hard drive initially but you need some way of acting when selecteditem changes.
You can't bind selecteditem to a viewmodel because it's read only.
I would therefore use a behaviour like:
Data binding to SelectedItem in a WPF Treeview
Bind a SelectedFolder using that.
In the setter of SelectedFolder go get the list of folders and files for that folder and fill the two collections. One is the child collection of that selecteditem - for it's folders.
The other is an observableCollection for the files to see in the listbox.
Make that a propfull and implement inotifyproprtychanged so when I set it to a new collection it notifies the ui.
Bind that collection to the itemssource of the listbox.
Basicly The MVVM pattern uses three Layers :
The Model : Basicly it contains the Model classes and the business logic to get and manipulate Data information.
The ViewModel : It acts as an intermediate layer between the Model and the Views, it is attached to the different views.
The views : The différent views of the app.
Here an example how to fill a Window with list of Drives and Files.
Class BindableBaseViewModel
namespace TalkRepeater.ViewModel
{
public class BindableBaseViewModel : DependencyObject,INotifyPropertyChanged
{
protected virtual void SetProperty<T>(ref T member, T val,[CallerMemberName] string propertyName = null)
{
if (object.Equals(member, val)) return;
member = val;
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
}
Class ViewModel
public class FoldersControlViewModel : BindableBaseViewModel
{
private ObservableCollection<Folders> _listFolders;
private ObservableCollection<Folders> _listFiles;
public FoldersControlViewModel()
{
FoldersBusinessObject vbo =new FoldersBusinessObject()
vbo.FillFolders();
ListFolders = FileBusinessObject.ListFolders;
}
public ObservableCollection<Folders> ListFolders
{
get
{
return _listFolders;
}
set
{
_listFolders = value;
OnPropertyChanged("ListFolders");
}
}
public ObservableCollection<Folders> ListFiles
{
get
{
return _listFiles;
}
set
{
_listFiles = value;
OnPropertyChanged("ListFiles");
}
}
Public void FillListFiles()
{
/*ListFiles= Cod to fill ListFiles*/
}
}
Class BusinessObject
public class FoldersBusinessObject
{
private ObservableCollection<Folders> _ListFolders;
public void FillFolders()
{
/* ListFolders= Code To fill the collection ListFolders */
}
public ObservableCollection<Folders> ListFolders
{
get
{
return _ListFolders;
}
set
{
_ListFolders = value;
}
}
}
Foldersview
<Window x:Class="Foldersview"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
d:DesignHeight = "300" Width="1007" Height="606">
<Grid Margin="10" >
<Canvas x:Name="canvasFolders" Margin="-10,0,912,10">
<TreeView x:Name="TreevFolders" ItemsSource="{Binding Path=ListFolders, Mode=TwoWay}" Canvas.Top="5" Canvas.Left="17" Width="142" Height="561"
SelectedItemChanged="TreevFolders_SelectedItemChanged" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=ListFolders}">
<TextBlock Text="{Binding Path=FileName}">
</TextBlock>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Canvas>
<Canvas Margin="159,10,0,10">
<Listview x:Name="Listview1" ItemsSource="{Binding ListFiles, Mode=TwoWay}" >
</Listview>
</Canvas>
</Grid>
</Window>
Class Foldersview Code Behind
public partial class Foldersview : Window
{
private void TreevFolders_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
FoldersControlViewModel vmd = (FoldersControlViewModel)this.DataContext;
vmd.FillListFiles ();
}
}
Class Mainwindow
public class MainWindowViewModel : BindableBase
{
private FoldersControlViewModel FoldersviewModel;
public MainWindowViewModel()
{
FoldersviewModel = new FoldersControlViewModel();
Foldersview=new Foldersview();
Foldersview.Datacontext=FoldersviewModel;
}
}
Cordialy
Related
I'm developing a WPF application and I'm struggling a little bit to understand some of the details of DataContext as it applies to binding. My application uses a business object which is defined like this:
public class MyBusinessObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
// enumerations for some properties
public enum MyEnumValues
{
[Description("New York")]
NewYork,
[Description("Chicago")]
Chicago,
[Description("Los Angeles")]
LosAngeles
}
// an example property
private string _myPropertyName;
public string MyPropertyName
{
get { return _myPropertyName; }
set
{
if (_myPropertyName == value)
{
return;
}
_myPropertyName = value;
OnPropertyChanged(new PropertyChangedEventArgs("MyPropertyName"));
}
}
// another example property
private MyEnumValues _myEnumPropertyName;
public MyEnumValues MyEnumPropertyName
{
get { return _myEnumPropertyName; }
set
{
if (_myEnumPropertyName== value)
{
return;
}
_myEnumPropertyName= value;
OnPropertyChanged(new PropertyChangedEventArgs("MyEnumPropertyName"));
}
}
// example list property of type Widget
public List<Widget> MyWidgets { get; set; }
// constructor
public MyBusinessObject()
{
// initialize list of widgets
MyWidgets = new List<Widget>();
// add 10 widgets to the list
for (int i = 1; i <= 10; i++)
{
MyWidgets.Add(new Widget());
}
// set default settings
this.MyPropertyName = string.empty;
}
}
As you can see, I have some properties that are declared in this class one of which is a list of Widgets. The Widget class itself also implements INotifyPropertyChanged and exposes about 30 properties.
My UI has a combobox which is bound to my list of Widgets like this:
MyBusinessObject myBusinessObject = new MyBusinessObject();
public MainWindow()
{
InitializeComponent();
this.DataContext = myBusinessObject;
selectedWidgetComboBox.ItemsSource = myBusinessObject.MyWidgets;
selectedWidgetComboBox.DisplayMemberPath = "WidgetName";
selectedWidgetComboBox.SelectedValuePath = "WidgetName";
}
The majority of the controls on my UI are used to display the properties of a Widget. When my user selects a Widget from the combobox, I want these controls to display the properties for the selected Widget. I'm currently achieving this behavior by updating my window's DataContext in the SelectionChanged event handler of my combobox like this:
private void selectedWidgetComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.DataContext = selectedWidgetComboBox.SelectedItem;
}
This allows me to bind my controls to the appropriate Widget property like this:
<TextBox Text="{Binding WidgetColor}"></TextBox>
However, not all of the controls in my UI are used to display Widget properties. Some of the controls need to display the properties from MyBusinessObject (for example: MyPropertyName defined above). In this scenario, I can't simply say:
<TextBox Text="{Binding MyPropertyName}"></TextBox>
...because the DataContext of the window is pointing to the selected Widget instead of MyBusinessObject. Can anyone tell me how I set the DataContext for a specific control (in XAML) to reference the fact that MyPropertyName is a property of MyBusinessObject? Thank you!
Instead of changing the DataContext of your window, you should add a property to your MyBusinessObject class like this one:
private Widget _selectedWidget;
public Widget SelectedWidget
{
get { return _selectedWidget; }
set
{
if (_selectedWidget == value)
{
return;
}
_selectedWidget = value;
OnPropertyChanged(new PropertyChangedEventArgs("SelectedWidget"));
}
}
Then bind SelectedWidget to the SelectedItem property of your combobox. Anywhere that you need to use the widget's properties you can do this:
<TextBox Text="{Binding Path=SelectedWidget.WidgetColor}"></TextBox>
try
<TextBox Text="{Binding MyBusinessObject.MyPropertyName}"></TextBox>
this works if MyBusinessObject is the datacontext of the textbox and MyPropertyName is a property of MyBusinessObject
Also, Here is a good article to clarify binding
hope this helps
EDIT 1:
use a relative binding like this:
text="{Binding DataContext.MyPropertyName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TypeOfControl}}}"
So the relatve binding allows you to look up the visual tree to another UI element and use its datacontext. I would consider wrapping your window's contents in a grid. and wet your windows datacontext to the businessobject and the grids datacontext to the widget. That way you can always use the parent window's datacontext through the realtive source binding.
so use the following if your window's datacontext is your business object
text="{Binding DataContext.MyPropertyName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
I have a mvvm(model view viewmodel) silverlight application that has several views that need to be loaded into ContentControls (i made it all in expression blend). What i dont know how to do is, for example, to load one view (user control) in one content control by clicking a button from another view that is in another content control. To make it easier to understand the problem, i need to do something similar to this:
http://www.codeproject.com/KB/silverlight/BlendableVMCom.aspx
with that difference that child1 and child2 are supposed to be loaded into theirown content controls by clicking Call child1 or call child2 buttons.
and example would be appreciated. Thanks in advance!
This example is very simplified, but I think you now how to adjust it to your application.
The main view:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border x:Name="commandsView">
<Button Content="Call view 1" Command="{Binding CallView1Command}" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="5" />
</Border>
<Border x:Name="displayedView" Grid.Column="1">
<ContentControl Content="{Binding CurrentView}" />
</Border>
</Grid>
I haven't created separated views as user controls, here are just borders, which can be replaced by real views.
Different view models for different views in code behind:
this.commandsView.DataContext = new CommandsViewModel();
this.displayedView.DataContext = new DisplayedViewModel();
First view model conains the command which sends the message to another view model:
public class CommandsViewModel
{
public CommandsViewModel()
{
this.CallView1Command = new RelayCommand(() =>
Messenger.Default.Send<View1Message>(new View1Message()));
}
public RelayCommand CallView1Command { get; set; }
}
public class View1Message : MessageBase
{
}
To make this example work, download the MVVM Light library.
The second view model receive the message and creates a view for its property:
public class DisplayedViewModel : ViewModelBase
{
public DisplayedViewModel()
{
Messenger.Default.Register<View1Message>(this, obj =>
this.CurrentView = new TextBlock { Text = "Pressed the button 1 and now here is the view 1" });
}
private object currentView;
public object CurrentView
{
get { return currentView; }
set
{
currentView = value;
RaisePropertyChanged("CurrentView");
}
}
}
Again, it is possible to use clr object instead of controls and apply data templates in xaml, but there will not be enough space to provide all the resulting code.
So that is all, the main idea is a some kind of event aggregator, which is the Messenger class in this particular case.
Without the MVVM Light it will require more code:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
var events = new GlobalEvents();
this.commandsView.DataContext = new CommandsViewModel(events);
this.displayedView.DataContext = new DisplayedViewModel(events);
}
}
public class GlobalEvents
{
public event EventHandler View1Event = delegate { };
public void RaiseView1Event()
{
View1Event(this, EventArgs.Empty);
}
}
/// <summary>
/// Commands which call different views
/// </summary>
public class CommandsViewModel
{
public CommandsViewModel(GlobalEvents globalEvents)
{
this.CallView1Command = new DelegateCommand(globalEvents.RaiseView1Event);
}
public DelegateCommand CallView1Command { get; set; }
}
/// <summary>
/// Model where views are changed and then displayed
/// </summary>
public class DisplayedViewModel : INotifyPropertyChanged
{
public DisplayedViewModel(GlobalEvents globalEvents)
{
globalEvents.View1Event += (s,e) =>
this.CurrentView = new TextBlock { Text = "Pressed the button 1 and now here is the view 1" };
}
private object currentView;
public object CurrentView
{
get { return currentView; }
set
{
currentView = value;
RaisePropertyChanged("CurrentView");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
In this example you must change the DelegateCommand class for something different. Other code will work for everyone.
It sounds like you might be trying to do some sort of navigation. If that's true, check out the Silverlight navigation framework.
I have an ObservableCollection of "Layouts" and a "SelectedLocation" DependencyProperty on a Window. The SelectedLocation has a property called "Layout", which is an object containing fields like "Name" etc. I'm trying to bind a combobox to the SelectedLayout but it's not working.
The following does not work, I've tried binding to SelectedItem instead to no avail. I believe it may be something to do with the fact that I'm binding to a subProperty of the SelectedLocation DependencyProperty (though this does implement INotifyPropertyChanged.
<ComboBox Grid.Row="2" Grid.Column="0" x:Name="cboLayout" ItemsSource="{Binding Layouts,ElementName=root}" SelectedValue="{Binding SelectedLocation.Layout.LayoutID,ElementName=root}" DisplayMemberPath="{Binding Name}" SelectedValuePath="LayoutID" />
However, the following works (Also bound to the "SelectedLocation" DP:
<TextBox Grid.Row="4" Grid.Column="1" x:Name="txtName" Text="{Binding SelectedLocation.Name,ElementName=root,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
What type property Layouts has? I suppose something like this this: IEnumerable<Layout>.
But you bind selected value to Layout.LayoutID. So you got situation, when combo box contains Layout objects, and you try to select it by Int identifier. Of course binding engine can't find any Int there.
I have no idea about details of your code, so one thing I could propose: try to reduce your binding expression: SelectedItem="{Binding SelectedLocation.Layout,ElementName=root}.
If no success, provide more code to help me understand what's going on.
====UPDATE====
As I've said, you are obviously doing something wrong. But I am not paranormalist and couldn't guess the reason of your fail (without your code). If you don't want to share your code, I decided to provide simple example in order to demonstrate that everything works. Have a look at code shown below and tell me what is different in your application.
Class Layout which exposes property LayoutId:
public class Layout
{
public Layout(string id)
{
this.LayoutId = id;
}
public string LayoutId
{
get;
private set;
}
public override string ToString()
{
return string.Format("layout #{0}", this.LayoutId);
}
}
Class SelectionLocation which has nested property Layout:
public class SelectedLocation : INotifyPropertyChanged
{
private Layout _layout;
public Layout Layout
{
get
{
return this._layout;
}
set
{
this._layout = value;
this.OnPropertyChanged("Layout");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
var safeEvent = this.PropertyChanged;
if (safeEvent != null)
{
safeEvent(this, new PropertyChangedEventArgs(name));
}
}
}
And Window class with dependency properties (actually, in my example StartupView is UserControl, but it doesn't matter):
public partial class StartupView : UserControl
{
public StartupView()
{
InitializeComponent();
this.Layouts = new Layout[] { new Layout("AAA"), new Layout("BBB"), new Layout("CCC") };
this.SelectedLocation = new SelectedLocation();
this.SelectedLocation.Layout = this.Layouts.ElementAt(1);
}
public IEnumerable<Layout> Layouts
{
get
{
return (IEnumerable<Layout>)this.GetValue(StartupView.LayoutsProperty);
}
set
{
this.SetValue(StartupView.LayoutsProperty, value);
}
}
public static readonly DependencyProperty LayoutsProperty =
DependencyProperty.Register("Layouts",
typeof(IEnumerable<Layout>),
typeof(StartupView),
new FrameworkPropertyMetadata(null));
public SelectedLocation SelectedLocation
{
get
{
return (SelectedLocation)this.GetValue(StartupView.SelectedLocationProperty);
}
set
{
this.SetValue(StartupView.SelectedLocationProperty, value);
}
}
public static readonly DependencyProperty SelectedLocationProperty =
DependencyProperty.Register("SelectedLocation",
typeof(SelectedLocation),
typeof(StartupView),
new FrameworkPropertyMetadata(null));
}
XAML of StartupView:
<UserControl x:Class="Test.StartupView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:self="clr-namespace:HandyCopy"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="Root">
<WrapPanel>
<ComboBox ItemsSource="{Binding Path=Layouts,ElementName=Root}"
SelectedItem="{Binding Path=SelectedLocation.Layout, ElementName=Root}"/>
</WrapPanel>
</UserControl>
hello i'm building a wpf app with data grids,
the pattern is model view view model.
all og my screens contains a contentcontrol, and i just assign him the view model, that have a suitable data template,
anyway, my problem is with combo box column, the data context is the presented entity, and i need it to be the view model.
whats the best solution?
I'm using another datagrid, but it might be similar. The way i did it was like that:
in the XAML, i defined an ObjectDataProvider in the resources:
<ObjectDataProvider x:Key="VM" ObjectInstance="{x:Null}" x:Name="vm"/>
then after assigning the DataContext (either the constructor or the DataContextChanged event), i did this:
(this.Resources["VM"] as ObjectDataProvider).ObjectInstance = this.DataContext;
In the Combobox xaml, i used that as binding source:
ItemsSource="{Binding Source={StaticResource VM}, Path=SomeItems, Mode=OneWay}"
Not sure if it works for the microsoft datagrid, but i guess it's worth a try.
this is how I used ViewModel with ComboBoxes, the DataContext is the ViewModel, not the underlying entity (List<Person>).
ViewModel (Person is a Simple class with Name and Age):
public class PeopleViewModel : INotifyPropertyChanged
{
private List<Person> _peopleList;
private Person _selectedPerson;
public PeopleViewModel()
{
// initialize with sample data
_peopleList = getPeopleList();
}
// gets sample data
private List<Person> getPeopleList()
{
var result = new List<Person>();
for (int i = 0; i < 10; i++)
{
result.Add(new Person("person " + i, i));
}
return result;
}
public List<Person> PeopleList
{
get { return _peopleList; }
}
public Person SelectedPerson
{
get { return _selectedPerson; }
set
{
if (_selectedPerson == value) return;
_selectedPerson = value;
// required so that View know about changes
OnPropertyChanged("SelectedPerson");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
// WPF will listen on this event for changes
public event PropertyChangedEventHandler PropertyChanged;
}
XAML for ComboBox:
<ComboBox Name="cmbEnum" Width="150" ItemsSource="{Binding Path=PeopleList}" SelectedValue="{Binding Path=SelectedPerson}" SelectedValuePath="" DisplayMemberPath="Name" ></ComboBox>
And in code behind I can do:
public Window2()
{
InitializeComponent();
vm = new PeopleViewModel();
// we are listening on changes of ViewModel, not ComboBox
vm.PropertyChanged += new PropertyChangedEventHandler(vm_PropertyChanged);
this.DataContext = vm;
}
void vm_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedPerson")
{
MessageBox.Show(vm.SelectedPerson.Age.ToString());
}
}
// button1_Click should be probably replaced by Command
private void button1_Click(object sender, RoutedEventArgs e)
{
// sample showing that GUI is updated when ViewModel changes
vm.SelectedPerson = vm.PeopleList[2];
}
Hope this helps, I'm quite new to WPF, I'd like to hear any feedback if this is the right way to use MVVM, I think it's quite elegant since you only deal with the ViewModel and Model in code, and the View can be replaced.
I Found that the best way of implementing this is define some external class for all lookups that i use in grid and embedd them in the template as a static resource
We ended up having classes with static properties for each of of our combo box lists:
(you can't make the class itself static otherwise XAML won't be able to open it, but you won't get compile errors)
For example:
public class ZoneList
{
private static readonly IList<Zone> _Items = new List<Zone>();
public static IList<Zone> Items
{
get { return _Items; }
}
}
and then in XAML:
<UserControl.Resources>
<ResourceDictionary>
<ObjectDataProvider x:Key="myZoneList" ObjectType="{x:Type StaticLists:ZoneList}"/>
</ResourceDictionary>
</UserControl.Resources>
<ComboBox ItemsSource="{Binding Path=Items, Source={StaticResource myZoneList}}"></ComboBox>
I have a screen with a ListBox of items. The item template contains an expander control with some of the data in the header and some of the data in the content part of the expander.
The data template for the ListBox ItemTemplate is similar to this:
<DataTemplate x:Key="MyTypeTemplate" DataType="{x:Type MyType}">
<Expander DataContext="{Binding}">
<Expander.Header>
<Canvas>
<TextBox Text="{Binding MyProperty}"/>
</Canvas>
</Expander.Header>
<Canvas>
<TextBox Text={Binding MyDetailedProperty}"/>
</Canvas>
</Expander>
</DataTemplate>
Whenever these properties change, either 'MyProperty' or 'MyDetailedProperty' changes, the expander control collapsed. I believe that is has something to do with the Expander item getting recreated when the data changes.
As an additional data item, the list being bound to the listbox implements IBindingList as it comes from a library created for .NET 2.0. I cannot recreate the list using ObservableCollection due to time constraints
I ended up wrapping my model objects in a view object that adds an IsExpandable property that I could bind to the Expanded IsExpanded property and then exposed the data.
This is not a general purpose solution but it solves my immediate problem. The possible issues that I see that I haven't explored are whether the PropertyChanged and ListChanged event attaches cause memory leak issues with my UI objects, but in my situation each object should only be created once.
Also, events beyond Add and Remove in the collection change are not supported, but in my case I'm not firing anything else so it is safe for me to ignore them.
public class ExpandableItem<T> : INotifyPropertyChanged
where T: INotifyPropertyChanged
{
private bool m_isExpanded;
private readonly T m_data;
public ExpandableItem(T data)
{
m_data = data;
m_data.PropertyChanged +=
delegate
{
PropertyChanged(this, new PropertyChangedEventArgs("Data"));
};
}
public bool IsExpanded
{
get { return m_isExpanded; }
set
{
if (value != m_isExpanded)
{
m_isExpanded = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsExpanded"));
}
}
}
public T Data
{
get
{
return m_data;
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
public class ExpandableList<TObject,TList> :
ObservableCollection<ExpandableItem<TObject>>
where TList : ObservableCollection<TObject>
where TObject : INotifyPropertyChanged
{
readonly TList m_list;
public ExpandableList(TList list)
: base(list.Select(obj=>new ExpandableItem<TObject>(obj)))
{
list.CollectionChanged += OnListChanged;
m_list = list;
}
public TList Data { get { return m_list; } }
private void OnListChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
Insert(e.NewStartingIndex, e.NewItems[0]);
}
if (e.Action == NotifyCollectionChangedAction.Remove)
{
RemoveAt(e.OldStartingIndex);
}
}
}