Component Communication at Design Time - c#

I want to have a "form" or something else (no matter what) that can add string to a List
it would be done with a First Component "StringManager" witch contains the string collection
On the other hand i want to have another Component "ComponentReader" that use IExtenderProvider and add on All controls (on the component form) a property named "TheString" which let me choose in one of the string from List
So, to be clear : i want to share the List<String> with the minimum of code on each forms, (the most with properties editor)
I don't know how can i tell the "ComponentReader" where is the main component that he refers,
(i 've add a property ReferedStringManager in my "ComponentReader").
Is there any properties or instruction (or way)to inspect the project and his references to get all matchable value as List in "ComponentReader" properties
for the ReferedStringManager property of the ComponentReader;
If its not possible ,i think of Static List or something else, maybe XML file, (but i don't know how to manage that during conception )
Of course all of that is at Design Time, not at Execution Time (it would be so simpler !!)

this is a late answer, but if you're still interested, here's how to do it.
There are quite a few requirements for both your StringManager and your ComponentReader classes.
1) both classes need to derive from System.ComponentModel.Component.
2) The StringManager must override the Site property of the Component class. This is what makes it "VS Designer aware", and allows it to be selectable in your ComponentReader's properties later on.
3) The StringManager must expose the List as a public property. For convenience, in my sample code bolow, I have set the EditorAttribute for easy string collection edition in the VS property grid.
4) The ComponentReader must implement a custom interface with only one property of type StringManager. This is needed by the requirement #6.
5) The ComponentReader must have a public property of type StringManager
6) The ComponentReader must have a public property of type string for the SelectedString. The catch is that we must set the TypeConverterAttribute to yet another class that we must implenent (see #7)
7) We must implement a StringConverter derived class. This will be used to allow us to select a string from the selected StringManager. Within this class, we must be able to retrieve a reference to the ComponentReader through the interface created in #4.
Now, for the code:
StringManager.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;
using System.ComponentModel.Design;
namespace VSDesignHost
{
class StringManager : Component
{
private ContainerControl _containerControl = null;
public StringManager()
{
TheList = new List<string>();
}
public StringManager(IContainer container) : this()
{
container.Add(this);
InitializeComponent();
}
private void InitializeComponent()
{
//Whatever
}
public ContainerControl ContainerControl
{
get { return _containerControl; }
set { _containerControl = value; }
}
public override ISite Site
{
set
{
base.Site = value;
if (value != null)
{
IDesignerHost host = value.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (host != null)
{
IComponent rootComponent = host.RootComponent;
if (rootComponent is ContainerControl)
{
this.ContainerControl = (ContainerControl)rootComponent;
}
}
}
}
}
[Editor("System.Windows.Forms.Design.StringCollectionEditor, System.Design", "System.Drawing.Design.UITypeEditor, System.Drawing")]
public List<string> TheList { get; set; }
}
}
ComponentReader.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace VSDesignHost
{
class ComponentReader : Component, IStringManagerEnabled
{
private StringManager sm;
public ComponentReader()
{
sm = null;
}
[Browsable(true), Category("MyCategory")]
public StringManager StringManager
{
get { return sm; }
set
{
sm = value;
}
}
[Browsable(true), Category("MyCategory"), TypeConverter(typeof(StringManagerStringConverter))]
public string SelectedString { get; set; }
}
}
IStringManagerEnabled.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace VSDesignHost
{
interface IStringManagerEnabled
{
StringManager StringManager
{
get;
set;
}
}
}
StringManagerStringConverter.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace VSDesignHost
{
class StringManagerStringConverter : StringConverter
{
#region Make It A ComboBox
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return false;
}
#endregion
#region Display Tags In List
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
if ((context == null) || (context.Container == null))
{
return null;
}
Object[] Tags = this.GetTagsFromServer(context);
if (Tags != null)
{
return new StandardValuesCollection(Tags);
}
return null;
}
private object[] GetTagsFromServer(ITypeDescriptorContext context)
{
List<string> availableTags = new List<string>();
if (context.Instance == null)
{
availableTags.Add("ITypeDescriptorContext.Instance is null");
return availableTags.ToArray();
}
IStringManagerEnabled inst = context.Instance as IStringManagerEnabled;
if (inst == null)
{
availableTags.Add(context.Instance.ToString());
return availableTags.ToArray();
}
if (inst.StringManager == null)
{
availableTags.Add("No StringManager selected");
return availableTags.ToArray();
}
availableTags = inst.StringManager.TheList;
availableTags.Sort(Comparer<string>.Default);
return availableTags.ToArray();
}
#endregion
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 value.ToString();
return base.ConvertFrom(context, culture, value);
}
}
}
Create a Windows Forms project, add these files to it, and build the project.
Now, on you Form, you can add an instance of each the StringManager and ComponentReader by dragging from the toolbox.
Select the StringManager, and in the properties, add a few string to TheList by using the string collection editor.
Select the ComponentReader, and in the properties windows, you should be able to select your StringManager instance from the dropdown list.
You should now be able to select one of the string from the dropdown for SelectedString.
I hope you enjoy
Luc

Related

C# - Overriding the return type from interface with a derived type using generics

My goal is to have a dictionary which stores different Container objects that derive from a interface IContainer. The user can
add different Container objects (as long as they implement IContainer) to this dictionary. The containers can add elements related to the
container (eg configContainer will add configElements, diContainer will add diConfigElements).
The elements also implement from a interface.
I want to avoid the scenario of DiConfigElements being added to ConfigContainer. I have looked at related questions and they dont quite solve my problem.
I feel that generics will solve my problem, I have a example but I get Argument 2: cannot convert from 'ConfigContainer' to 'IContainer'
I am using Unity C#.
test.cs
using System.Collections;
using System.Collections.Generic;
public class test
{
public Dictionary<string, IContainer<IResolveableItem>> containers;
// Use this for initialization
void Start()
{
containers = new Dictionary<string, IContainer<IResolveableItem>>();
ConfigContainer configContainer = new ConfigContainer();
ConfigContainerElement configElement = new ConfigContainerElement();
configElement.Name = "configTest";
configElement.Path = "configTest/configTest";
configContainer.Add("test1", configElement);
containers.Add("config",configContainer);
}
}
IContainer.cs
using System.Collections;
public interface IContainer<T> where T : IResolveableItem
{
void Add(string key , T value);
}
ConfigContainer.cs
using System.Collections.Generic;
public class ConfigContainer : IContainer<ConfigContainerElement>
{
public Dictionary<string, IResolveableItem> container = new Dictionary<string, IResolveableItem>();
public void Add(string key, ConfigContainerElement value)
{
throw new System.NotImplementedException();
}
}
ConfigContainerElement.cs
using System.Collections;
public class ConfigContainerElement : IResolveableItem
{
protected string name;
protected string path;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public string Path
{
get
{
return path;
}
set
{
path = value;
}
}
}
IResolveableItem.cs
using System.Collections;
public interface IResolveableItem
{
string Name { get; set; }
string Path { get; set; }
}
It's a limitation of generics, will have to use runtime checking.

Issue adding component to IContainer

I have an IContainer called container and I'm trying to add a component to it using the add method. When adding it I get this error
Error 1 The best overloaded method match for
'System.ComponentModel.IContainer.Add(System.ComponentModel.IComponent,
string)' has some invalid
arguments C:\Users\Dan\Source\Workspaces\Bio-Catalysts
repack\Biocats_Repack\itas.s200.biocats.business\SOP\SOPOrderLineVwItems.cs 19 13 itas.s200.biocats.business
Heres the code:
namespace itas.S200.Biocats.Business.SOP
{
using System;
using System.Collections;
using System.ComponentModel;
using Sage.Common.Data;
using Sage.ObjectStore;
public class SOPOrderLineVwItems : PersistentSOPOrderLineVwItems
{
public SOPOrderLineVwItems()
{
}
public SOPOrderLineVwItems(System.ComponentModel.IContainer container)
{
container.Add(this, null);
}
//public SOPOrderLineVwItems(System.Data.IDataReader reader) :
// base(reader)
//{
//}
[System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)]
public new virtual SOPOrderLineVwItem this[int index]
{
get
{
return ((SOPOrderLineVwItem)(base[index]));
}
set
{
base[index] = value;
}
}
[System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)]
public override Sage.ObjectStore.PersistentObject Owner
{
get
{
if ((this.Query.Owner == null))
{
this.Query.Owner = new SOPOrderLineVwItem();
}
return this.Query.Owner;
}
}
[System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)]
public new virtual SOPOrderLineVwItem First
{
get
{
return ((SOPOrderLineVwItem)(base.First));
}
}
}
}
Can provide more code/info if required
Thanks
public class SOPOrderLineVwItems : PersistentSOPOrderLineVwItems, IComponent
IComponent Add() takes a IComponent as argument.
Add
and since you do this
container.Add(this, null);
you need this which is SOPOrderLineVwItems to be an IComponent

CsvHelper ConvertUsing not changing output

I'm trying to use the ConvertUsing method of the CsvHelper library (v 2.4.0).
I've read the documentation about ConvertUsing but can't get it to work.
I'm using a simple class:
public class Test
{
public long Id { get; set; }
public string Title { get; set; }
}
With this ClassMap:
public class TestClassMap : CsvClassMap<Test>
{
public override void CreateMap()
{
Map(m => m.Id).Name("id").ConvertUsing(row => 11111);
Map(m => m.Title).Name("title").ConvertUsing(row => row.GetField("title") + " 123");
}
}
My code which uses these creates an instance of the class and then writes it to CSV:
var test = new Test() { Id = 99, Title = "Test title" };
using (var streamWriter = new StreamWriter("test.csv"))
{
var csv = new CsvWriter(streamWriter);
csv.Configuration.RegisterClassMap<TestClassMap>();
csv.WriteRecord(test);
}
However the output file test.csv is always the following format:
id,title
99,Test title
The output I'm looking for is:
id,title
11111,Test title 123
And the ConvertUsing is being ignored. I've tried only converting the Id, and only the Title, but this doesn't work either.
Any ideas where I'm going wrong?
Currently ConvertUsing is only used when reading.
You can use a custom type converter if you want to customize the output. You also have some limited abilities through the type converter options.
I had a similar need and this is what I made in order to modify the content before saving it into a csv file.
I have a custom class called StringNormalizer that implements CsvHelper.TypeConversion.ITypeConverter interface.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CsvHelper.TypeConversion;
namespace MyNamespaceInHere {
public class StringNormalizer : ITypeConverter {
public bool CanConvertFrom(Type type) {
if (type == typeof(string)) return true;
return false;
}
public bool CanConvertTo(Type type) {
if (type == typeof(string)) return true;
return false;
}
public object ConvertFromString(TypeConverterOptions options, string text) { return normalize(text); }
public string ConvertToString(TypeConverterOptions options, object value) {
if (value == null) return string.Empty;
if (value.GetType() == typeof(string)) {
string str = (string)value;
return normalize(str);
}
return string.Empty;
}
public string normalize(string field) {
// Do stuff in here and return normalized string
return field + " just a sample";
}
}
}
Then in my main program where I have defined mappings I use it like this
public sealed class ConMap : CsvClassMap<Contact> {
public override void CreateMap() {
Map(m => m.FirstName).Name("FirstName").TypeConverter<StringNormalizer>();
}
}
And thus all that is saved to csv are "run through" my string normalizer.

Ninject factory not working with conventions for me

I am trying to use the method of binding located here but having no luck
https://github.com/ninject/ninject.extensions.factory/wiki/Factory-interface
https://github.com/ninject/ninject.extensions.factory/wiki/Factory-interface%3A-Referencing-Named-Bindings
Keep in mind I am not trying to do it this way:https://gist.github.com/akimboyko/4593576
Rather I am trying to use the convention GetMercedes() to mean
I am basically trying to achieve this:https://gist.github.com/akimboyko/4593576 with conventions specified in the above examples.
using Ninject;
using Ninject.Extensions.Factory;
using Ninject.Modules;
using Ninject.Parameters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Test.NinjectFactory
{
public class Program
{
public static void Main()
{
using (var kernel = new StandardKernel(new CarModule()))
{
var factory = kernel.Get<ICarFactory>();
var mercedes =factory.GetMercedes();
int i = 1;
}
}
public interface ICar
{
void Drive();
}
public class Mercedes : ICar
{
readonly ICarFactory carFactory;
public Mercedes(ICarFactory carFactory)
{
this.carFactory = carFactory;
}
public void Drive()
{
var Mercedes = this.carFactory.GetMercedes();
}
}
public interface ICarFactory
{
ICar GetMercedes();
}
public class CarModule : NinjectModule
{
public override void Load()
{
//https://github.com/ninject/ninject.extensions.factory/wiki/Factory-interface%3A-Referencing-Named-Bindings
Kernel.Bind<ICarFactory>().ToFactory();
Bind<ICar>().To<Mercedes>().NamedLikeFactoryMethod<ICarFactory>(x => x.GetMercedes());//doesnt work for me
}
}
}
}
I'm posting this as an answer because it is most likely the cause.
The factory extensions use prefixed Get methods as a standard. You'll run into issues by calling any of your factory methods with prefixed Get and using NamedLikeFactoryMethod. For example, GetFord, GetMercedes, GetNissan. You'll notice that, in the example at the link you provided, the function is called CreateMercedes.
Change your function name to CreateMercedes or anything that doesn't start with Get and it should be fine.
I found the anwswer here:
https://gist.github.com/akimboyko/5338320
It seems you need a function to take care of the binding
public class BaseTypeBindingGenerator<InterfaceType> : IBindingGenerator
{
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
{
if (type != null && !type.IsAbstract && type.IsClass && typeof(InterfaceType).IsAssignableFrom(type))
{
string.Format("Binds '{0}' to '{1}' as '{2}", type, type.Name, typeof(InterfaceType)).Dump();
yield return bindingRoot.Bind(typeof(InterfaceType))
.To(type)
.Named(type.Name) as IBindingWhenInNamedWithOrOnSyntax<object>;
}
}
}

Markup extension in XAML for binding to ISubject<string>

If I have the following view model
class Foo : INotifyPropertyChanged {
ISubject<string> Name { ... }
}
and some imagined XAML code
<TextBox Text="{my:Subscribe Path=Name}/>
I wish the two way binding to behave that
Subject.onNext is called when the text box is updated in the UI
the text box is updated by subscribing to the Subject.Subscribe
As WPF only supports INPC directly my idea is to create a proxy INPC object
in via a markup extension
class WPFSubjectProxy : INotifyPropertyChanged{
string Value { ... }
}
The proxy would be wired up to the subject as so
subject.Subscribe(v=>proxy.Value=v);
proxy
.WhenAny(p=>p.Value, p.Value)
.Subscribe(v=>subject.OnNext(v))
Note WhenAny is a ReactiveUI helper for subscribing to
INPC events.
But then I would need to generate a binding and return
that via the markup extension.
I know what I want to do but can't figure out the
Markup extension magic to put it all together.
It's hard to say without seeing specifically what you're struggling with, but perhaps this helps?
EDIT
The solution I (bradgonesurfing) came up with is below thanks to the pointer in the
assigned correct answer.
    Nodes
     
and the implementing code. It has a dependency on ReactiveUI and a helper function in a private library for binding ISubject to a mutable property on an INPC supporting object
using ReactiveUI.Subjects;
using System;
using System.Linq;
using System.Reactive.Subjects;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace ReactiveUI.Markup
{
[MarkupExtensionReturnType(typeof(BindingExpression))]
public class SubscriptionExtension : MarkupExtension
{
[ConstructorArgument("path")]
public PropertyPath Path { get; set; }
public SubscriptionExtension() { }
public SubscriptionExtension(PropertyPath path)
{
Path = path;
}
class Proxy : ReactiveObject
{
string _Value;
public string Value
{
get { return _Value; }
set { this.RaiseAndSetIfChanged(value); }
}
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var pvt = serviceProvider as IProvideValueTarget;
if (pvt == null)
{
return null;
}
var frameworkElement = pvt.TargetObject as FrameworkElement;
if (frameworkElement == null)
{
return this;
}
object propValue = GetProperty(frameworkElement.DataContext, Path.Path);
var subject = propValue as ISubject<string>;
var proxy = new Proxy();
Binding binding = new Binding()
{
Source = proxy,
Path = new System.Windows.PropertyPath("Value")
};
// Bind the subject to the property via a helper ( in private library )
var subscription = subject.ToMutableProperty(proxy, x => x.Value);
// Make sure we don't leak subscriptions
frameworkElement.Unloaded += (e,v) => subscription.Dispose();
return binding.ProvideValue(serviceProvider);
}
private static object GetProperty(object context, string propPath)
{
object propValue = propPath
.Split('.')
.Aggregate(context, (value, name)
=> value.GetType()
.GetProperty(name)
.GetValue(value, null));
return propValue;
}
}
}

Categories

Resources