WPF VS Collection editor tutorial? - c#

I have a usercontrol with a Collection property. What I want to achieve is to be able to add/modify/remove items of some data types of that collection via VS designer (Property window/Collection editor).
I have a simple class:
public class Quantity
{
public string Name { get; set; }
public Type DataType { get; set; }
}
In my UserControl I have:
private ObservableCollection<Quantity> _quantities = new ObservableCollection<Quantity>();
public ObservableCollection<Quantity> Quantities
{
get { return _quantities; }
}
And the thing is that I am able to change the Name property via that VS Collection editor but I am unable to change DataType property that way.
So what do I have to do to make it work?

I don't believe this can be achieved through the properties editor. You can however produce the result in XAML. Here is what it would look like using your example:
<my:UserControl1>
<my:UserControl1.Quantites>
<my:Quantity Name="Hello World" DataType="{x:Type sys:Boolean}"/>
<my:Quantity Name="This is a double" DataType="{x:Type sys:Double}"/>
</my:UserControl1.Quantites>
</my:UserControl1>
If you need access to system types (like I used in my example) you can include the following xmlns:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Hope it helps :)

Related

MVVMCross add multiple dynamic properties to viewmodel

I have an requirement to render the screen (screen type : form) dynamically based on the service response (rather than defining the UI manually). I was able to successfully bind predefined properties to dynamically created textboxes & textviews. Following is the sample code that used to bind predefined property to dynamic textbox
Type myType = typeof(DynamicViewModel);
PropertyInfo myPropInfo = myType.GetProperty(nameof(dynamicProperty)); //dynamicProperty -static property in VM
var set = this.CreateBindingSet<DynamicActivity, DynamicViewModel>();
set.Bind(editText).To(myPropInfo.Name); //editText - dynamically created textbox
set.Apply();
But the code needs to be further improved by dynamically creating the no of properties - matching with the no of UI elements dynamically created.
The project is created using Xamarin.Android with MVVMCross's latest version. Please share the way to generate dynamic string(or object type) properties in viewmodels that can be binded with dynamically generated view elements(textboxes & textviews).
Thanks
There are a couple of ways to do this.
One is using Reflection as you are doing there, but you could have performance issues.
The other way is to arrange a bit the data and model you are getting from the server to be something like you can then use some Factories to build your View/VM:
So it could be:
public enum DataType
{
String,
Number,
Boolean,
List,
// and any other types that you need
}
public class OptionItemModel
{
public int Id { get; set; }
public string Name { get; set; }
}
public class FieldModel
{
public DataType Type { get; set; }
public string DefaultValue { get; set; } // this is a serialized value
public List<OptionItemModel> Options { get; set; } // this only applies to DataType -> List
}
public class StructureModel
{
public List<FieldModel> Fields { get; set; }
}
So then you can have an ObservableCollection on your VM and your items can be created by a factory iterating for each one of the fields of the structure and so you can have custom Item View Models depending on the DataType of the field.
Then you can have a List on your View that uses a Template selector where you can create the Cell/Row depending on the DataType or the ItemViewModel and that would be it.
Then you can have a similar model structure to fill the values and upload them to the server.
Hope it's clear enough

Get the DisplayMember value of any object in ListBox in WPF

I have a ListBox, and it's items consist of custom class objects (can be any class).
Then I set the DisplayMemberPath so the ListBox shows the right property of that custom class fine.
Now I need to enumerate the Items list of ListBox, and get the DisplayMember value of each item in the list, without knowing the type of the class in the list. Is there any way to get this DisplayMember value without Reflection?
In WPF, you don't need to implement an interface, or a base class for a container control to read the value of a property. In an ideal world, it would make sense to declare a base class or interface and have all of your custom classes extend, or implement these, but the benefit of that is really to keep your data type safe.
For example, in WPF, this is perfectly legal and will work just the same:
public class RadioButtonData
{
public string Label { get; set; }
public bool IsSelected { get; set; }
}
public class CustomData
{
public string Label { get; set; }
public string Value { get; set; }
}
...
private ObservableCollection<object> objects = new ObservableCollection<object>();
public ObservableCollection<object> Objects
{
get { return objects; }
set { objects = value; NotifyPropertyChanged("Objects"); }
}
...
Objects.Add(new RadioButtonData() { Label = "Some Value" });
Objects.Add(new CustomData() { Label = "Another Value" });
...
<ListBox ItemsSource="{Binding Objects}" DisplayMemberPath="Label" />
So as long as your various classes have the same name of property, then they will all be displayed in the same way, like above. They don't even have to be of the same type... just as long as the name matches that used in the ListBox.DisplayMemberPath property.
UPDATE >>>
Ah sorry, I misunderstood your question. In the case that you want to access these property values in code, then you have four basic options:
Define an Interface with a particular property and make your custom classes implement it.
Declare a base class with a particular property and make your custom classes extend it.
Create a (potentially long) section of if else statements that checks the type of each object and then accesses the relevant property.
Use reflection.
In my personal opinion, I would recommend options 1 or 2 first, then 4 and lastly 3. I'm really not sure what you have against reflection, but it's really not that bad, or slow... I'd certainly prefer to use it rather than having an else if statement for every possible type used.

How to bind a TreeView Hierarchically by using enum values in WPF

Ok, so I am not sure this is even possible but I will ask the question here and hope to get an answer to it.
Suppose I have a list of a class, say Media items defined as follows
Enum MediaItemType{
Book,
CD,
VideoGame
}
public MediaItem{
public string Name { get; set; }
public MediaItemType { get; set; }
}
Now suppose I want to bind a list of MediaItems to a TreeView such that each MediaItem ends upp in a separate subtree depending on the media item type. Is that possible to do and if so, how do I do that?
In my Xaml-code I assume that I have defined the list as a property named MediaItems in the context.
<Grid>
<TreeView ItemsSource="{Binding Path=MediaItems}">
</Grid>
The tree view should be something like this
Book
- In to the wild
- Code Complete
CD
- Foo Fighters
- Bach
DVD
- X-men
- Casino Royale
Don't see any problem.
In model you have MediaItem, on model view you have to have MediaItemView type, something like this:
public class MediaItemView
{
public MediaItemType { get; set; }
public List<MediaItem> medialist;
}
Define bindings on that class, and define a Converter which will convert enum value to its string presentation.
I don't know about about a tree view, but you could achieve something similar to what you describe using a GroupDescription grouping on the MediaItemType property with a ListBox or ListView as described here

Is there attached property in C# itself?

In C# itself, is there something like "attached property" used in WPF?
The short answer is no. The slightly longer answer is that this is a bit of an unfortunate story. We designed "extension properties" for C# 4 and got as far as implementing (but not testing) them when we realized, oh, wait, the thing we designed is not really compatible with WPF-style properties. Rather than redesign and reimplement the feature we ended up cutting it.
The even longer version is here:
http://blogs.msdn.com/b/ericlippert/archive/2009/10/05/why-no-extension-properties.aspx
AttachedProperties are part of the .NET Framework, not part of the C# language specification, and specifically part of the System.Activities.Presentation.Model namespace, which is WPF specific.
In WPF, an attached property allows you to do something like:
<TextBlock Grid.Row="2" Text="I know nothing about grids!" />
This would be like having a class in C# defined as:
public class TextBlock
{
public string Text { get; set; }
}
And being able to do this:
var tb = new TextBlock();
tb.Grid.Row = 2; // this line would not compile
In order to make this work, you'd need to pass a Grid object into your TextBlock class:
public class TextBlock
{
public string Text { get; set; }
public Grid Grid { get; set; }
public TextBlock(Grid grid)
{
Grid = grid;
}
}
But I don't think there's anything directly equivalent to the way attached properties work in WPF. You'd need to build it by hand.
What are you trying to accomplish?
You can use the ConditionalWeakTable<TKey, TValue> class to attach arbitrary state to an instance. You can combine it with extension methods to create a form of extension properties, but unfortunately without using the nice property syntax in C#.
I think you're thinking of getters and setters.
They are created like this:
public class Person
{
//default constructor
public Person()
{
}
private string _Name;
public string Name
{
//set the person name
set { this._Name = value; }
//get the person name
get { return this._Name; }
}
}
More on how they work here:
http://msdn.microsoft.com/en-us/library/aa287786(v=vs.71).aspx

WPF ListView confusing the SelectedItem with equal items

A ListView (or ListBox) with following properties:
<ListView SelectionMode="Single">
<sys:String>James</sys:String>
<sys:String>Claude</sys:String>
<sys:String>Justing</sys:String>
<sys:String>James</sys:String>
</ListView>
will result in selecting two items simultaneously if I click on "James", even though
I chose SelectionMode="Single". It's even the same behaviour when I use a helper class with
a string-property to display in the ListView. It seems like the ListView is evaluating the Items and selecting those which are Equal() rather than ReferenceEqual(). Is there a way to change this behaviour so that the ListView treats every item individually?
The problem really is Equals() versus ReferenceEquals().
It's even the same behaviour when I use a helper class with a string-property to display in the ListView.
Not quite. You get the same behavior if you use an anonymous helper class.
Why doesn't wrapping an anonymous type around the string fix the problem? As described here, when you create an anonymous type, the compiler creates a generic Equals() method that returns true if the objects are of the same (anonymous) type and their properties have the same values.
The solution is to implement a real (non-anonymous) class - it can be as simple as this:
public class Item
{
public string Display { get; set; }
}
Object.Equals() does a reference comparison, so as long as you don't override it, you'll get the behavior you would expect.
I suspect that the design of the control assumes that you're not going using it to display lists containing two equal items.
As to why it uses Equals and not ReferenceEquals, I suspect that the underlying design choice has to do with the fact that controls have to be able to display value types. ReferenceEquals boxes value types, with the result that ReferenceEquals(1,1) returns false.
In order to get the desired behavior, you will need to create a helper class that wraps the string type like this:
public class Item
{
public Item(string name) { Name = name; }
public string Name { get; set; }
}
Using it, you can now do the following:
private ObservableCollection<Item> _items;
public MainWindow()
{
InitializeComponent();
_items = new ObservableCollection<Item>() { new Item("James"), new Item("John"), new Item("Steve"), new Item("Drew"), new Item("Andy"), new Item("James") };
list.ItemsSource = _items;
}
with the XAML like this:
<ListView SelectionMode="Single" x:Name="list" DisplayMemberPath="Name" />

Categories