Has anybody found a nice pattern for binding ImmutableCollections ( from the MS BCL ) to a WPF DataGrid? To represent mutablility of an immutable structure I would wrap it with ISubject to track changes and provide new versions to the datagrid.
ISubject<ImmutableList<T>> <--(binding)--> DataGrid
The DataGrid obviously needs a mutable collection such as ObservableCollection directly under it so perhaps the problem can be reduced to
ISubject<ImmutableList<T>> <-----> ObservableCollection<T>
Any suggestions?
ObservableCollection
Here is a partial solution.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Subjects;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Immutable;
using System.Collections.Specialized;
using ReactiveUI;
namespace ReactiveUI.Ext
{
public class ImmutableListToReactive<T> : ReactiveObject, ICollection<T>, INotifyCollectionChanged, IDisposable, IList<T>
where T : class, Immutable
{
private ISubject<ImmutableList<T>> _Source;
ImmutableList<T> _Current;
public ImmutableList<T> Current
{
get { return _Current; }
set { this.RaiseAndSetIfChanged(ref _Current, value); }
}
public void Dispose()
{
_Subscription.Dispose();
}
public ImmutableListToReactive( ISubject<ImmutableList<T>> source )
{
_Source = source;
_Subscription = source.Subscribe(newVersion =>
{
if ( !rebound )
{
_Current = newVersion;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
});
}
private void OnNext( ImmutableList<T> list )
{
rebound = true;
_Current = list;
try
{
_Source.OnNext(list);
}
finally
{
rebound = false;
}
}
public void Add( T item )
{
OnNext(_Current.Add(item));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T>(){item}, Current.Count - 1));
}
public void Clear()
{
OnNext(_Current.Clear());
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public bool Contains( T item )
{
return _Current.Contains(item);
}
public void CopyTo( T[] array, int arrayIndex )
{
_Current.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _Current.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove( T item )
{
var idx = _Current.IndexOf(item);
var next = _Current.Remove(item);
if ( next == _Current )
{
return false;
}
else
{
OnNext(next);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, idx));
return true;
}
}
public IEnumerator<T> GetEnumerator()
{
return _Current.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _Current.GetEnumerator();
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
private IDisposable _Subscription;
bool rebound = false;
protected virtual void OnCollectionChanged( NotifyCollectionChangedEventArgs e )
{
if ( !rebound )
{
rebound = true;
if ( CollectionChanged != null )
{
CollectionChanged(this, e);
}
rebound = false;
}
}
public int IndexOf( T item )
{
return _Current.IndexOf(item);
}
public void Insert( int index, T item )
{
OnNext(_Current.Insert(index, item));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
public void RemoveAt( int index )
{
var itemToBeRemoved = _Current[index];
OnNext(_Current.RemoveAt(index));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, itemToBeRemoved, index));
}
public T this[int index]
{
get
{
return _Current[index];
}
set
{
OnNext(_Current.SetItem(index, value));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, value, index));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value, index));
}
}
}
}
and a test case
using FluentAssertions;
using ReactiveUI.Subjects;
using System;
using System.Collections.Immutable;
using System.Collections.Specialized;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Xunit;
namespace ReactiveUI.Ext.Spec
{
public class Data : Immutable
{
public int A { get; private set; }
public int B { get; private set; }
public bool Equals(Data other){
return A == other.A && B == other.B;
}
public bool Equals(object o){
Data other = o as Data;
if ( other == null )
{
return false;
}
return this.Equals(other);
}
public static bool operator ==( Data a, Data b )
{
return a.Equals(b);
}
public static bool operator !=( Data a, Data b )
{
return !a.Equals(b);
}
public Data( int a, int b )
{
A = a;
B = b;
}
}
public class DataMutable : ReactiveObject, IDisposable
{
int _A;
public int A
{
get { return _A; }
set { this.RaiseAndSetIfChanged(ref _A, value); }
}
int _B;
public int B
{
get { return _B; }
set { this.RaiseAndSetIfChanged(ref _B, value); }
}
IDisposable _Subscriptions;
public DataMutable( ILens<Data> dataLens )
{
_Subscriptions = new CompositeDisposable();
var d0 = dataLens.Focus(p => p.A).TwoWayBindTo(this, p => p.A);
var d1 = dataLens.Focus(p => p.B).TwoWayBindTo(this, p => p.B);
_Subscriptions = new CompositeDisposable(d0, d1);
}
public void Dispose()
{
_Subscriptions.Dispose();
}
}
public class ImmutableListToReactiveSpec : ReactiveObject
{
ImmutableList<Data> _Fixture;
public ImmutableList<Data> Fixture
{
get { return _Fixture; }
set { this.RaiseAndSetIfChanged(ref _Fixture, value); }
}
[Fact]
public void ReactiveListSux()
{
var a = new ReactiveList<int>();
var b = a.CreateDerivedCollection(x => x);
a.Add(10);
b.Count.Should().Be(1);
}
[Fact]
public void ShouldWork()
{
Fixture = ImmutableList<Data>.Empty;
// Convert an INPC property to a subject
ISubject<ImmutableList<Data>> s = this.PropertySubject(p => p.Fixture);
var MutableList = new ImmutableListToReactive<Data>(s);
var DerivedList = MutableList.CreateDerivedCollection(x => x);
Fixture = Fixture.Add(new Data(10, 20));
DerivedList.ShouldAllBeEquivalentTo(Fixture);
Fixture = Fixture.Add(new Data(11, 21));
DerivedList.ShouldAllBeEquivalentTo(Fixture);
Fixture = Fixture.Add(new Data(12, 22));
MutableList.Count.Should().Be(3);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
MutableList.Add(new Data(33, 88));
MutableList.Count.Should().Be(4);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
MutableList[1] = new Data(99, 21);
MutableList.Count.Should().Be(4);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
var itemAtOne = MutableList[1];
MutableList.RemoveAt(1);
MutableList.Should().NotContain(itemAtOne);
MutableList.Count.Should().Be(3);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
var i = new Data(78, 32);
MutableList.Insert(0, i);
DerivedList[0].Should().Be(i);
MutableList.Count.Should().Be(4);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
var j = new Data(18, 22);
MutableList.Insert(3, j);
DerivedList[3].Should().Be(j);
MutableList.Count.Should().Be(5);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
var k = new Data(18, 22);
MutableList.Add(k);
DerivedList[DerivedList.Count-1].Should().Be(k);
MutableList.Count.Should().Be(6);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
MutableList.Remove(i);
DerivedList[DerivedList.Count-1].Should().Be(k);
MutableList.Count.Should().Be(5);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
}
}
}
Related
I created a reference to load big data into a datagrid with a dapper extension. I have a Behavior that detects when the scroll is then down load the following data using this RelayCommand:
XAML in Datagrid Properties :
Behavior:ScrollViewerMonitor.AtEndCommand="{Binding LoadCommand}"
My Behavior (Detect when scroll is down) :
public class ScrollViewerMonitor
{
public static DependencyProperty AtEndCommandProperty
= DependencyProperty.RegisterAttached(
"AtEndCommand", typeof(ICommand),
typeof(ScrollViewerMonitor),
new PropertyMetadata(OnAtEndCommandChanged));
public static ICommand GetAtEndCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(AtEndCommandProperty);
}
public static void SetAtEndCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(AtEndCommandProperty, value);
}
public static void OnAtEndCommandChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
if (element != null)
{
element.Loaded -= element_Loaded;
element.Loaded += element_Loaded;
}
}
static void element_Loaded(object sender, RoutedEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
element.Loaded -= element_Loaded;
ScrollViewer scrollViewer = FindChildOfType<ScrollViewer>(element);
if (scrollViewer == null)
{
// throw new InvalidOperationException("ScrollViewer not found.");
return;
}
var dpd = DependencyPropertyDescriptor.FromProperty(ScrollViewer.VerticalOffsetProperty, typeof(ScrollViewer));
dpd.AddValueChanged(scrollViewer, delegate (object o, EventArgs args)
{
bool atBottom = scrollViewer.VerticalOffset
>= scrollViewer.ScrollableHeight;
if (atBottom)
{
var atEnd = GetAtEndCommand(element);
if (atEnd != null)
{
atEnd.Execute(null);
}
}
});
}
static T FindChildOfType<T>(DependencyObject root) where T : class
{
var queue = new Queue<DependencyObject>();
queue.Enqueue(root);
while (queue.Count > 0)
{
DependencyObject current = queue.Dequeue();
for (int i = VisualTreeHelper.GetChildrenCount(current) - 1; 0 <= i; i--)
{
var child = VisualTreeHelper.GetChild(current, i);
var typedChild = child as T;
if (typedChild != null)
{
return typedChild;
}
queue.Enqueue(child);
}
}
return null;
}
}
My ViewModel with LoadCommand :
//Init mmy ObservableCollection for DataGrid
var myObservableCollection = new ObservableCollection<Mouvement_Brouillard>();
//Init my Object
//Parameters(NumberPerPage,Conditions,OrderBy,Connection)
var myReference = new Paged2<Mouvement_Brouillard>(150, "", "Swmo_Id", new ConnectionProvider());
//Load First Datas
myReference.AddDatas(myObservableCollection);
//Call LoadCommand when Scroll is down
LoadCommand = new RelayCommand<object>(myReference.LoadCommand(myObservableCollection));
And my reference Paged2 (AddData in ObservableCollection and execute LoadCommand:
public class Paged2<T>
{
private T Value { get; set; }
public int NumberPage { get; set; }
public int RowsPerPage { get; set; }
public string Conditions { get; set; }
public string OrderBy { get; set; }
public ConnectionProvider Cnn { get; set; }
public static bool Busy;
public Paged2(int _RowsPerPage, string _Conditions, string _OrdeBy, ConnectionProvider _Cnn)
{
this.RowsPerPage = _RowsPerPage;
this.Conditions = _Conditions;
this.OrderBy = _OrdeBy;
this.NumberPage = 1;
this.Cnn = _Cnn;
}
public async void AddDatas(ObservableCollection<T> myList)
{
IEnumerable<T> myNewBlocList;
//DAL
using (var myCnn = this.Cnn.GetOpenConnection())
{
myNewBlocList = await myCnn.GetListPagedAsync<T>(this.NumberPage, this.RowsPerPage, this.Conditions, this.OrderBy);
}
NumberPage++;
foreach (var Item in myNewBlocList)
myList.Add(Item);
}
public Action<object> LoadCommand(Ref<ObservableCollection<T>> myList)
{
return new Action<object>(
obj =>
{
if (Busy)
return;
Busy = true;
System.Threading.ThreadPool.QueueUserWorkItem(
delegate
{
Application.Current.Dispatcher.BeginInvoke(new Action(
delegate
{
AddDatas(myList);
Busy = false;
}));
});
});
}
public class Ref<T>
{
public Ref() { }
public Ref(T value) { Value = value; }
public T Value { get; set; }
public override string ToString()
{
T value = Value;
return value == null ? "" : value.ToString();
}
public static implicit operator T(Ref<T> r) { return r.Value; }
public static implicit operator Ref<T>(T value) { return new Ref<T>(value); }
}
}
Everything works but since I outsource (place in another file ) method LoadCommand the next part of the code no longer works :
public Action<object> LoadCommand(Ref<ObservableCollection<T>> myList)
{
return new Action<object>(
obj =>
{
if (Busy)
return;
Busy = true;
System.Threading.ThreadPool.QueueUserWorkItem(
delegate
{
Application.Current.Dispatcher.BeginInvoke(new Action(
delegate
{
AddDatas(myList);
Busy = false;
}));
});
});
}
I'm a java developer and new to C#, I'm stuck with InvalidCastException on the following code below.
I have implemented a Queue, based on a custom Doubly Linked List implementation. Both implementations are generic, and thus, on the test code, I want to use a generic print method.
The test code below is just working fine;
using System;
using System.Collections.Generic;
namespace Queue01
{
public class TestQueue
{
public static void Main(string[] args)
{
Queue<int> queue = new Queue<int>();
queue.Enqueue(4);
queue.Enqueue(5);
queue.Enqueue(6);
Console.WriteLine("*****");
PrintQueue(queue);
Console.WriteLine("*****");
int first = queue.Dequeue();
Console.WriteLine("Enqueued value : " + first);
Console.WriteLine("*****");
PrintQueue(queue);
}
public static void PrintQueue(object queue)
{
Queue<int> q = (Queue<int>)queue;
foreach (object i in q)
{
Console.WriteLine(i);
}
}
}
}
However, I want to make the PrintQueue static method working for all types, thus I've changed the method code as below;
public static void PrintQueue(object queue)
{
Queue<object> q = (Queue<object>)queue;
foreach (object i in q)
{
Console.WriteLine(i);
}
}
The whole test code which is not working is as below;
using System;
using System.Collections.Generic;
namespace Queue01
{
public class TestQueue
{
public static void Main(string[] args)
{
Queue<int> queue = new Queue<int>();
queue.Enqueue(4);
queue.Enqueue(5);
queue.Enqueue(6);
Console.WriteLine("*****");
PrintQueue(queue);
Console.WriteLine("*****");
int first = queue.Dequeue();
Console.WriteLine("Enqueued value : " + first);
Console.WriteLine("*****");
PrintQueue(queue);
}
public static void PrintQueue(object queue)
{
Queue<object> q = (Queue<object>)queue;
foreach (object i in q)
{
Console.WriteLine(i);
}
}
}
}
And then I'm stuck with the InvalidCastException. Where is the problem and how can I fix this exception and what is the best practice to make this code generic?
On java, Object is the base, root class of every class instance. Thus, I've passed the stack instance as an object to the method, assuming that it won't be a problem because int, the alias for Int32 also extended from the Object.
Here down below, I am adding the whole rest files that I've used for compiling the test code above;
1) Here is the DoublyLinkedListNode class that I used in Doubly Linked List implemetation;
using System;
using System.Collections.Generic;
namespace Queue01
{
public class DoublyLinkedListNode<T>
{
// Fields
public T Value { get; set; }
public DoublyLinkedListNode<T> Previous { get; set; }
public DoublyLinkedListNode<T> Next { get; set; }
public DoublyLinkedListNode(T value)
{
Value = value;
}
}
}
2) Here is my DoublyLinkedList class which implements a doubly linked list for the queue implementation I am going to use;
using System;
using System.Collections.Generic;
namespace Queue01
{
public class DoublyLinkedList<T> : ICollection<T>
{
#region Fields
public DoublyLinkedListNode<T> Head { get; private set; }
public DoublyLinkedListNode<T> Tail { get; private set; }
#endregion
#region Constructor
#endregion
#region Add
public void AddFirst(T value)
{
AddFirst(new DoublyLinkedListNode<T>(value));
}
public void AddFirst(DoublyLinkedListNode<T> node)
{
DoublyLinkedListNode<T> temp = Head;
Head = node;
Head.Next = temp;
//if(Count == 0)
if (Empty)
{
Tail = Head;
}
else
{
temp.Previous = Head;
}
Count++;
}
public void AddLast(T value)
{
AddLast(new DoublyLinkedListNode<T>(value));
}
public void AddLast(DoublyLinkedListNode<T> node)
{
//if (Count == 0)
if (Empty)
{
Head = node;
}
else
{
Tail.Next = node;
node.Previous = Tail;
}
Tail = node;
Count++;
}
#endregion
#region Remove
public void RemoveFirst()
{
//if (Count != 0)
if (!Empty)
{
Head = Head.Next;
Count--;
if (Count == 0)
{
Tail = null;
}
else
{
Head.Previous = null;
}
}
}
public void RemoveLast()
{
//if (Count != 0)
if (!Empty)
{
if (Count == 1)
{
Head = null;
Tail = null;
}
else
{
Tail.Previous.Next = null;
Tail = Tail.Previous;
}
Count--;
}
}
#endregion
#region ICollection
public int Count
{
get;
private set;
}
public void Add(T item)
{
AddFirst(item);
}
public bool Contains(T item)
{
DoublyLinkedListNode<T> current = Head;
while (current != null)
{
if (current.Value.Equals(item))
{
return true;
}
current = current.Next;
}
return false;
}
public void CopyTo(T[] array, int arrayIndex)
{
DoublyLinkedListNode<T> current = Head;
while (current != null)
{
array[arrayIndex++] = current.Value;
current = current.Next;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
public bool Remove(T item)
{
DoublyLinkedListNode<T> previous = null;
DoublyLinkedListNode<T> current = Head;
while (current != null)
{
if (current.Value.Equals(item))
{
if (previous != null)
{
previous.Next = current.Next;
if (current.Next == null)
{
Tail = previous;
}
else
{
current.Next.Previous = previous;
}
Count--;
}
else
{
RemoveFirst();
}
return true;
}
previous = current;
current = current.Next;
}
return false;
}
public void Clear()
{
Head = null;
Tail = null;
Count = 0;
}
//System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()
public IEnumerator<T> GetEnumerator()
{
DoublyLinkedListNode<T> current = Head;
while (current != null)
{
yield return current.Value;
current = current.Next;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
//return ((System.Collections.Generic.IEnumerable<T>)this).GetEnumerator();
return this.GetEnumerator();
}
#endregion
#region helper
public void PrintEnumerable()
{
IEnumerator<T> e = this.GetEnumerator();
while (e.MoveNext())
{
T val = e.Current;
Console.WriteLine("Value: " + val);
}
}
public void PrintReverse()
{
for (DoublyLinkedListNode<T> node = Tail; node != null; node = node.Previous)
{
Console.WriteLine(node.Value);
}
}
public bool isEmpty()
{
if (Count == 0)
{
return true;
}
return false;
}
public bool Empty { get { return isEmpty(); } }
public DoublyLinkedListNode<T> First { get { return this.Head; } }
public DoublyLinkedListNode<T> Last { get { return this.Tail; } }
#endregion
}
}
3) And this is my Queue implementation based on doubly linked list implemetation that I've used above;
using System;
using System.Collections.Generic;
namespace Queue01
{
public class Queue<T> : System.Collections.Generic.IEnumerable<T>
{
DoublyLinkedList<T> _items = new DoublyLinkedList<T>();
LinkedList<T> hede = new LinkedList<T>();
public void Enqueue(T item)
{
_items.AddLast(item);
}
public T Dequeue()
{
if(_items.Count == 0)
{
throw new InvalidOperationException("The queue is empty.");
}
T value = _items.First.Value;
_items.RemoveFirst();
return value;
}
public IEnumerator<T> GetEnumerator()
{
return this._items.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}
Fundamentally, you shouldn't try to make PrintQueue work for every object - you should try to make it work for any Queue<T>... and the simplest way of doing that is to make it generic:
public static void PrintQueue<T>(Queue<T> queue)
{
foreach (T item in queue)
{
Console.WriteLine(item);
}
}
Or you could be more general and accept an IEnumerable<T>:
public static void PrintSequence<T>(IEnumerable<T> queue)
{
foreach (T item in queue)
{
Console.WriteLine(item);
}
}
Your original code is failing because a Queue<int> isn't a Queue<object>... in fact, because Queue<T> isn't covariant in T, you can't convert Queue<string> to Queue<object>... but IEnumerable<T> is covariant, so:
Queue<string> stringQueue = new Queue<string>();
...
PrintSequence<object>(stringQueue);
... would be okay.
What about change PrintQueue method. Just like this:
public static void PrintQueue<T>(object queue)
{
var q = (Queue<T>)queue;
foreach (var i in q)
Console.WriteLine(i);
}
and use it like this:
PrintQueue<int>(queue);
change your code like this:
public static void PrintQueue(dynamic queue)
{
foreach (var i in queue)
{
Console.WriteLine(i);
}
}
I have problem with implementing custom comparer in Composite C#. I would like to pass custom comparer my Composite object.
Here is IComponent interface:
namespace composite
{
public interface IComponent<T>
{
void Add(IComponent<T> component);
IComponent<T> Find(T item);
IComponent<T> Remove(T item);
string Display(int depth);
string Name { get; set; }
}
}
For Component a Composite object I´m using the same interface.
Composite (my Collection of Components)
namespace composite
{
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
public class Composite<T> : IComponent<T>
{
private ICollection<IComponent<T>> components;
private IComponent<T> holder;
public Composite(string name)
{
this.Name = name;
this.holder = null;
this.components = new List<IComponent<T>>();
}
public string Name { get; set; }
public void Add(IComponent<T> component)
{
this.components.Add(component);
}
public IComponent<T> Find(T item)
{
this.holder = this;
if (item.Equals(this.Name))
{
return this;
}
IComponent<T> found = null;
//this.components.Select(s => s.Name == item)
foreach (IComponent<T> component in this.components)
{
found = component.Find(item);
if (found != null)
{
break;
}
}
return found;
}
public IComponent<T> Remove(T item)
{
this.holder = this;
IComponent<T> p = holder.Find(item);
if (this.holder != null)
{
(this.holder as Composite<T>).components.Remove(p);
return this.holder;
}
else
{
return this;
}
}
//public IEnumerable<IComponent<T>> Dsiplay(int depth)
public string Display(int depth)
{
StringBuilder s = new StringBuilder();
s.Append("set " + this.Name + " length :" + this.components.Count + "\n");
foreach (IComponent<T> component in components)
{
s.Append(component.Display(depth + 2));
}
return s.ToString();
}
}
}
Component:
namespace composite
{
using System;
using System.Collections.Generic;
public class Component<T> : IComponent<T>
{
public Component(string name)
{
this.Name = name;
}
public string Display(int depth)
{
return new string('-', depth) + this.Name + "\n";
}
public string Name { get; set; }
public IComparer<T> MyComparer { get; private set; }
//public IComparer<T> myComparer { set; }
public void Add(IComponent<T> item)
{
throw new NotImplementedException();
}
public IComponent<T> Find(T item)
{
//Here I want to use comparer object, instead of Equals
//Something like this:
//return MyComparer.Compare( ... ) == 0 ? this : null;
return item.Equals(this.Name) ? this : null;
}
public IComponent<T> Remove(T item)
{
throw new NotImplementedException();
}
}
}
And finally my main function:
namespace composite
{
class Program
{
static void Main(string[] args)
{
//Creating custom comparer and pass to the constructor composite?
IComparer<string> myComparer = new ...
IComponent<string> comp1 = new Component<string>("Komponenta 1");
IComponent<string> comp2 = new Component<string>("Komponenta 2");
IComponent<string> comp3 = new Component<string>("Komponenta 3");
IComponent<string> comp4 = new Component<string>("Komponenta 4");
IComponent<string> comp5 = new Component<string>("Komponenta 5");
IComponent<string> composite = new Composite<string>("Composite 1");
IComponent<string> composite2 = new Composite<string>("Composite 2");
composite.Add(comp1);
composite.Add(comp2);
composite.Add(comp3);
composite.Add(comp4);
composite.Add(comp5);
composite2.Add(comp1);
composite2.Add(comp2);
composite2.Add(comp3);
composite2.Add(comp4);
composite2.Add(comp5);
composite.Add(composite2);
Console.Write(composite.Display(0));
}
}
}
Can you help me with implement custom comparer and pass to Find method?
Is it good way or not?
Very thanks
The main issue here is that:
the components have a Name property
the components don't use the generic type parameter T
the Find method gets a generic item of type T to find and compares
it with the component name - this will only work for IComponent<string>
So the most simple (and my preferred) solution would be to get rid of the comparer.
Instead, I would a define find method with a predicate (as mentioned in the comments).
But you asked for a comparer!
So there are a few adjustments to make:
define a property T Data {get; set;} in IComponent<T> and
provide an implementation in Component<T> and Composite<T>
switch from IComparer<T> to IEqualityComparer<IComponent<T>> if you want to
because you want to search equal components and not compare elements
change the remove method accordingly
In code (shortened a little):
public interface IComponent<T>
{
void Add(IComponent<T> component);
IComponent<T> Find(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer);
IComponent<T> Find(Predicate<IComponent<T>> condition)
bool Remove(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer);
string Display(int depth);
string Name { get; set; }
T Data { get; set; }
}
public class Component<T> : IComponent<T>
{
public T Data { get; set; }
public string Name { get; set; }
public Component(string name)
=> Name = name;
public string Display(int depth) =>
new string('-', depth) + Name + "\n";
public IComponent<T> Find(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer)
=> comparer.Equals(item, this) ? this : null;
public IComponent<T> Find(Predicate<IComponent<T>> condition)
=> condition(this) ? this : null;
public void Add(IComponent<T> item)
=> throw new InvalidOperationException();
public bool Remove(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer)
=> throw new InvalidOperationException();
}
public class Composite<T> : IComponent<T>
{
private IList<IComponent<T>> components = new List<IComponent<T>>();
public T Data { get; set; }
public string Name { get; set; }
public Composite(string name)
=> Name = name;
public void Add(IComponent<T> component)
=> components.Add(component);
public IComponent<T> Find(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer)
{
if (comparer.Equals(item, this))
return this;
else
foreach (var component in components)
{
var childItem = component.Find(item, comparer);
if (childItem != null)
return childItem;
}
return null;
}
public bool Remove(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer)
{
var result = false;
for (var i = components.Count - 1; i >= 0; i--)
if (comparer.Equals(components[i], item))
{
components.RemoveAt(i);
result = true;
}
return result;
}
public IComponent<T> Find(Predicate<IComponent<T>> condition)
{
if (condition(this))
return this;
foreach (var item in components)
{
var result = item.Find(condition);
if (result != null)
return result;
}
return null;
}
public string Display(int depth)
{
var s = new StringBuilder();
s.Append(new string('-', depth) + "set " + Name + " length :" + components.Count + "\n");
foreach (var component in components)
s.Append(component.Display(depth + 2));
return s.ToString();
}
}
Two comparer implementations would be:
public class DefaultComparer<T> : IEqualityComparer<IComponent<T>>
{
public bool Equals(IComponent<T> x, IComponent<T> y)
=> EqualityComparer<T>.Default.Equals(x.Data, y.Data);
public int GetHashCode(IComponent<T> obj)
=> EqualityComparer<T>.Default.GetHashCode(obj.Data);
}
public class NameComparer<T> : IEqualityComparer<IComponent<T>>
{
public bool Equals(IComponent<T> x, IComponent<T> y)
=> string.Equals(x.Name, y.Name);
public int GetHashCode(IComponent<T> obj)
=> (obj.Name ?? string.Empty).GetHashCode();
}
How to use that?
If you want to search for a component with a given name, you can now use:
var element1 = composite.Find(new Component<string>("Komponenta 5"), new NameComparer<string>());
Console.WriteLine(element1.Name);
Or event simpler:
var element2 = composite.Find(t => string.Equals(t.Name, "Komponenta 5"));
Console.WriteLine(element2.Name);
MultiSet.cs and Form.cs
The best overloaded method match for 'string.Join(string, string[])' has some invalid arguments
Argument '2': cannot convert from 'lab3.MultiSet' to 'string[]'
namespace lab3
{
internal class MultiSet : IEnumerable<int>
{
public MultiSet()
{
}
public MultiSet(IEnumerable<int> elements)
{
if (elements == null)
throw new ArgumentNullException("elements");
foreach (int element in elements)
Add(element);
}
public MultiSet(params int[] elements)
: this((IEnumerable<int>)elements)
{
}
public IEnumerator<int> GetEnumerator()
{
return new Enumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public bool Add(int element)
{
if (_cardinal == _elements.Length)
{
int[] newElementsArray = new int[_elements.Length * 2];
_elements.CopyTo(newElementsArray, 0);
_elements = newElementsArray;
}
_elements[_cardinal] = element;
_cardinal++;
_lastModificationTime = DateTime.Now;
return true;
}
public int Count
{
get
{
return _cardinal;
}
}
public override string ToString()
{
return string.Format("{{ {0} }}", string.Join(", ", this));
}
private int _cardinal = 0;
private int[] _elements = new int[8];
private DateTime _lastModificationTime = DateTime.Now;
private sealed class Enumerator: IEnumerator<int>
{
public Enumerator(MultiSet set)
{
if (set == null)
throw new ArgumentNullException("set");
_set = set;
_setLastModificationTime = _set._lastModificationTime;
Reset();
}
public int Current
{
get
{
if (_index < 0 || _set._cardinal <= _index || _setLastModificationTime != _set._lastModificationTime)
throw new InvalidOperationException();
return _set._elements[_index];
}
}
void IDisposable.Dispose()
{
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public bool MoveNext()
{
if (_setLastModificationTime != _set._lastModificationTime)
throw new InvalidOperationException();
_index++;
return (_index < _set.Count);
}
public void Reset()
{
_index = -1;
}
private int _index;
private readonly MultiSet _set;
private readonly DateTime _setLastModificationTime;
}
}
}
namespace lab3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
_setElementNumericUpDown.Minimum = int.MinValue;
_setElementNumericUpDown.Maximum = int.MaxValue;
_currentSetTextBox.Text = _currentSet.ToString();
}
private void _addSetButton_Click(object sender, EventArgs e)
{
_setsListBox1.Items.Add(_currentSet);
_setsListBox2.Items.Add(_currentSet);
_currentSet = new MultiSet();
_currentSetTextBox.Text = _currentSet.ToString();
}
private void _addElementToSetButton_Click(object sender, EventArgs e)
{
_currentSet.Add((int)_setElementNumericUpDown.Value);
_currentSetTextBox.Text = _currentSet.ToString();
}
private MultiSet _currentSet = new MultiSet();
}
}
Since your class is an IEnumerable< int >, have you tried making a method that returns a string[] of the items in your list?
private string[] ToStringArray()
{
if (this.Count == 0)
return null;
int i = 0;
string[] items = new string[this.Count];
IEnumerator<int> enumerator = this.GetEnumerator();
while(enumerator.MoveNext())
{
items[i] = enumerator.Current.ToString();
i++;
}
// Reset your enumerator index if needed
enumerator.Reset();
return items;
}
Then your ToString() would look like this:
public override string ToString()
{
return string.Format("{{ {0} }}", string.Join(", ", ToStringArray()));
}
On several recommendations, I started to assign a DataSource to my DataGridView instead of using DataGridView.Rows.Add(...). This is convenient since my data source is already a big list which doesn't change (much). However, when I use the DataSource assignment, it becomes impossible to sort the columns.
class MyGridView : DataGridView
{
private List<Person> m_personList;
private class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string first, string last)
{
FirstName = first;
LastName = last;
}
}
public MyGridView()
{
/* ...initialise stuff... */
m_personList.Add(new Person("Kate", "Smith"));
m_personList.Add(new Person("Bill", "Davids"));
m_personList.Add(new Person("Ann", "Roth"));
this.DataSource = m_personList;
}
}
I also tried to replace List<Person> by BindingList<Person> and by BindingSource, but none of that seems to matter. I also tried adding a custom sorter:
this.SortCompare += MyGridView_SortCompare;
private void MyGridView_SortCompare(object sender, EventArgs e)
{
/* ...Compare method...*/
}
but the thing doesn't even get called. Is there some other way to enable sorting with a DataSource?
Note: Note that my DataSource is not (necessarily) an SQL one, but just any List.
You need to coerce your datasource into a list that supports sorting (IBindingList and IBindingListView), and then it will work out of the box. There are lots of examples on the web, this is the one I've used in the past:
// usage:
// var sortableList = new SortableList(m_personList);
// dgv.DataSource = m_sortableList;
/// <summary>
/// Suitable for binding to DataGridView when column sorting is required
/// </summary>
/// <typeparam name="T"></typeparam>
public class SortableList<T> : BindingList<T>, IBindingListView
{
private PropertyComparerCollection<T> sorts;
public SortableList()
{
}
public SortableList(IEnumerable<T> initialList)
{
foreach (T item in initialList)
{
this.Add(item);
}
}
public SortableList<T> ApplyFilter(Func<T, bool> func)
{
SortableList<T> newList = new SortableList<T>();
foreach (var item in this.Where(func))
{
newList.Add(item);
}
return newList;
}
protected override bool IsSortedCore
{
get { return this.sorts != null; }
}
protected override bool SupportsSortingCore
{
get { return true; }
}
protected override ListSortDirection SortDirectionCore
{
get
{
return this.sorts == null
? ListSortDirection.Ascending
: this.sorts.PrimaryDirection;
}
}
protected override PropertyDescriptor SortPropertyCore
{
get
{
return this.sorts == null ? null : this.sorts.PrimaryProperty;
}
}
public void ApplySort(ListSortDescriptionCollection
sortCollection)
{
bool oldRaise = RaiseListChangedEvents;
RaiseListChangedEvents = false;
try
{
PropertyComparerCollection<T> tmp
= new PropertyComparerCollection<T>(sortCollection);
List<T> items = new List<T>(this);
items.Sort(tmp);
int index = 0;
foreach (T item in items)
{
SetItem(index++, item);
}
this.sorts = tmp;
}
finally
{
RaiseListChangedEvents = oldRaise;
ResetBindings();
}
}
public bool Exists(Predicate<T> func)
{
return new List<T>(this).Exists(func);
}
string IBindingListView.Filter
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
void IBindingListView.RemoveFilter()
{
throw new NotImplementedException();
}
ListSortDescriptionCollection IBindingListView.SortDescriptions
{
get { return (this.sorts == null ? null : this.sorts.Sorts); }
}
bool IBindingListView.SupportsAdvancedSorting
{
get { return true; }
}
bool IBindingListView.SupportsFiltering
{
get { return false; }
}
protected override void RemoveSortCore()
{
this.sorts = null;
}
protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
{
ListSortDescription[] arr = { new ListSortDescription(prop, direction) };
ApplySort(new ListSortDescriptionCollection(arr));
}
}
public class PropertyComparerCollection<T> : IComparer<T>
{
private readonly PropertyComparer<T>[] comparers;
private readonly ListSortDescriptionCollection sorts;
public PropertyComparerCollection(ListSortDescriptionCollection
sorts)
{
if (sorts == null)
{
throw new ArgumentNullException("sorts");
}
this.sorts = sorts;
List<PropertyComparer<T>> list = new
List<PropertyComparer<T>>();
foreach (ListSortDescription item in sorts)
{
list.Add(new PropertyComparer<T>(item.PropertyDescriptor,
item.SortDirection == ListSortDirection.Descending));
}
this.comparers = list.ToArray();
}
public ListSortDescriptionCollection Sorts
{
get { return this.sorts; }
}
public PropertyDescriptor PrimaryProperty
{
get
{
return this.comparers.Length == 0
? null
: this.comparers[0].Property;
}
}
public ListSortDirection PrimaryDirection
{
get
{
return this.comparers.Length == 0
? ListSortDirection.Ascending
: this.comparers[0].Descending
? ListSortDirection.Descending
: ListSortDirection.Ascending;
}
}
int IComparer<T>.Compare(T x, T y)
{
int result = 0;
foreach (PropertyComparer<T> t in this.comparers)
{
result = t.Compare(x, y);
if (result != 0)
{
break;
}
}
return result;
}
}
public class PropertyComparer<T> : IComparer<T>
{
private readonly bool descending;
private readonly PropertyDescriptor property;
public PropertyComparer(PropertyDescriptor property, bool descending)
{
if (property == null)
{
throw new ArgumentNullException("property");
}
this.descending = descending;
this.property = property;
}
public bool Descending
{
get { return this.descending; }
}
public PropertyDescriptor Property
{
get { return this.property; }
}
public int Compare(T x, T y)
{
int value = Comparer.Default.Compare(this.property.GetValue(x),
this.property.GetValue(y));
return this.descending ? -value : value;
}
}
An alternate is to convert your List into DataTable and then bind that DataTable to the DataGridView through BindingSource. This way your DataGridView will inherit the sort options available with the DataTable.
For converting a list to DataTable, refer the article:
How to fill a datatable with List<T>