(update) ICustomTypeDescriptor works for my Windows Forms app, but not for Silverlight; Not supported. I will keep investigating this idea though and see where i get to.
(/update)
I have, say a few switch panels (for those that like analogies).
Each of these switch panels has switches that have a Name(string) can be in state(bool) of On or Off.
The switchpanel and switches are objects that have INotify interface on them.
Using the switches Names, I create a list of all possible switch names over the collection and create a dynamic class that has all these Names as properties.
SwitchPanel1 (Switches( Switch1 ("Main",On) , Switch2("Slave",Off)))
SwitchPanel2 (Switches( Switch1 ("Bilge",On) , Switch2("Main",Off)))
Produces a collection of
(Main,Bilge,Slave)
And a dynamic class is produced that has the properties:
SwitchPanel : (SwitchPanel)
Main : (Switch)
Bilge : (Switch)
Slave: (Switch)
The idea is that if the switch panel has a switch with the Name of the property, it is placed on that property. So using a bit of linq
propeties["Main"].SetValue(newSwitchType,SwitchPanel.Switches.FirstOrDefault(sw => sw.Name == "Main"));
I want to cast this new dynamic class to INotfyPropertyChanged AND catch the actual changes on these new properties, so if a switch changes state the dynamic object will report it.
Why? It needs to be displayed in a list view and the list view I'm using has its binding by supplying the Property name, and not the binding path.
It also attempts to catch INotify events by casting the object against INotifyPropertyChanged. This means it will sort and/or group when things change.
If you know of a better way to do this let me know. Please.
You probably don't need a dynamic class. You can implement runtime binding properties via ICustomTypeDescriptor / GetProperties(), creating your own PropertyDescriptor implementation that returns the named switch. It isn't clear what knows first about the change, but you could either use INotifyPropertyChanged, or the older property-specific change event, again tied to each property (so each PropertyDescriptor attaches to, for example, the event in the named switch.
Not trivial, but not impossible either.
Related
I have a WPF treeview that displays multiple nested classes based on hierarchical datatemplates. It uses the classes here: https://complexdatatemplates.codeplex.com/. This question applies to DataGrids also, though or any control who's selected item is an anonymous class.
What I want to do seems simple. I want to select an item, then press a button and operate directly on the selected item--not the SelectedItem property of the tree view, but the object behind it. But, since the compiler doesn't know the class of the selected item until runtime, it understandably won't give me access to any of the methods or properties in it.
The class in this case is 'Roms', the treeview is 'DB_tree'. I can create a new object from the selected item, and cast it as Roms, then do whatever I want. Like this
roms = (Roms)DB_tree.SelectedItem;
But I can't figure out how to operate on the actual object that would be returned by DB_tree.SelectedItem.
Well, it turns out that by assigning (Roms)DB_tree.SelectedItem to another object
DummyObject = (Roms)DB_tree.SelectedItem
Then everything I do to DummyObject is done to the original object. The equals operator for Objects acts more like assigning an alias than an equals operator.
Strange, that, though clearly common sense to OO programmers everywhere. If the equals operator worked the same way for doubles or ints it would be impossible to do math, now that I understand it, it is cleaning up a lot of my code.
I'm trying to build a check list for a ToolStripMenuItem that automatically handles the checking and unchecking of an item and then I provide an event to the programmer allowing them to handle what happens next. If something like this already exists, I would LOVE to know where it is. I've created the collection editor for my custom ToolStripMenuItem and I can add check lists to this collection of checklists. My problem is you create the collection editor like this:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
Editor(typeof(ToolStripItemExtCollectionEditor), typeof(UITypeEditor))]
I need to be able to pass this ToolStripMenuItem's DropDownitems to this collection editor so when you add a new checklist and click on the items property of the checklist you can add/remove any one of the known ToolStripMenuItems to/from the checklist. Passing a reference won't work since all of this is happening inside an attribute and I wouldn't know where to begin if the answer is reflection.
This answer applies to VB.NET. I plan on turning this into C# for a DLL, but for now it's in vb.net because that's where I started this idea from and the language the project is in.
Here's what I have so far:
ToolStripMenuItemExt
Purpose: My custom ToolStripMenuItem.
ToolStripMenuItemExt has a CheckListSheet which contains a reference to ToolStripMenuItemExt's DropDownItems (I passed in dropdownitems byref and not byval). It has one property that returns the CheckLists object in CheckListSheet.
CheckListSheet
Purpose: Maintains a reference to the collection I'm observing through an observable collection type and an object of the collection I return in ToolStripMenuItemExt.
CheckListSheet has the CheckLists object. The dropdownitems I pass in byref are stored in an ObservableToolStripItemCollection which hopefully when I get to testing it allows me to update the collection of checklists easier since it inherits ObservableCollection(of ToolStripItemCollection). This class also has a shared function that returns the observable collection which has a scope identifier of private shared.
CheckLists
Purpose: The CollectionBase type that stores CheckList objects.
CheckList
Purpose: Stores the ToolStripItemCollection whose objects act as a single item checked checklist (only one item is checked at a time).
This has some properties for the designer and the collection for the check list. Eventually I'll add in the logic to check and automatically uncheck and raise an event for it.
MenuItemCheckListCollectionEditor
Purpose: Allows a collection of known and instantiated ToolStripItem objects to be displayed and added to a CheckList.
Right now it demands I give it a Type or array of Types so it can establish itself what type of CollectionEditor it is. I haven't be able to show a drop down of types or a drop down of ToolStripItem objects. Any class having ToolStrip in their name inherits ToolStripItem which is why I use this type of object.
If ANYONE has any advice on my current answer or can forecast any foreseeable pitfalls please share. I don't care if you talk in c# or vb.net. Maybe I just need to stop and turn this into c# code. Maybe this is impossible. I am making progress though. What would be extremely helpful is figuring out how MenuStrip's collection editor is able to populate a dropdown of ToolStripItems
[Update]
A collection Editor requires you to provide a type for it to display. This type has to inherit CollectionBase which means at design time there's no way for it to reference the dropdownitems. :sigh:
I have a network service that provides a number of key-value pairs. The collection is huge and the available keys are changing over time. My goal is to be able to bind to this collection in either Visual Studio 2013 or Blend using the properties window in design mode (or intellisense in XAML). This of course requires the service client object to somehow be initialized during design mode to connect to the service and expose the keys as bindable properties.
I have been searching a lot about "ObjectDataSource" but my problem is that I cannot dynamically change the properties of this object.
What type of object should I choose as my data context?
How can I populate it during design mode?
Any ideas or suggestions towards how this could be implemented would be great.
You can set your data context to any object - you get dynamic updates if
the object implements INotifyPropertyChanged (http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx)
or is observable (like ObservableCollection<> (http://msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx)).
If your goal is to create an observable dictionary, have a look at this post: http://blogs.microsoft.co.il/shimmy/2010/12/26/observabledictionarylttkey-tvaluegt-c/
The object data provider can have it's data source set to any object too - the dynamic updates will occur if the object is as stated above. In addition, you can declaratively set the objectDataSource's values in xaml - but this doesn't appear to be something you want to do.
Note: You don't change the properties of the object data source - you change the object that it is wrapping for you.
If you want to be able to control the selected item of something like a list (a bit like the Winforms BindingSource)- take a look at ICollectionView (http://msdn.microsoft.com/en-us/library/system.componentmodel.icollectionview(v=vs.110).aspx) which can be created by wrapping up your actual data source (same observable rules apply to get dynamic updates) using CollectionView (see http://msdn.microsoft.com/en-us/library/system.windows.data.collectionviewsource.getdefaultview(v=vs.110).aspx)
So - to answer the rest of your question, one solution is to create an observable collection of observable objects (wrapping your key value pairs) and set that as your data context (or set it as your objectDataProvider's source, and bind your Xaml accordingly. This isn't the only way, but a quick google will show you others.
Edit Design mode
For design time data - look at using the design mode flags - an example is here: http://www.arrangeactassert.com/how-to-use-designdata-to-populate-controls-with-design-time-data-in-silverlight-and-wpf/
A list of (Silverlight design time attributes): http://msdn.microsoft.com/en-us/library/ff602277%28v=vs.95%29.aspx
I've never actually tried the design time stuff, so that's all I got!
Hth.
I am new to the MVVM-pattern, and am trying out Caliburn.Micro on a project.
I want to have one ViewModel (which contains a collection of ViewModels) shared by multiple Views, where each view only displays items which have a certain value on one of it's properties.
To get specific, I am using a service which allows me to monitor different values that update frequently. I then get an object of type MonitoredItem, which contains a property of type DataValue, which in turn contains an object for the Value and a property for the value's datatype.
So far I have a MonitoredItemViewModel which uses this service's MonitoredItem class as it's model, and a MonitoredItemsViewModel which contains BindableCollection<MonitoredItemViewModel> MonitoredItems, and commands for adding/removing items.
I also have a MonitoredItemsView where I can see all the items I am currently monitoring.
How do I go about splitting up the view, so that I can have all MonitoredItems where DataValue is an integer/float/double displayed in one area in my window, boolean values displayed somewhere else etc?
Don't do it in the view, instead expose different collections on your ViewModels according to what you need to filter.
This can be done either as known collections, e.g.
public ObservableCollection<MonitoredItemViewModel> ItemsWhereFooIsBar ...
public ObservableCollection<MonitoredItemViewModel> ItemsWhereFooIsntBar ...
or you could do it more generically to return filtered collections on demand
public ObservableCollection<MonitoredItemViewModel> GetItems(Func<DataValue, bool> matches)
{
//Filter collection with
return ... allItems.Where(x=>matches(x))... ;
}
and call via
GetItems(x=>x.Foo == Bar)
The problem you are going to have is when the items change and should switch from collection to collection. If you were using ReactiveUI this would be incredibly easy as you can use Rx to trigger its built in item tracking and also use its .CreateDerivedCollection(...) to build the new collections automatically (hint, hint :-))
If not then you have a few choices.
You can derive a class from ObservableCollection so that as well as being notified via CollectionChanged when new items are added or removed, or also get notified when the properties of the items change as well.
Or you could make your ItemViewModel immutable so that its properties never change, but instead you drop the old item and add an updated one into the correct collection.
Context
I've been working on a custom collection editor / designer for a custom ASP.Net web control. The web control exposes a strange hierarchy, so a custom editor seemed like the right thing to do to make it easier for developers.
Building ASPX code and using the web control works. In other words, things like PersistChildren and ParseChildren are taken care of.
The signature of the property in the web control looks something like this:
[PersistenceMode(PersistenceMode.InnerProperty)]
[Themeable(false)]
[Browsable(false)]
public virtual DimensionsCollection Dimensions { get; internal set; }
Note that the property is not public; if it were public, all kinds of things in the designer will go wrong. DimensionsCollection is a class that simply inherits List<Dimension>. The Dimension class itself is nothing fancy, just a thing with some properties.
Just because I think it looks cool, I want to be able to modify the property from an action in the designer. To do that, I implemented a ControlDesigner class and added an ActionList. One of the actions there is a linkbutton that opens an editor:
var editor = new Editors.DimensionEditor(control.Dimensions);
if (editor.ShowDialog() == DialogResult.OK)
{ /* SEE BELOW */ }
The editor itself is a windows form that takes a List<Dimension> as constructor argument and modifies the collection.
Problem
When I use this code, I can see that the editor works and that the control collection is updated in the 'designer' view. If I open the editor multiple times, the state changes, meaning that somewhere in memory the state is updated by the editor.
However, if I go to the ASPX code, I can see that the Dimensions are not there anymore. So, the problem in a nutshell is that I somehow have to tell Visual Studio to write/serialize/persist the property to the ASPX file. (simple as that...)
Strangely, I cannot find anywhere how to do this... even though a normal CollectionEditor seems to be capable of doing just that (which I cannot subclass unfortunately)
Some things I tried
For other properties I noticed you have to use something like this, but this doesn't seem to work. Code was entered at the point marked as 'see below' or in some cases to a helper call in the designer called from that point:
PropertyDescriptor pd = TypeDescriptor.GetProperties(base.Component)["Dimensions"];
// use setter with internal property -> no effect
// this.OnComponentChanged(this, new ComponentChangedEventArgs(this.Component, pd, null, newdim)); -> no effect
// use getter to obtain list -> populate that using another list that's created in the editor
I can understand why it doesn't work; apparently someone has to tell Visual Studio that the property has changed... I just don't know how to do just that.
This was really a pain to figure out with apparently no sources online that explain how to do this.
Basically you want to use the OnComponentChanging / Changed methods to notify the designer. And apparently the designer uses transactions for the rest of the logic. (My guess is that it has to do with undo/redo behavior). For a normal type this is done automatically when you use the PropertyDescriptor, for collections it apparently doesn't wrap the collection which means you have to do it manually.
To solve the issue, you need to create a small method like this in either the UITypeEditor or in the DesignerActionList class your implementing:
private void ChangeAction(List<Dimension> newDimensions)
{
IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost;
PropertyDescriptor pd = TypeDescriptor.GetProperties(typeof(MyControl))["Dimensions"];
var dimensions = (DimensionsCollection)pd.GetValue(control);
var trans = host.CreateTransaction();
IComponentChangeService ccs = (IComponentChangeService)GetService(typeof(IComponentChangeService));
ccs.OnComponentChanging(control, pd);
dimensions.Clear();
dimensions.AddRange(newDimensions);
ccs.OnComponentChanged(control, pd, null, dimensions);
trans.Commit();
}
If you're implementing a UITypeEditor, make sure to use context.Instance from EditValue as the control and the given provider to lookup the services.