Remove duplicates from listbox/combobox in WPF - wpf

I've seen answers mentioning something compact like this:here
List<T> withDupes = LoadSomeData();
List<T> noDupes = withDupes.Distinct().ToList();
So I tried the following (syntax)
List<InfoControl> withDupes = (List<InfoControl>)listBox1.ItemsSource;
listBox1.ItemsSource = withDupes.Distinct().ToList();
but withDupes is null ? Perhaps I am retrieving the wrong data list. I added InfoControls one at a time.
Is there something else I should be implementing in InfoControl class? (Equal,hashCode)?
Thanks
Addendum 1: [ignore I should not be translating from Java :) ]
Also have (translated from a Java example, not sure it's 100% correct) declared in the InfoControl class..
public Boolean Equals(Object obj)
{ if (obj == this) { return true; }
if (!(obj is InfoControl)) { return false; }
InfoControl other = (InfoControl)obj;
return this.URL.Equals(other.URL); }
public int hashCode()
{ return this.URLFld.Content.GetHashCode(); }
Addendum 2:
When I try to use override based on the msdn link custom type example it says it is sealed :)
It does not seem distinct is stepping thru GetHashCode() and I am still getting the same listbox.items.count after distinct.
bool IEquatable<InfoControl>.Equals(InfoControl other)
{
if (Object.ReferenceEquals(other, null)) return false;
if (Object.ReferenceEquals(this, other)) return true;
return URL.Equals(other.URL);
}
public int GetHashCode(InfoControl obj)
{
return obj.URL.GetHashCode();
}
Addendum 3:
When I try override VS2010 says it is sealed? "cannot override inherited member 'System.Windows.DependencyObject.GetHashCode()' because it is sealed" what am I doing wrong?
public override int GetHashCode()
{
return URL.GetHashCode();
}
public string URL
{
get { return this.URLFld.Content.ToString() ; }
set
{
this.URLFld.Content = value;
}
}
.
Addendum 4:
public partial class InfoControl : UserControl
, IEquatable<YouTubeInfoControl>
{
private string URL_;
public string URL
{
get { return URL_; }
set
{
URL_ = value;
}
}
bool IEquatable<YouTubeInfoControl>.Equals(YouTubeInfoControl other)
{
if (Object.ReferenceEquals(other, null)) return false;
if (Object.ReferenceEquals(this, other)) return true;
return URL == other.URL;
}
public override int GetHashCode()
{
return URL.GetHashCode();
}
}

A ListBox's items can either be set via ListBox.Items or ListBox.ItemsSource, if you add items using listBox1.Items.Add this does not affect the ItemsSource which will stay null. In this case you should get your initial list from listBox1.Items.

If you're adding the InfoControl objects one at a time, the ItemSource of the listBox will remain set to NULL. You're better off binding a List to the listbox which will allow you to get the data back off of the ItemSource property later

Related

How to pop to root

I am writing a WPF application with MvvmCross. I have a custom view presenter that I want to use so that I can pop multiple view models in 1 shot. Here is my view presenter:
public class ViewPresenter : MvxWpfViewPresenter
{
ContentControl _contentControl;
Type _currentViewModelType;
IMvxViewModel _rootViewModel;
public ViewPresenter(ContentControl c) : base(c)
{
_contentControl = c;
AddPresentationHintHandler<SetRootHint>(SetRootHintHandler);
AddPresentationHintHandler<PopToRootHint>(PopToRootHintHandler);
}
protected override void ShowContentView(FrameworkElement element, MvxContentPresentationAttribute attribute, MvxViewModelRequest request)
{
base.ShowContentView(element, attribute, request);
_currentViewModelType = request.ViewModelType;
}
private bool SetRootHintHandler(SetRootHint hint)
{
_rootViewModel = hint.CurrentViewModel;
return true;
}
private bool PopToRootHintHandler(PopToRootHint hint)
{
// How to pop all the way down to _rootViewModel ?
return true;
}
}
How can I pop all the way back to _rootViewModel? Is there a better way of popping back multiple view models in one shot?
I ended up writing a helper class that keeps a reference of all the view models, as well as the one you set as the Root. And then I can just call my PopToRoot method.
public class NavigationStack
{
private readonly List<IMvxViewModel> _stack;
public IMvxViewModel Root { get; set; }
public NavigationStack()
{
_stack = new List<IMvxViewModel>();
}
public void AddToStack(IMvxViewModel viewModel)
{
_stack.Add(viewModel);
}
public async Task PopToRoot(IMvxNavigationService navigationService)
{
if (Root == null)
{
throw new Exception("Can not pop to root because Root is null.");
}
else
{
_stack.Reverse();
foreach (var v in _stack)
{
if (v != Root)
{
await navigationService.Close(v);
}
else
{
break;
}
}
_stack.Clear();
}
}
}
It works, but I'm not sure if this is a good idea since I'm keeping a reference to all the IMvxViewModel in my app, and closing them one after another...does anyone know if this code can cause any problems in the framework?

Winforms Designer custom property does not keep its value when I build

I have a control that I subclass from Label (as an example) and add a custom property to it. I have a custom editor that allows me to pick an object of a custom type. The custom type is attributed with the converter and and editor. Editing works fine - I click in my property cell in the VS designer property grid and the ellipsis displays. I click that and I get my custom form displaying my list to choose from. I make a selection and that selection shows up in my property grid. All good. However, I can not seem to clear that value by backspacing over it like I can with most properties. And more importantly, as soon as I build the project the property value disappears. Before I build, I can go from control to control and the value is set properly, but as soon as I build (or save and close the form) the property loses its value. My custom control looks like this:
public class MyLabel : Label
{
private MyAlias _alias;
public MyAlias Alias
{
get
{
return _alias;
}
set
{
if (_alias != value)
{
_alias = value;
}
}
}
}
The custom type:
[System.ComponentModel.TypeConverter(typeof(MyConverter))]
[System.ComponentModel.Editor(MyTypeEditor, System.Drawing.Design.UITypeEditor)]
public class MyAlias
{
private string _aliasName;
public string AliasName
{
get
{
return _aliasName;
}
set
{
if (_aliasName != value)
{
_aliasName = value;
}
}
}
public MyAlias(string aliasName)
{
if (aliasName == null)
{
return;
}
_aliasName = aliasName;
}
public override string ToString()
{
return _aliasName;
}
}
Custom converter:
public class MyConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (!(value is string))
{
return base.ConvertFrom(context, culture, value);
}
return new MyAlias((string)value);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType == null)
{
throw new ArgumentNullException("destinationType");
}
MyAlias alias = (MyAlias)value;
return alias.ToString();
}
}
Custom Editor:
public partial class AliasEditorForm : Form
{
private object _value;
public object Value
{
get
{
return _value;
}
set
{
if (_value != value)
{
_value = value;
}
}
}
public AliasEditorForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Button does have DialogResult property set to OK
_value = new MyAlias(...text string from form controls here...);
this.Close();
}
}
public class MyTypeEditor : UITypeEditor
{
protected IWindowsFormsEditorService edSvc = null;
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (null != context && null != context.Instance && null != provider)
{
edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
if (null != edSvc)
{
AliasEditorForm form = new AliasEditorForm();
form.Value = value;
DialogResult r = edSvc.ShowDialog(form);
return (DialogResult.OK == r) ? form.Value : value;
}
}
return value;
}
}
It's been a long time since I've worked in WinForms and I never really did much dabbling with design-time stuff so forgive me if I've done something terribly stupid. If I didn't provide enough info, let me know. Thanks in advance!
EDIT: I notice it is not generating the code for the property in the form.Designer.cs file. Still not sure why...
EDIT: Changed the code for the type converter. I can now clear the property by backspacing. I still can't get the designer to generate the code to preserve the property value.
Dennis

WPF-Multiple Views on ObservableCollection

I have a viewmodel containing two CollectionViews defined.
One I am using for navigation and data entry/edit.
Another I want to use for filtering purpose and show the filteration in some Listview on the form.
I don't want the main view(used for DataEntry purpose) to get affected while I applying filteration on observablecollection.
Thanks in Advance!
As long as you're using separate collection views, changing one won't affect the other. That is the point of collection views - they're independent views on the same collection.
ok, Got it! and went ahead with the same idea. But when I did so, I get Error = "The calling thread cannot access this object because a different thread owns it.". Hence my filteration doesn't work.. Following is the code-
public ICollectionView Clients { get; set; } //Used for Data-navigation/modification
public ListCollectionView CodeView { get; set; } // to be used for filteration purpose on form.
string searchText = String.Empty;
public string CompanyCodeSearch
{
get { return searchText; }
set
{
try
{
searchText = value;
OnPropertyChanged("CompanyCodeSearch");
CodeView.Filter = new Predicate<object>(cmFilterData);
}
catch (Exception ex)
{
}
}
}
private bool cmFilterData(object item)
{
bool _filteredData = false;
try
{
var value = (item as cntClient);
if (value == null || value.CompanyCode == null)
return false;
_filteredData = value.CompanyCode.StartsWith(this.CompanyCodeSearch);
return _filteredData;
}
catch (Exception ex)
{
return false;
}
}

Initializing sorted collections from XAML

I have a SortedObservableCollection class (initially based on this). It does exactly what it promises - it is a generic collection, which implements INotifyCollectionChanged and maintains its elements in sorted order (according to a provided IComparer). The order is only checked upon insertion though - when an item is being inserted, it is inserted into a correct place into the collection.
However I encountered a major issue while trying to initialize the collection from XAML with syntax like this (the Items property is of SortedObservableCollection<MyItem> type, Priority is the sorting key):
<my:SomeElement.Items>
<my:MyItem Priority="0">
<my:MyItem Priority="2">
<my:MyItem Priority="1">
</my:SomeElement.Items>
This should result in collection with items in order 2, 1, 0, but it results in order 1, 2, 0.
It took me quite some time to discover the cause: Collection items are first constructed, then added to the collection and only then are their properties' values assigned.
I couldn't find this behavior documented anywhere and I agree it doesn't really matter usually. But in my case, the Priority property is always of value 0, so the sorting doesn't take occur at all (in fact, the items are inserted in reverse order than they are in XAML). And the, after the sorting has taken place, the Priority is initialized.
Did you encounter this behavior yourself? Why is the XAML implemented like this? How can I work around this issue?
The only solution I can think of is to let the items implement INotifyPropertyChanged and then subscribe to it in the Add method (and then update the order when necessary), but I guess this would bring more trouble than it is worth (performance, memory leaks...).
Thanks for any help!
If you are aiming at a collection which is properly sorted at all times, then you'll need to go for the listening approach. You could make you items support a weak event mechanism to prevent them from holding a strong reference to the collection.
Another approach would be to defer sorting until the collection is "fully constructed". You could for example have a flag isSorted in your collection implementation. Have this flag set to false whenever the collection is modified (for simplicity) and check it before the collection is "read".
Something like this:
public void Add(T item)
{
_innerList.Add(item);
_isSorted = false;
}
and:
public int IndexOf(T item)
{
EnsureSorted();
return _innerList.IndexOf(item);
}
where EnsureSorted could look something like this:
private void EnsureSorted()
{
if (!_isSorted)
{
_innerList.Sort(_comparer);
_isSorted = true;
// TODO: Raise the CollectionChanged event here, specifying
// NotifyCollectionChangedAction.Reset
}
}
This should make your collection appear sorted while still allowing it to be unsorted while populating the list.
Perhaps this would be a feasible workaround?
Update:
I created a simple observable collection with this kind of deferred sorting. I think you might find it helpful, at least it should clearify what I mean.
The idea is to call the EnsureSorted method just before "reading" the collection and to clear the isSorted flag whenever collection is modified.
public class SortedObservableCollection<T> : IList<T>, IList, INotifyCollectionChanged, INotifyPropertyChanged
{
private readonly List<T> _innerList;
private IComparer<T> _comparer;
private bool _isSorted;
public event NotifyCollectionChangedEventHandler CollectionChanged;
public event PropertyChangedEventHandler PropertyChanged;
public SortedObservableCollection()
: this(null)
{
}
public SortedObservableCollection(IComparer<T> comparer)
{
_innerList = new List<T>();
_comparer = comparer ?? Comparer<T>.Default;
}
// Call this before "reading" collection
private void EnsureSorted()
{
if (!_isSorted)
{
_innerList.Sort(_comparer);
_isSorted = true;
}
}
// Call this after modifying the collection
private void NotifyChanged()
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Count"));
}
if (CollectionChanged != null)
{
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
_isSorted = false;
}
#region List implementation
public int IndexOf(T item)
{
EnsureSorted();
return _innerList.IndexOf(item);
}
public void Insert(int index, T item)
{
EnsureSorted();
_innerList.Insert(index, item);
NotifyChanged();
}
public void RemoveAt(int index)
{
EnsureSorted();
_innerList.RemoveAt(index);
NotifyChanged();
}
public T this[int index]
{
get
{
EnsureSorted();
return _innerList[index];
}
set
{
EnsureSorted();
_innerList[index] = value;
NotifyChanged();
}
}
public void Add(T item)
{
_innerList.Add(item);
NotifyChanged();
}
public void Clear()
{
_innerList.Clear();
NotifyChanged();
}
public bool Contains(T item)
{
return _innerList.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
EnsureSorted();
_innerList.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _innerList.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
if (!_innerList.Remove(item))
{
return false;
}
NotifyChanged();
return true;
}
public IEnumerator<T> GetEnumerator()
{
EnsureSorted();
return _innerList.GetEnumerator();
}
#endregion
// Non-generic implementation omitted for brevity...
}

Inherited Control Visible/Enabled Property Value Always True: PropertyGrid

I have created a custom WinForms hosting environment. Which has a toolbox and a PropertyGrid.
The controls displayed in the Toolbox are inherited from existing WinForm controls.
DropDownList Source:
public interface IPropertyFilter : ICustomTypeDescriptor
{
PropertyDescriptorCollection FilterProperties(PropertyDescriptorCollection pdc);
List<string> GetPropertiesToShow();
}
[Serializable]
public class DropDownList : System.Windows.Forms.ComboBox, IPropertyFilter
{
public DropDownList()
{
}
#region IPropertyFilter Members
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(this, attributes, true);
return FilterProperties(pdc);
}
PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties()
{
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(this, true);
return FilterProperties(pdc);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public PropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public PropertyDescriptorCollection FilterProperties(PropertyDescriptorCollection pdc)
{
// Filter out properties that we do not want to display in PropertyGrid
return ControlDesignerHelper.GetBrowsableProperties(pdc, GetPropertiesToShow());
}
// Determines what properties of this control has to be shown in PropertyGrid
public List<string> GetPropertiesToShow()
{
// get a list of common properties that we want to show for all controls
List<string> browsableProps = ControlDesignerHelper.GetBasePropertiesToShow();
// add properties that are specific to this controls
browsableProps.Add("Items");
browsableProps.Add("AutoPostBack");
browsableProps.Add("AppendDataBoundItems");
browsableProps.Add("DataTextField");
browsableProps.Add("DataValueField");
return browsableProps;
}
#endregion
}
I have implemented ICustomTypeDescriptor to filter out properties that I do not want to show in the PropertyGrid.
Problem:
I am facing problem while serializing values of Enabled & Visible properties that are inherited from System.Windows.Forms.Control class.
WriteProperties Method (BasicDesignerLoader):
private void WriteProperties(XmlDocument document, PropertyDescriptorCollection properties, object value, XmlNode parent, string elementName)
{
foreach (PropertyDescriptor prop in properties)
{
System.Diagnostics.Debug.WriteLine(prop.Name);
if (prop.ShouldSerializeValue(value))
{
string compName = parent.Name;
XmlNode node = document.CreateElement(elementName);
XmlAttribute attr = document.CreateAttribute("name");
attr.Value = prop.Name;
node.Attributes.Append(attr);
DesignerSerializationVisibilityAttribute visibility = (DesignerSerializationVisibilityAttribute)prop.Attributes[typeof(DesignerSerializationVisibilityAttribute)];
switch (visibility.Visibility)
{
case DesignerSerializationVisibility.Visible:
if (!prop.IsReadOnly && WriteValue(document, prop.GetValue(value), node))
{
parent.AppendChild(node);
}
break;
case DesignerSerializationVisibility.Content:
object propValue = prop.GetValue(value);
if (typeof(IList).IsAssignableFrom(prop.PropertyType))
{
WriteCollection(document, (IList)propValue, node);
}
else
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(propValue, propertyAttributes);
WriteProperties(document, props, propValue, node, elementName);
}
if (node.ChildNodes.Count > 0)
{
parent.AppendChild(node);
}
break;
default:
break;
}
}
}
}
Problem # 1: The ShouldSerializeValue method for the Enabled & Visible property always returns false.
Problem # 2: Even if I skip the ShouldSerializeValue method check the GetValue method of the PropertyDescriptor always returns True.
Current Workaround:
As a workaround I have currently made the Enabled & Visible properties hidden using the BrowsableAttribute, and created two other boolean properties and used the DisplayNameAttribute to change their display name to be Enable & Visible.
But for this workaround I have to write these snippets in every control.
Am I missing something or doing anything wrong? Why are the Enabled & Visible property do not change?
You will find a long discussion about this issue here. (dead link, can't find a new one)
This MSDN page aldo makes this remark:
The InheritedPropertyDescriptor class
modifies the default value of a
property, so that the default value is
the current value at object
instantiation. This is because the
property is inherited from another
instance. The designer defines
resetting the property value as
setting it to the value that was set
by the inherited class. This value may
differ from the default value stored
in metadata.
ShouldSerializeValue's return value is based on the difference between the current value and the default value so I think this is directly related to your problem.
I hope this will help you figure out what happens in your own context.

Resources