Sort ListCollectionView from second item down - wpf

In an MVVM environment, I have a ListCollectionView bound to an ObservableCollection. My Foo objects have a IsDefault property to them, and my requirement is to have that item first in the list, and the rest should be alpha-sorted.
So this code only sorts the whole list, obviously:
_list = new ListCollectionView(Model.Data);
_list.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
Not sure how to make sure that item #3 (for example, which has IsDefault=true) be at the top of the list, and the rest (that have IsDefault=false) be alpha sorted.
Is this a case to use _list.CustomSort and implement IComparer in some way?

Yes, this is exactly the case where you need to use ListCollectionView.CustomSort. Custom sorting is mutually exclusive with using SortDescriptions; the documentation for the former is explicit about this:
Setting this property clears a previously set SortDescriptions value.
So what you need to do is define an IComparer and use it to sort the view:
class CustomComparer : IComparer
{
public int Compare (object lhs, object rhs)
{
// missing: checks for null, casting to Model.Data, etc etc
if (lhsModelData.IsDefault && rhsModelData.IsDefault) {
return lhsModelData.Name.CompareTo(rhsModelData.Name);
}
else if (lhsModelData.IsDefault) {
return -1;
}
else if (rhsModelData.IsDefault) {
return 1;
}
else {
return lhsModelData.Name.CompareTo(rhsModelData.Name);
}
}
}

Related

Why don't array types have Add() method although they implement IList<T>?

Array types such as int[] implement many interfaces, and IList is one of them. IList requires to implement the Add(T) method (because IList inherits from ICollection).
I am writing a "CircularArray" class which contains an array in itself, but doesn't have any index out of range exceptions because of being circular.
The class's definition is this:
public class CircularArray<T> : ICloneable, IList<T>, IStructuralComparable, IStructuralEquatable
It should implement all interfaces Arrays do, so i have to implement The Add method. But... Arrays don't have this method, although they implement the "ICollection" interface.
How is it all done for arrays so that they can implement the ICollection interface and not have Add method? I want to do the same for my CircularArray class.
The explanation is in Daniel's answer in the other topic. To do what you want to achieve is to use explicit interface implementation.
interface ILeft
{
int P { get;}
}
class Middle : ILeft
{
int ILeft.P { get { return 0; } }
}
var mid= new Middle();
var abc = mid.P // invalid;
var abc2 = (mid as ILeft).P; //valid

How to make a Listview non selectable?

I crated an application using DialogBox. I added a ListView to it.
Is there a way to make this Listview non-selectable?
The user should not select anything, it should just display some data.
Thanks in advance!
In order to disable list items on list creation you have to subclass from ArrayAdapter. You have to override the following methods: isEnabled(int position) and areAllItemsEnabled(). In former you return true or false depending is list item at given position enabled or not. In latter you return false.
If you want to use createFromResource() you will have to implement that method as well, since the ArrayAdapter.createFromResource() still instantiates ArrayAdapter instead of your own adapter.
Finally, the code would look something like the following:
class MenuAdapter extends ArrayAdapter<CharSequence> {
public MenuAdapter(
Context context, int textViewResId, CharSequence[] strings) {
super(context, textViewResId, strings);
}
public static MenuAdapter createFromResource(
Context context, int textArrayResId, int textViewResId) {
Resources resources = context.getResources();
CharSequence[] strings = resources.getTextArray(textArrayResId);
return new MenuAdapter(context, textViewResId, strings);
}
public boolean areAllItemsEnabled() {
return false;
}
public boolean isEnabled(int position) {
// return false if position == position you want to disable
}
}

How to move by code the BindingSource to a specific record

Using datagridview bound to BindingSource control bound to a LINQ to SQL class, I wonder how to position the bindingSource to a specific record, that is, when I type a Product name in a textbox, the bindingsource should move to that specific product. Here is my code:
In my form FrmFind:
NorthwindDataContext dc;
private void FrmFind_Load(object sender, EventArgs e)
{
dc = new NorthwindDataContext();
var qry = (from p in dc.Products
select p).ToList();
FindAbleBindingList<Product> list = new FindAbleBindingList<Product>(qry);
productBindingSource.DataSource = list.OrderBy(o => o.ProductName);
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
TextBox tb = sender as TextBox;
int index = productBindingSource.Find("ProductName", tb.Text);
if (index >= 0)
{
productBindingSource.Position = index;
}
}
In the program class:
public class FindAbleBindingList<T> : BindingList<T>
{
public FindAbleBindingList()
: base()
{
}
public FindAbleBindingList(List<T> list)
: base(list)
{
}
protected override int FindCore(PropertyDescriptor property, object key)
{
for (int i = 0; i < Count; i++)
{
T item = this[i];
//if (property.GetValue(item).Equals(key))
if (property.GetValue(item).ToString().StartsWith(key.ToString()))
{
return i;
}
}
return -1; // Not found
}
}
How can I implement the find method to make it work?
You can combine the BindingSource.Find() method with the Position property.
For example, if you have something like this in your TextBox changed event handler:
private void textBox1_TextChanged(object sender, EventArgs e)
{
TextBox tb = sender as TextBox;
int index = bs.Find("Product", tb.Text);
if (index >= 0)
{
bs.Position = index;
}
}
This of course will depend on a lot of things like the particular implementation of the Find method the data source for the binding source has.
In a question you asked a little while ago I gave you an implementation for Find which worked with full matches. Below is a slightly different implementation that will look at the start of the property being inspected:
protected override int FindCore(PropertyDescriptor property, object key)
{
// Simple iteration:
for (int i = 0; i < Count; i++)
{
T item = this[i];
if (property.GetValue(item).ToString().StartsWith(key.ToString()))
{
return i;
}
}
return -1; // Not found
}
Do note that the above method is case sensitive - you can change StartsWith to be case insensitive if you need.
One key thing to note about the way .Net works is that the actual type of an object is not sufficient all the time - the declared type is what consuming code knows about.
This is the reason why you get a NotSupported exception when calling the Find method, even though your BindingList implementation has a Find method - the code that receives this binding list doesn't know about the Find.
The reason for that is in these lines of code:
dc = new NorthwindDataContext();
var qry = (from p in dc.Products
select p).ToList();
FindAbleBindingList<Product> list = new FindAbleBindingList<Product>(qry);
productBindingSource.DataSource = list.OrderBy(o => o.ProductName);
When you set the data source for the binding source you include the extension method OrderBy - Checking this shows that it returns IOrderedEnumerable, an interface described here on MSDN. Note that this interface has no Find method, so even though the underlying FindableBindingList<T> supports Find the binding source doesn't know about it.
There are several solutions (the best is in my opinion to extend your FindableBindingList to also support sorting and sort the list) but the quickest for your current code is to sort earlier like so:
dc = new NorthwindDataContext();
var qry = (from p in dc.Products
select p).OrderBy(p => p.ProductName).ToList();
FindAbleBindingList<Product> list = new FindAbleBindingList<Product>(qry);
productBindingSource.DataSource = list;
In WinForms there are no entirely out of the box solutions for the things you are trying to do - they all need a little bit of custom code that you need to put together to match just your own requirements.
I took a different approach. I figured, programmatically, every record must be checked until a match is found, so I just iterated using the MoveNext method until I found a match. Unsure if the starting position would be the First record or not, so I used the MoveFirst method to ensure that is was.
There is one assumption, and that is that what you are searching for is unique in that column. In my case, I was looking to match an Identity integer.
int seekID;
this.EntityTableBindingSource.MoveFirst();
if (seekID > 0)
{
foreach (EntityTable sd in EntityTableBindingSource)
{
if (sd.ID != seekID)
{
this.t_EntityTableBindingSource.MoveNext();
}
else
{
break;
}
}
}
I didn't really care for either answer provided. Here is what I came up with for my problem:
// Create a list of items in the BindingSource and use labda to find your row:
var QuickAccessCode = customerListBindingSource.List.OfType<CustomerList>()
.ToList().Find(f => f.QuickAccessCode == txtQAC.Text);
// Then use indexOf to find the object in your bindingSource:
var pos = customerListBindingSource.IndexOf(QuickAccessCode);
if (pos < 0)
{
MessageBox.Show("Could not find " + txtQAC.Text);
}
else
{
mainFrm.customerListBindingSource.Position = pos;
}

Dependecy properties that depend on other properties

Class C implements INotifyPropertyChanged.
Assume the C has Length, Width and Area propreties, where Area = Length * Width. A change in either might cause a change in area. All three are bound, i.e. the UI expects all three to notify of changes in their values.
When either Length or Width change, their setters call NotifyPropertyChanged.
How should I treat the calculated Area property? Currently the pattern I can think of is detecting in NotifyPropertyChanged whether the changed property is either Length or Width and, if such is the case, initiate an addional PropertyChanged notification for Area. This, however, requires that I maintain inside NotifyPropertyChanged the dependencies graph, which I feel is an anti-pattern.
So, my question is: How should I code dependency properties that depend on other dependency properties?
edit: People here suggested that Length and Width also call NotifyPropertyChanged for Area. Again, I think this is an anti-pattern. A property (IMHO) shouldn't be aware of who depends on it, as shouldn't NotifyPropertyChanged. Only the property should be aware of who it depends on.
This issue kept on bugging me, so I re-opened it.
First, I'd like to appologize for anyone taking my "anti-pattern" comment personally. The solutions offered here were, indeed, how-it's-done in WPF. However, still, IMHO they're bad practices caused, deficiencies in ther framework.
My claim is that the information hiding guide dictates that when B depeneds on A, A should not be aware of B. For exmaple, when B derives from A, A should not have code saying: "If my runtime type is really a B, then do this and that". Simiarily, when B uses A, A should not have code saying: "If the object calling this method is a B, then ..."
So it follows that if property B depends on property A, A shouldn't be the one who's responsible to alert B directly.
Conversely, maintaining (as I currently do) the dependencies graph inside NotifyPropertyChanged is also an anti-pattern. That method should be lightweight and do what it name states, not maintain dependency relationships between properties.
So, I think the solution needed is through aspect oriented programming: Peroperty B should use an "I-depend-on(Property A)" attribute, and some code-rewriter should create the dependency graph and modify NotifyPropertyChanged transparently.
Today, I'm a single programmer working on a single product, so I can't justify dvelving with this any more, but this, I feel, is the correct solution.
Here is an article describing how to create a custom attribute that automatically calls PropertyChanged for properties depending on another property: http://www.redmountainsw.com/wordpress/2012/01/17/a-nicer-way-to-handle-dependent-values-on-propertychanged/
The code will look like this:
[DependsOn("A")]
[DependsOn("B")]
public int Total
{
get { return A + B; }
}
public int A
{
get { return m_A; }
set { m_A = value; RaisePropertyChanged("A"); }
}
public int B
{
get { return m_B: }
set { m_B = value; RaisePropertyChanged("B"); }
}
I haven't tried it myself but I like the idea
When the Length or Width properties are changed you fire PropertyChanged for Area in addition to firing it for either Length or Width.
Here is a very simple implementation based on backing fields and the method OnPropertyChanged to fire the PropertyChanged event:
public Double Length {
get { return this.length; }
set {
this.length = value;
OnPropertyChanged("Length");
OnPropertyChanged("Area");
}
}
public Double Width {
get { return this.width; }
set {
this.width = value;
OnPropertyChanged("Width");
OnPropertyChanged("Area");
}
}
public Double Area {
get { return this.length*this.width; }
}
Doing it like this is certainly not an anti-pattern. That is exactly the pattern for doing it. You as the implementer of the class knows that when Length is changed then Area is also changed and you encode it by raising the appropriate event.
Then you should raise twice, in Length and Width property setters. One for the actual property and one for the Area property.
for example:
private int _width;
public int Width
{
get { return _width; }
set
{
if (_width == value) return;
_width = value;
NotifyPropertyChanged("Width");
NotifyPropertyChanged("Area");
}
}
People here suggested that Length and Width also call
NotifyPropertyChanged for Area. Again, I think this is an
anti-pattern. A property (IMHO) shouldn't be aware of who depends on
it, as shouldn't NotifyPropertyChanged. Only the property should be
aware of who it depends on.
This is not an anti-pattern. Actually, your data encapsulated inside this class, so this class knows when and what changed. You shouldn't know outside of this class that Area depends on Width and Length. So the most logical place to notify listeners about Area is the Width and Length setter.
A property (IMHO) shouldn't be aware of who depends on it, as
shouldn't NotifyPropertyChanged.
It does not break encapsulation, because you are in the same class, in the same data structure.
An extra information is that knockout.js (a javascript mvvm library) has a concept which accessing this problem: Computed Observables. So I believe this is absolutely acceptable.
Here is a possible implementation of an attribute:
public class DependentPropertiesAttribute : Attribute
{
private readonly string[] properties;
public DependentPropertiesAttribute(params string[] dp)
{
properties = dp;
}
public string[] Properties
{
get
{
return properties;
}
}
}
Then in the Base View Model, we handle the mechanism of calling property dependencies:
public class ViewModelBase : INotifyPropertyChanged
{
public ViewModelBase()
{
DetectPropertiesDependencies();
}
private readonly Dictionary<string, List<string>> _dependencies = new Dictionary<string, List<string>>();
private void DetectPropertiesDependencies()
{
var propertyInfoWithDependencies = GetType().GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(DependentPropertiesAttribute))).ToArray();
foreach (PropertyInfo propertyInfo in propertyInfoWithDependencies)
{
var ca = propertyInfo.GetCustomAttributes(false).OfType<DependentPropertiesAttribute>().Single();
if (ca.Properties != null)
{
foreach (string prop in ca.Properties)
{
if (!_dependencies.ContainsKey(prop))
{
_dependencies.Add(prop, new List<string>());
}
_dependencies[prop].Add(propertyInfo.Name);
}
}
}
}
protected void OnPropertyChanged(params Expression<Func<object>>[] expressions)
{
expressions.Select(expr => ReflectionHelper.GetPropertyName(expr)).ToList().ForEach(p => {
RaisePropertyChanged(p);
RaiseDependentProperties(p, new List<string>() { p });
});
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected virtual void RaisePropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
protected void RaiseDependentProperties(string propertyName, List<string> calledProperties = null)
{
if (!_dependencies.Any() || !_dependencies.ContainsKey(propertyName))
return;
if (calledProperties == null)
calledProperties = new List<string>();
List<string> dependentProperties = _dependencies[propertyName];
foreach (var dependentProperty in dependentProperties)
{
if (!calledProperties.Contains(dependentProperty))
{
RaisePropertyChanged(dependentProperty);
RaiseDependentProperties(dependentProperty, calledProperties);
}
}
}
}
Finally we define dependencies in our ViewModel
[DependentProperties("Prop1", "Prop2")]
public bool SomeCalculatedProperty
{
get
{
return Prop1 + Prop2;
}
}

CanUserSortColumns in datagrid doesn't make effect?

I have a datagrid with binding item source. I have set CanUserSortColumns property of datagrid into TRUE and so do with all inner columns in datagrid but user still doesn't be able to sort columns.
Is there something I have missed ?
Are you explicitly defining DataTemplate for your headers? In case yes you have to set property on your column "SortMemberPath" to your CLR property on which you want to sort your column. This link might prove helpful to you, have a look at it -
WPF4 Datagrid doesn't sort on column headers
Thanks guys. That worked. I just want to add.
The types of those columns must implement the non-generic IComparable, which is usually not a problem if you are using primitive or .net types. But if you have your own types, then you will have to add it.
E.g.
/* this is my own type */
public struct Distance : ..., IComparable, IComparable<Distance>, ... {
...
public int CompareTo(object obj)
{
if (obj == null) { return 1; }
if (obj.GetType() != typeof(Distance)) { return 0; }
return CompareTo((Distance)obj);
}
public int CompareTo(Distance other) { return _meters.CompareTo(other._meters); }
}

Resources