As you can see in the pic below, for a ListView Control you can add Items using the Properties pane.
How do I enable this kind of stuff for my UserControl?
I'm not getting anything when I search Google, but I'm probably not using the correct terms.
Does anybody know?
Thanks
You need to create a class that defines the object type that the collection ids composed of. A listView has ListViewItem objects. A TabControl has TabPage objects. Your control has objects which are defined by you. Let's call it MyItemType.
You also need a wraper class for the collection. A simple implementation is shown below.
public class MyItemTypeCollection : CollectionBase
{
public MyItemType this[int Index]
{
get
{
return (MyItemType)List[Index];
}
}
public bool Contains(MyItemType itemType)
{
return List.Contains(itemType);
}
public int Add(MyItemType itemType)
{
return List.Add(itemType);
}
public void Remove(MyItemType itemType)
{
List.Remove(itemType);
}
public void Insert(int index, MyItemType itemType)
{
List.Insert(index, itemType);
}
public int IndexOf(MyItemType itemType)
{
return List.IndexOf(itemType);
}
}
Finally you need to add a member variable for the collection to your user control and decorate it properly:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public MyItemTypeCollection MyItemTypes
{
get { return _myItemTypeCollection; }
}
and you now have a simple interface that allows you to browse and edit the collection. Leaves a lot to be desired still but to do more you will have to learn about custom designers which can be difficult to understand and implement.
Related
I was trying to implement ListViewCachingStrategy=RecycleElement in a Xamarin.Forms xaml page and everything displays correctly until I try to scroll. Then I get an exception that says that INativeElementView needs to be implemented. I designed the views in each native platform's ui designer and have a custom renderer for each. This crash does not happen if I use RetainElement instead of RecycleElement. Any ideas or suggestions?
Edit: My view that I created in the designer is called FeeViewCell and if I understand it right that is where INativeElementView is supposed to be implemented (in FeeViewCell.cs). My problem is now that casting to an element returns a System.InvalidCastException saying that the specified cast is not valid. Here is my code implementing INativeElementView:
public Element Element
{
get
{
return this.ToView() as Element;
}
}
I also have this as a question on the Xamarin forum here.
The INativeNativeView interface needs to be implemented on the custom cell class. In my example in iOS I in the custom renderer provides an item that comes from the pcl in my case a FeeCell.
This is part of my custom renderer
public class FeeCellRenderer : ViewCellRenderer
{
public override UIKit.UITableViewCell GetCell(Cell item, UIKit.UITableViewCell reusableCell, UIKit.UITableView tv)
{
var x = (FeeCell)item;
//Do whatever you need to here.
}
}
This is part of my custom ui class:
public partial class FeeViewCell : UITableViewCell, INativeElementView
{
public static readonly NSString Key = new NSString(nameof(FeeViewCell));
Element _element;
public Element Element
{
get { return _element; }
set { _element = value; }
}
}
It also works to do this:
public Element Element
{
get { return new FeeCell(); }
}
I have a app that makes use of the PropertyGrid in C#/.NET
the PropertGrid holds onto the MyAppObject class/object shown below..
class MyAppObject
{
private List<MyObject> oItems;
public List<MyObject> Items
{
get { return this.oItems; }
}
}
And so far it works well, nice and simple. I want the property grid to allow users to view the items, which it does well, however when you select the property in the PropertyGrid the dialog also allows to add more List<MyObject> items.
I do not want this, I only want to have the ability to show the items, not edit them.
I thought by not providing the setter (set { this.oItems = value; }):
then it wouldnt allow the add button.
Hope this makes sense, The screenshots shows the dialog, and I circled the buttons I want to remove.
thanks
If you expose it as a read-only list, it should do what you need:
[Browsable(false)]
public List<MyObject> Items
{
get { return this.oItems; }
}
// this (below) is the one the PropertyGrid will use
[DisplayName("Items")]
public ReadOnlyCollection<MyObject> ReadOnlyItems
{
get { return this.oItems.AsReadOnly(); }
}
Note that the members of individual objects (MyObject instances) will still be editable, unless you decorate them as [ReadOnly(true)].
As you note, the setter is not necessary to add/remove/edit items. That is because the grid still has full access to the .Add, .Remove and indexer (list[index]) operations.
This is a slightly tricky one; the solution involves building with the full .NET Framework (since the client-only framework doesn't include System.Design). You need to create your own subclass of CollectionEditor and tell it what to do with the temporary collection after the UI is finished with it:
public class MyObjectEditor : CollectionEditor {
public MyObjectEditor(Type type) : base(type) { }
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
return ((MyObject)context.Instance).Items;
}
}
Then you have to decorate your property with the EditorAttribute:
[Editor(typeof(MyObjectEditor), typeof(UITypeEditor))]
public List<MyObject> Items{
// ...
}
Reference: What's the correct way to edit a collection in a property grid
Alternative:
return new ReadOnlyCollection(oItems);
OR
return oItems.AsReadOnly();
When I add an item to the CheckedListBox list box I also want to store a reference to another object. I tried adding a new instance of this object to the CheckedListBox.
public class CheckedListBoxExtention : CheckedListBox
{
private ReferenceItem _referenceItem;
public ReferenceItem storedItem
{
get { return _referenceItem; }
set { _referenceItem = value; }
}
public CheckedListBoxExtention(ReferenceItem storedItem)
{
_referenceItem = storedItem;
}
}
This works in that later when I foreach though the items in CheckedListBox I have a reference to the _referenceItem object. However, when I add items like this, CheckedListBox shows up as blank (the list in the GUI itself). So I am trying to find a way to override the item text or something like that.
This is the code I used to fix the problem
class ReferenceItemWrapper
{
private ReferenceItem _item;
public ReferenceItemWrapper(ReferenceItem item)
{
_item = item;
}
public ReferenceItem getItem
{get {return _item;}}
public override string ToString()
{
return _item.ToString();
}
}
I am a bit new to wrappers. Why exactly did it work after it was wrapped when it did not work when I added the ReferenceItem directly to the CheckedListBox?
The CheckedListBox uses the ToString method of the objects in the list to populate the captions in the box. Rather than extend the CheckedListBox, just create a wrapper class that lets you store both your reference and a caption, and implements a ToString method which returns your caption. Just create one of your wrapper objects, stick the text in it, stick your reference in it, then add the wrapper object to the list box.
I have a custom collection that I am passing to a WPF client, which is binding the collection to a datagrid using AutoGenerateColumns="True". The datagrid, however, is displaying empty rows (albeit the right number of empty rows). What am I doing wrong? Following is some sample code. For now I've omitted everything having to do with INotifyPropertyChanged and INotifyCollectionChanged because, well, I first want to have some data showing up in the grid.
I should also mention that I've tried implementing the above two interfaces, but they seem to have nothing to do with this issue.
(You might not actually want to look at the sample code as there's absolutely nothing interesting about it. The collection implementation is just wrapping an inner List.)
Some random POCO:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Simple collection implementation:
public class MyCollection<T> : IList<T>
{
private List<T> list = new List<T>();
public MyCollection()
{
}
public MyCollection(IEnumerable<T> collection)
{
list.AddRange(collection);
}
#region ICollection<T> Members
public void Add(T item)
{
list.Add(item);
}
public void Clear()
{
list.Clear();
}
public bool Contains(T item)
{
return list.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
list.CopyTo(array, arrayIndex);
}
public int Count
{
get { return list.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
return list.Remove(item);
}
#endregion
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return list.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region IList<T> Members
public int IndexOf(T item)
{
return list.IndexOf(item);
}
public void Insert(int index, T item)
{
list.Insert(index, item);
}
public void RemoveAt(int index)
{
list.RemoveAt(index);
}
public T this[int index]
{
get { return list[index]; }
set { list[index] = value; }
}
#endregion
}
The XAML:
<Window x:Class="TestWpfCustomCollection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid AutoGenerateColumns="True"
HorizontalAlignment="Stretch"
Name="dataGrid1" VerticalAlignment="Stretch"
ItemsSource="{Binding}"
/>
</Grid>
</Window>
The window's code-behind:
public MainWindow()
{
InitializeComponent();
MyCollection<Person> persons = new MyCollection<Person>()
{
new Person(){FirstName="john", LastName="smith"},
new Person(){FirstName="foo", LastName="bar"}
};
dataGrid1.DataContext = persons;
}
By the way, if you change the code-behind to use a List<Person> instead of the MyCollection<Person>, everything works as expected.
EDIT:
The above code is not taken from the real situation. I have only posted it to show what I am doing in order to test my problem and to make it easier to replicate it. The actual custom collection object is quite complex and I cannot post it here. Again, I'm just trying to understand the basic concept behind what needs to be done in order for a datagrid to properly bind to a custom collection and automatically generate columns for the underlying objects.
Apparently, in order for AutoGenerateColumns to work in a WPF DataGrid, your collection has to implement IItemProperties, although I've found that wrapping my collection in a (windows forms) BindingList does the trick as well (it actually wraps your collection, unlike the ObservableCollection which just copies your collections' members into itself).
What is your MyCollection<T> type adding over List<T>? Either way, I would use an ObservableCollection instead (so that the UI is notified of additions/removals), and implement INotifyPropertyChanged on your Person class so that the UI is notified of changes to that type's property values.
EDIT
I don't think it's particularly trivial binding to your own custom collection type. You'll need to implement INotifyPropertyChanged and INotifyCollectionChanged on that type. This article might be of some use - http://www.e-pedro.com/2009/04/creating-a-custom-observable-collection-in-wpf/
Another option if you are using MVVM, is to use your custom collection type on your model, and use a standard ObservableCollection on your view model and populate your view models collection from your model, then bind your grid to your view models collection.
I think you using your knowlege of Windows Forms by writing
dataGrid1.DataContext = persons;
In WPF DataGrid is connected to the collection (say List) very simple:
dataGrid1.ItemsSource = persons;
I am using WPF and C#
I have a button that opens a window, which also includes a button that adds an User object item to a listbox and I want the listbox index to be updated after insertion. I understood that the solution is about using observable class of INotifyCollectionChanged but actually I dont know how and where to use them. Can you help me on determining what and where to implement, register, fire etc.
Edit:
I succeeded this by Quartermeister's help where my User object is collected in a list, but now I want to do the same thing where my object is collected in a dictionary
The easiest way is to use System.Collections.ObjectModel.ObservableCollection<T> as your list. This implements INotifyCollectionChanged and INotifyPropertyChanged for you.
You would have a property on your DataContext object of this type, and use data binding on ListBox.ItemsSource to bind it to that property. The ListBox will automatically update its list of elements when the collection changes.
In the class that is your DataContext:
public class MyClass
{
public ObservableCollection<string> Items { get; set; }
}
In Xaml:
<ListBox ItemsSource="{Binding Items}">
</ListBox>
It sounds like you also want an observable dictionary, but unfortunately there is not one built into the framework. You could try using Dr. Wpf's implementation of ObservableDictionary from his post "Can I bind my ItemsControl to a dictionary?".
Implementing an observable dictionary is hard. It's much simpler to maintain an parallel observable collection, one that contains the dictionary's values. Bind the view to that collection, and make sure that any code which adds or removes values to/from the dictionary updates the both the dictionary and the parallel collection.
If you really wanted to go crazy, you could implement a subclass of ObservableCollection to hold your objects, and make that class maintain the dictionary, e.g.:
public class KeyedObject
{
public string Key { get; set; }
public object Value { get; set; }
}
public class ObservableMappedCollection : ObservableCollection<KeyedObject>
{
private Dictionary<string, KeyedObject> _Map;
public ObservableMappedCollection(Dictionary<string, KeyedObject> map)
{
_Map = map;
}
protected override void InsertItem(int index, KeyedObject item)
{
base.InsertItem(index, item);
_Map[item.Key] = item;
}
protected override void RemoveItem(int index)
{
KeyedObject item = base[index];
base.RemoveItem(index);
_Map.Remove(item.Key);
}
protected override void ClearItems()
{
base.ClearItems();
_Map.Clear();
}
protected override void SetItem(int index, KeyedObject item)
{
KeyedObject oldItem = base[index];
_Map.Remove(oldItem.Key);
base.SetItem(index, item);
_Map[item.Key] = item;
}
}
There are a bunch of potential problems in the above, mostly having to do with duplicate key values. For instance, what should SetItem do if you're adding an object whose key is already in the map? The answer really depends on your application. Issues like that also hint at why there isn't an observable dictionary class in the framework.