WPF databinding to a linked list - wpf

I want to databind a ListBox to a linked list
public class MyClass
{
public string MyText{ get; set; }
public MyClass PreviousItem{ get; set; }
}
I want to use an instance of MyClass as the datasource of a ListBox, to basically show a list of the MyClass instance and all it's PreviousItems.
Of course binding to an instance of MyClass will result in only the topmost parent being shown. What would be the best approach for this?

Why do you need a custom implementation of a LinkedList in the first place? There is a .NET implementation already: System.Collections.Generic.LinkedList
Other than that, you have basically three options:
Recommended: If it fits your
business logic, impement at least
IEnumerable in MyClass (like the .NET lists)
Create a ViewModel which traverses
your items of MyClass and
puts them into an
ObservableCollection
Create an IValueConverter object to
convert your linked list to a
collectionType like
ObservableCollection

You can use BCL LinkedList<T> class. Or alternatively if you like your class very much you can implement IEnumerable like this
public class MyClass : IEnumerable<MyClass>
{
public string MyText { get; set; }
public MyClass PreviousItem { get; set; }
public IEnumerator<MyClass> GetEnumerator()
{
var item = this;
do
{
yield return item;
item = item.PreviousItem;
} while (item != null);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

Actually implementing IEnumerable would suffice:
public IEnumerable<MyClass> TraverseItemsFromCurrent
{
get
{
var current = this;
yield return current;
while (current.PreviousItem != null)
{
current = current.PreviousItem;
yield return current;
}
}
}
And yes, using LinkedList<T> might be easier and more adequate to your purpose.

The easiest thing (in my opinion) is to implement IEnumerable
public class MyClass : IEnumerable<MyClass>
{
public string MyText { get; set; }
public MyClass PreviousItem { get; set; }
IEnumerator<MyClass> IEnumerable<MyClass>.GetEnumerator()
{
for (var item = this; item.PreviousItem != null; item = item.PreviousItem)
yield return item;
}
public IEnumerator GetEnumerator()
{
return ((IEnumerable<MyClass>)this).GetEnumerator();
}
}
Then you code would look like this
public Window1()
{
MyClass item1 = new MyClass() {MyText = "No1"};
MyClass item2 = new MyClass() {MyText = "No2"};
MyClass item3 = new MyClass() {MyText = "No3"};
MyClass item4 = new MyClass() {MyText = "No4"};
item4.PreviousItem = item3;
item3.PreviousItem = item2;
item2.PreviousItem = item1;
DataContext = item4; // your first item
}

Related

Execute RaiseCanExecuteChanged from 'subclass'

So I have the following setup:
PLANNING:
public class Planning : ViewModelBase
{
public Planning()
{
AddNewActivityCommand = new RelayCommand(AddActivity, CanAddActivity);
}
public ObservableCollection<PlanningItem> PlanningItems { get; set; }
public PlanningItem SelectedPlan { get; set; }
#region AddNewActivity
public RelayCommand AddNewActivityCommand { get; private set; }
private bool CanAddActivity()
{
if (!PlanningItems.Any())
{
return true;
}
if (string.IsNullOrEmpty(PlanningItems[PlanningItems.Count - 1].Activities) != true ||
PlanningItems[PlanningItems.Count - 1].DhpRepresentativeSelected != null)
{
return true;
}
return false;
}
private void AddActivity()
{
PlanningItems.Add(new PlanningItem());
AddNewActivityCommand.RaiseCanExecuteChanged();
}
#endregion
}
PLANNING ITEM:
public class PlanningItem : ViewModelBase
{
private string _activity;
public ObservableCollection<OutreachUser> DhpRepresentativeSource
{
get
{
var userSource = new ObservableCollection<OutreachUser>();
using (var context = new Outreach_Entities())
{
var query = from a in context.UserInfoes
join b in context.PersonalInfoes on a.UserIdentity equals b.PersonIdentity
join c in context.PersonalTitles on b.TitleLink equals c.TitleIdentity into cGroup
from c in cGroup.DefaultIfEmpty()
select new OutreachUser
{
PersonLink = a.UserIdentity,
Username = a.Username,
FirstName = b.FirstName,
MiddleInitial = b.MiddleInitial,
LastName = b.LastName
};
foreach (var result in query)
{
userSource.Add(result);
}
return userSource;
}
}
}
public OutreachUser DhpRepresentativeSelected { get; set; }
public DateTime PlanningDate { get; set; }
public TimeSpan PlanningStart { get; set; }
public TimeSpan PlanningEnd { get; set; }
public int PlanningTotalHours { get; set; }
public string Activities
{
get
{
return _activity;
}
set
{
_activity = value;
RaisePropertyChanged(nameof(Activities), "", _activity, true);
}
}
}
I have a ListBox bound to the PlanningItems Observable Collection.
I want to be able to add a new item to the list if the following criteria are met:
The Planning Items Collection is empty.
The last item in the Planning Items Collection has a DhpRepresentativeSelected that is not null.
The last item in the Planning Items Collection has some text in the Activities string.
The first item is easy enough because I call AddNewActivityCommand.RaiseCanExecuteChanged(); after I add a new item from an empty list.
Now I need to call the AddNewActivityCommand.RaiseCanExecuteChanged(); from within the PlanningItem ViewModel, but it does not have access rights to the command.
Clueless pointed me to the answer.
What I did was inside of my Planning ViewModel I created an internal Method that called the AddNewActivityCommand.RaiseCanExecuteChanged() method. I think called that method from within the PlanningItems ViewModel.

Binding hierarchical data with templates using a generic Tree data structure

How to bind a Tree data structure using hierarchical data template to a tree list control. The difference is that instead of creating different types for each level in hierarchy I intend to use only one type “Node” differentiated by the “Type” enumeration indicating its level in the hierarchy. Is this a feasible approach. How to display the data using TreeListControl.
public class TreeNode<T> where T : new()
{
public TreeNode<T> Parent { get; set; }
public IList<TreeNode<T>> Children { get; set; }
protected TreeNodeType Type { get; set; }
public T Current { get; set; }
public TreeNode()
{
}
public TreeNode(TreeNodeType type)
{
this.Type = type;
this.Current = new T();
this.Children = new List<TreeNode<T>>();
}
public void AddChildren(TreeNode<T> child)
{
child.Parent = this;
this.Children.Add(child);
}
public override string ToString()
{
return string.Format("Type :{0} Name :{1}", this.Type, this.Current);
}
}
/// <summary>
/// Tree node type
/// </summary>
public enum TreeNodeType
{
Manager = 0,
Employee,
}
public class EmployeeNode
{
public string Name { get; set; }
public override string ToString()
{
return this.Name;
}
}
You're going to need to use a DataTemplateSelector. MSDN describes how to use one. Even though the example is for a ListBox, you can do this with a TreeView.
I created a data selector that always returns the same template. This will return the same template for each node of the self referencing data.
public class HierarchialDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
if (element != null && item != null)
{
return element.FindResource("HierarchialDataTemplate") as DataTemplate;
}
return null;
}
}

WPF DataGrid - Can I decorate my POCOs with attributes to have custom column names?

I have a DataGrid in WPF and fill it with data like this:
public enum Sharing
{
Equal,
SurfaceBased,
}
public class Data
{
public bool Active { get; set; }
public string Name { get; set; }
public int Floor { get; set; }
public Sharing Sharing { get; set; }
}
public ObservableCollection<Data> _col = new ObservableCollection<Data>()
{
new Data(){Active = true, Name = "KRL", Floor = 0 },
new Data(){Name = "DAT", Floor = 1},
new Data(){Name = "TRE", Floor = 1},
new Data(){Name = "DUO", Floor = 2},
};
public MainWindow()
{
InitializeComponent();
grid.AutoGenerateColumns = true;
grid.DataContext = _col;
grid.ItemsSource = _col;
}
I was wondering if I could use some attributes on the enumerations and the POCO class so that the DataGrid displays them (instead of the variable names) on the headers and ComboCoxes.
Something like this:
public enum Sharing
{
[Name("This is a test")]
Equal,
[Name("This is a test 2")]
SurfaceBased,
}
Is this possible?
OK. Here is the way to do it for the Headers:
You add attributes, like Description attributes to your Properties.
public class MyPOCO
{
[Description("The amount you must pay")]
public float Amount { get; set; }
}
Then, in a class derived from DataGrid you do this:
protected override void OnAutoGeneratingColumn(DataGridAutoGeneratingColumnEventArgs e)
{
try
{
base.OnAutoGeneratingColumn(e);
var propDescr = e.PropertyDescriptor as System.ComponentModel.PropertyDescriptor;
e.Column.Header = propDescr.Description;
}
catch (Exception ex)
{
Utils.ReportException(ex);
}
}
For adding custom names to the members of the enumerations, you need to make a custom column. You can see a simple example here : https://stackoverflow.com/a/17510660/964053.

MVVM: how to create a ViewModel from Model object

I want to get values of my model and create a viewmode
In my Model I have
public class TestElement
{
public TestElement CurrentNode { get; set; }
public TestElement Parent { get; set; }
}
I have some method that do this
if (thisNode == null)
{
thisNode = new TestElement { Name = name, Parent = CurrentNode };
currentCollection.Add(thisNode);
}
In my view model I want to create TestElementViewModel Parent and get my model Parent values
public class TestElementViewModel
{
public TestElementViewModel Parent { get; set; }
I want to use it in this method
public IEnumerable<TestElementViewModel> ToTreeViewModel(IEnumerable<TestElement> treemodel)
{
foreach (TestElementitem in treemodel)
yield return new TestElementViewModel
{
Id = item.Id,
Name = item.Name,
Children = ToTreeViewModel(item.Children).ToList(),
Parent = item.Parent
};
}
}
How can I achieve that?
I'm guessing your casting error occurs on the the line
Parent = item.Parent
Well the Parent property in your TestElementViewModel isn't a TestElement type so you can't do that.
Try assigning a new TestElementViewModel instead.
Parent = new TestElementViewModel { Id = item.Parent.Id, Name = item.Parent.Name, ... }
One improvement you might want to consider is using wrappers in your ViewModel class, which will make assigning properties a little easier.
For example,
public class TestElementViewModel : INotifyPropertyChanged
{
public TestElementViewModel(TestElement model)
{
Model = model;
if(Model.Parent != null)
Parent = new TestElementViewModel(Model.Parent);
}
public TestElement Model { get; private set; }
private TestElementViewModel _parent;
public TestElementViewModel Parent
{ get { return _parent; }
set { _parent = value; OnPropertyChanged("Parent"); }
}
public int Id
{
get { return Model.Id; }
set { Model.Id = value; OnPropertyChanged("Id"); }
}
// rest of the properties need wrapping too
}
makes it so that you don't have to manually assign the properties each time you instantiate a new viewmodel.

Implementing INotifyCollectionChanged interface

I need to implement a collection with special capabilities. In addition, I want to bind this collection to a ListView, Therefore I ended up with the next code (I omitted some methods to make it shorter here in the forum):
public class myCollection<T> : INotifyCollectionChanged
{
private Collection<T> collection = new Collection<T>();
public event NotifyCollectionChangedEventHandler CollectionChanged;
public void Add(T item)
{
collection.Insert(collection.Count, item);
OnCollectionChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
protected virtual void OnCollectionChange(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
CollectionChanged(this, e);
}
}
I wanted to test it with a simple data class:
public class Person
{
public string GivenName { get; set; }
public string SurName { get; set; }
}
So I created an instance of myCollection class as follows:
myCollection<Person> _PersonCollection = new myCollection<Person>();
public myCollection<Person> PersonCollection
{ get { return _PersonCollection; } }
The problem is that the ListView does not update when the collection updates although I implemented the INotifyCollectionChanged interface.
I know that my binding is fine (in XAML) because when I use the ObservableCollecion class instead of myCollecion class like this:
ObservableCollection<Person> _PersonCollection = new ObservableCollection<Person>();
public ObservableCollection<Person> PersonCollection
{ get { return _PersonCollection; } }
the ListView updates
What is the problem?
In order for your collection to be consumed, you should implement IEnumerable and IEnumerator too. Although, you're probably better off subclassing ObservableCollection<T>

Resources