|
Nice. Couldn't make the leap from (illegal) generic event with unbounded type to static generic inner class. You really do rock more
Have you used this technique before?
|
|
|
|
|
I think the main disadvantage of my technique is that its forced to be static (I can't see an elegant workaround for this in any case). Kinda precludes it from being a component in an IOC scenario. I think my preferred compromise would be just providing a eventerInstance.Subscribe< T>(EventHandler< TEventArgs>> handler) pattern. Pity you can't do generic operator overloading - that would allow the same syntax as delegates.
I wouldn't use this in real code. Closest I've come for simpler things is the command handling and actions in the Composite Application Block, which is mainly for dispatching simple GUI events. If I wanted some serious pub/sub I'd probably end up needing finer differentiation than just event types - so I'd probably be looking at a service bus to handle it.
|
|
|
|
|
Few, perhaps dumb, questions about your idea:
DaveyM69 wrote: any type to be passed in event args
Are the types known to the singleton class or can it be any type, even a class that the singleton isn't aware of?
If the type is known to the singleton, wouldn't this lead to hardcoding the possible types to the singleton implementation thus leading to several different event declarations and subsrciptions (based on type). I understood that this is what you're trying to prevent.
If the type can be unknown, what is it's role in event firing in singleton? Should only those methods be invoked that have subscribed this event with a certain type or is the logic for the type fully in the code that has subscribed the event. If it is the latter, wouldn't it be sufficient to carry the type info in custom eventargs without generics.
|
|
|
|
|
Hi Mika, not dumb questions at all!
Mika Wendelius wrote: can it be any type, even a class that the singleton isn't aware of?
Ideally yes, but using a Singleton, I couldn't find a way of making it work.
Mika Wendelius wrote: If the type is known to the singleton, wouldn't this lead to hardcoding the possible types... this is what you're trying to prevent.
Correct, and correct - hence the question.
Mika Wendelius wrote: wouldn't it be sufficient to carry the type info in custom eventargs without generics
Yes, but I'd have to box/unbox to and from object. I thought it's be nice (if possible) to do it with generics. As you can probably guess, I've not needed/used generics much apart from List<T> so I'm learning as I go a little trying to get this concept to work
I think the only way to do this is to have seperate static instances of each type, instead of one singleton.
To explain more clearly, here's some working code. I've used a winforms app where two forms receive and send notifications. I've used the static Program class to create static instances of Notifier<T> (instead of a singleton) that the notifiction can be done through application wide. Sorry for the long post.
Edit: The code below is improved by creating a GlobalNotifier singleton, and adding the Notifier<T> instance fields and properties to there (instead of Program class) as needed. Still gotta hard code the types somewhere though
using System;
public class TEventArgs<T> : EventArgs
{
private T m_Value;
public TEventArgs(T value)
{
m_Value = value;
}
public T Value
{
get { return m_Value; }
}
}
using System;
public class Notifier<T>
{
public event EventHandler<TEventArgs<T>> Notification;
public void Notify(T value)
{
OnNotify(new TEventArgs<T>(value));
}
protected virtual void OnNotify(TEventArgs<T> e)
{
EventHandler<TEventArgs<T>> eh = Notification;
if (eh != null)
eh(this, e);
}
}
using System;
using System.Windows.Forms;
static class Program
{
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private static Notifier<int> intNotifier = new Notifier<int>();
private static Notifier<string> stringNotifier = new Notifier<string>();
public static Notifier<int> IntNotifier
{
get { return intNotifier; }
}
public static Notifier<string> StringNotifier
{
get { return stringNotifier; }
}
}
using System;
using System.Windows.Forms;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Form2 frm2 = new Form2();
Program.IntNotifier.Notification += new EventHandler<TEventArgs<int>>(IntNotifier_Notification);
Program.StringNotifier.Notification += new EventHandler<TEventArgs<string>>(StringNotifier_Notification);
Program.IntNotifier.Notify(5);
Program.StringNotifier.Notify("This rocks!");
}
void IntNotifier_Notification(object sender, TEventArgs<int> e)
{
Console.WriteLine(e.Value);
}
void StringNotifier_Notification(object sender, TEventArgs<string> e)
{
Console.WriteLine(e.Value);
}
}
using System;
using System.Windows.Forms;
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
Program.IntNotifier.Notification += new EventHandler<TEventArgs<int>>(IntNotifier_Notification);
}
void IntNotifier_Notification(object sender, TEventArgs<int> e)
{
Program.StringNotifier.Notify("Got the integer " + e.Value);
}
}
DaveBTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)Visual Basic is not used by normal people so we're not covering it here. (Uncyclopedia)
modified on Tuesday, January 13, 2009 5:13 AM
|
|
|
|
|
I'm really loosing some hair over here... Can't figure out how to do this fully type safe without boxing. However, I changed the concept a little bit. Instead of using events, I decided to declare a delegate and use a subscribe method. So far I've done the following. In your opinion, are we getting any further?
So far I think the singleton isn't aware of the type anymore. The subsriber is and it subscribes an 'event' based on a type. Also when the 'event' is raised using Notify, the instance is defined for the type. This could be taken furher by adding a reason for an event and that could be delivered to the subscriber or the subscriber could define that it want's only certain event reasons.
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
int testVariable = 23;
Singleton a = Singleton.Instance;
Tester b = new Tester();
a.Notify(testVariable);
...
}
public delegate void NotifyDelegate<T>(T instance);
public class Tester {
public Tester() {
Singleton.Instance.Subscribe<int>(this.MethodToCall);
}
public void MethodToCall(int value) {
System.Windows.Forms.MessageBox.Show(value.ToString());
}
}
public class Singleton {
private class Subscriber {
private System.Type _type;
private object _notifyDelegate;
internal Subscriber(System.Type type, object notifyDelegate) {
this._type = type;
this._notifyDelegate = notifyDelegate;
}
internal System.Type Type {
get {
return this._type;
}
}
internal NotifyDelegate<T> GetNotifyDelegate<t>() {
return (NotifyDelegate<T> )this._notifyDelegate;
}
}
private static Singleton _instance;
private System.Collections.Generic.List<Subscriber> _subsribers = new List<Subscriber>();
private Singleton() {
_instance = this;
}
public void Subscribe<T>(NotifyDelegate<T> delegateToAdd) {
this._subsribers.Add(new Subscriber(typeof(T), delegateToAdd));
}
public void Notify<T>(T instance) {
foreach (Subscriber item in this._subsribers) {
if (item.Type.Equals(typeof(T))) {
item.GetNotifyDelegate<T>()(instance);
}
}
}
public static Singleton Instance {
get {
if (_instance == null) {
_instance = new Singleton();
}
return _instance;
}
}
}</t>
|
|
|
|
|
Having a busy morning here - will try and look this afternoon or this evening.
Just done a copy and paste so far and the compiler's complaining about the T in:
internal NotifyDelegate<T> GetNotifyDelegate()
Mika Wendelius wrote: I'm really loosing some hair over here
Too late for me... it's already gone!
DaveBTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)Visual Basic is not used by normal people so we're not covering it here. (Uncyclopedia)
|
|
|
|
|
I tried to double check the code, but still the formatting removed parts if the generic definitions. At first it replaced them with emoticons.... The method should be:
internal NotifyDelegate<T > GetNotifyDelegate<T > () {
return (NotifyDelegate<T > )this._notifyDelegate;
}
There are few extra spaces after T and before parenthesis, but if I don't add the the result is
Hopefully it compiles now.
The need to optimize rises from a bad design.My articles[^]
|
|
|
|
|
Yeah, that compiles fine
It works too! Nice one. I've only scanned it very quickly. I will look in depth tonight to figure out exactly what you're doing and post back.
Thanks for your help Mika. Interesting problem isn't it?
DaveBTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)Visual Basic is not used by normal people so we're not covering it here. (Uncyclopedia)
|
|
|
|
|
It's a problem but I'm not sure if interesting is the correct word
Just kidding, you're right, it is. I'm trying to figure when and how to use this in different scenarios in real life. Obviously what we're trying to do isn't suitable for every case, but I believe it would have it's place. Most likely in UI scenarios, but I can see some scenarios also in business logic layer even if it resides in separate middle-tier.
|
|
|
|
|
Had a good look at the code you posted. Very good! I've extended it a little so the delegate signature is now (object sender, NotifyEventArgs<T> e) so it's more like a normal event. Only problem with this at the moment is the object calling the Notify method needs to send this like it would in a normal protected virtual void OnEvent method. I might have a look to see if there's some 'magic' way to determine what class called the method.
By using NotifyEventArgs<T> : EventArgs it's easy to extend it so a Reason enum or whatever can be incorporated. I've given it a Value property who's type is set by T so any type can be passed still.
I've still got a bit of work to do - an Unsubscribe method at least, but it's been very helpful and insightful
I might do an article on this. If I do, I'll pass it by you first as I think this should be a co-author thing with the quality of your help/input.
DaveBTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)Visual Basic is not used by normal people so we're not covering it here. (Uncyclopedia)
|
|
|
|
|
DaveyM69 wrote: I've extended it a little so the delegate signature is now (object sender, NotifyEventArgs<t> e)
Yep, I thought of the same, but then I was thinking maybe there should be some other base class for the arguments. For example if we add reason and other enums they could reside in the base class and then the application could extend that to whatever it wants to deliver, complex types, arrays etc.
DaveyM69 wrote: I might have a look to see if there's some 'magic' way to determine what class called the method
I'd be very interested if you find a solution. I've been looking for a way and not found anything reasonable so I implemented a bit different strategy. The only thing I found when investigating was StackFrame, but it had too severe performance penalty. However, it was a long time ago and based on framework 1.1
DaveyM69 wrote: Unsubscribe method at least
That would be handy in most cases along with few other methods
DaveyM69 wrote: I'll pass it by you first as I think this should be a co-author thing with the quality of your help/input
I'd be happy to participate (just brief me how it happens, since never done that before). I must say that the logic for the singleton class isn't what I would do. There's a lot of things to correct, but at the time I was writing it, it was very late in the evening so I decided to do it brute-force.
|
|
|
|
|
Mika Wendelius wrote: I'd be very interested if you find a solution
There doesn't appear to be a way to get the instance that called the method. I can get the Type easily by using the StackTrace but not the actual instance. I'll keep investigating and ask a new question here, but in the meantime I've just overloaded the Notify method so the instance can be passed, and if not the Type is used.
DaveBTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)Visual Basic is not used by normal people so we're not covering it here. (Uncyclopedia)
|
|
|
|
|
I'd say the workaround you used may be sufficient for most of the cases. Do you think there's something in the implementation I could help you with?
|
|
|
|
|
Well there's the base class you mentioned earlier. So far I've just done this.
public enum NotifyReasons
{
Unspecified = 0,
Created,
Updated,
Removed
}
public class NotifyArgs<T>
{
#region Fields
private T m_Value;
private NotifyReasons m_Reason;
#endregion
#region Constructors
public NotifyArgs(T value)
{
m_Value = value;
}
public NotifyArgs(T value, NotifyReasons reason)
{
m_Value = value;
m_Reason = reason;
}
#endregion
#region Properties
public T Value
{
get { return m_Value; }
}
public NotifyReasons Reason
{
get { return m_Reason; }
}
#endregion
}
which is created by this method (all the overloads call this method)
private void Notify<T>(object source, NotifyArgs<T> a)
{
foreach (Subscriber item in m_Subsribers)
if (item.Type.Equals(typeof(T)))
item.GetNotifyDelegate<T>()(source, a);
}
I haven't looked at the best way to implement this so it can be extended by the user and still passed through the notifier...
DaveBTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)Visual Basic is not used by normal people so we're not covering it here. (Uncyclopedia)
|
|
|
|
|
Sorry for the delay. I was going to answer you sooner, but I fell while the putting children to sleep
One change I had in mind is that the list base operation is going to be slow. So if we're targeting for 3.5 I'd suggest that we use this kind of approach for notifying the subscribers.
private System.Collections.Generic.HashSet< Subscriber > _subsribers = new HashSet<subscriber>();
...
public void Notify< T >(T instance) {
var subscribers = from item in this._subsribers
where item.Type.Equals(typeof(T))
select item;
foreach (Subscriber subscriber in subscribers) {
subscriber.GetNotifyDelegate< T >()(instance);
}
}</subscriber>
That's based on my version of the singleton so the Notify signature is different from yours but the contents should quite same.
What do you think if we would use another way to communicate? I'm afraid we'll flood the forum at certain point and it's also a bit difficult to change code like this. Perhaps using email so we could transfer the entire projects?
|
|
|
|
|
Mika Wendelius wrote: Perhaps using email so we could transfer the entire projects
I agree.
DaveBTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)Visual Basic is not used by normal people so we're not covering it here. (Uncyclopedia)
modified on Friday, January 16, 2009 7:57 AM
|
|
|
|
|
Okay, I got the address so you can remove it from the post. I'll send you a message in the evening.
|
|
|
|
|
I'm developing a drawing app and I'm using a UserControl as a basis to paint on. Everything is working fine but I'm wanting to keep the current view centered when a ZoomIn/ZoomOut operation occurs. For instance, if the user is working on a Rectangle and wants to zoom in closer, he would want the rectangle to stay in focus instead of scaling out of bounds. In my initial test, I can't figure out how microsoft calculates the position of the scrollbar handle and therefore I can't perform any centering algorithms. I don't want to center the whole document, just the area where the user is currently scrolled to.
Any help would be greatly appreciated!!
Richard
My code this week has no errors. But it's Monday morning and I haven't got out of bed.
|
|
|
|
|
Hi Richard,
I haven't used it myself yet, but it seems to me the documentation on ScrollableControl.AutoScrollPosition explains rather well how scrolling is supposed to work.
This is what I would try:
assume control size is (2w,2h) and current autoscrollposition is (asx,asy)
center point is (w,h) relative to the topleft corner of the visible control, hence (asx+w,asy+h) logically;
when the zoom factor changes by a factor of zf (zoomnew/zoomold), new logical position of center point would be (zf*(asxnew+w),zf*(asynew+h)), but relative position should remain (w,h), hence:
asxnew=(asx+w)/zf-w
asynew=(asy+h)/zf-h
obviously boundary tests would be necessary to prevent "overscrolling" (showing stuff that lies outside
the drawing) if possible.
I hope I got this right and made this somewhat clear...
Luc Pattyn [Forum Guidelines] [My Articles]
I use ListBoxes for line-oriented text, and PictureBoxes for pictures, not drawings.
modified on Friday, June 10, 2011 12:33 PM
|
|
|
|
|
Thanks for the info. I haven't tried it yet but it sounds logical.
My code this week has no errors. But it's Monday morning and I haven't got out of bed.
|
|
|
|
|
Hi .. Everyone
Im trying to a simple address lookup using winform. What im trying to do is have a text box were a user can type in their PostCode/zip code and it will do a simple database(Sql) lookup but you do not have to type in your full postcode/ zip i can b just the first few characters and it still should find the full address information ..
Can any one help me with this because im stuck on this
|
|
|
|
|
Well did you try anything yet? Are you creating this database? Let's see some code, what do you have done so far, which part are you specifically stuck on?
|
|
|
|
|
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace AddressFinder
{
public partial class AddressSelecter : Form
{
private static readonly object EventClose = new object();
public event EventHandler OnClose
{
add
{
Events.AddHandler(EventClose,value);
}
remove
{
Events.RemoveHandler(EventClose,value);
}
}
public virtual void OnCloseEvent(EventArgs e)
{
EventHandler handler = (EventHandler) Events[EventClose];
if(handler!=null)
{
handler(this, e);
}
}
public AddressSelecter()
{
InitializeComponent();
}
private string buildingNo;
public string BuildingNo
{
get { return buildingNo; }
set { buildingNo = value; }
}
private string street;
public string Street
{
get { return street; }
set { street = value; }
}
private string city;
public string City
{
get { return city; }
set { city = value; }
}
private string country;
public string Country
{
get { return country; }
set { country = value; }
}
public string PostCode
{
get { return txtPostCode.Text; }
set { txtPostCode.Text = value; }
}
private void button1_Click(object sender, EventArgs e)
{
SqlCommand cmd = new SqlCommand(@"select
b.buildingId,
b.buildingname,
s.streetname,
c.cityname
from
zipcode z
inner join city c on z.CityID = c.CityID
inner join building b on z.zipcodeid = b.zipcodeid
inner join street s on z.zipcodeid = s.zipcodeid
where z.zipcode = @postcode;");
cmd.Parameters.AddWithValue("postcode", txtPostCode.Text);
SqlDataReader reader = Read(cmd);
if (reader.HasRows)
{
while (reader.Read())
{
ItemObject item = new ItemObject();
item.Key =
string.Format("{0}, {1}, {2}", reader.GetString(1), reader.GetString(2), reader.GetString(3));
item.ValueOfKey = reader.GetInt64(0).ToString();
listBox1.Items.Add(item);
}
}
}
private void button2_Click(object sender, EventArgs e)
{
ItemObject selectedObject = (ItemObject)listBox1.SelectedItem;
lblBuilding.Text = selectedObject.ToString();
SqlCommand cmd = new SqlCommand(@"select
b.buildingname,
s.streetname,
c.cityname,
cntry.countryname
from
zipcode z
inner join city c on z.CityID = c.CityID
inner join building b on z.zipcodeid = b.zipcodeid
inner join street s on z.zipcodeid = s.zipcodeid
inner join country cntry on z.countryid = cntry.countryid
where z.zipcode = @postcode and b.buildingId = @buildingId");
cmd.Parameters.AddWithValue("postcode", txtPostCode.Text);
cmd.Parameters.AddWithValue("buildingId", selectedObject.ValueOfKey);
SqlDataReader reader = Read(cmd);
if (reader.HasRows)
{
reader.Read();
BuildingNo = reader.GetString(0);
Street = reader.GetString(1);
City = reader.GetString(2);
Country = reader.GetString(3);
}
lblBuilding.Text = BuildingNo + " " + Street;
lblCity.Text = City;
lblCountry.Text = Country;
}
private static SqlDataReader Read(SqlCommand cmd)
{
SqlConnection connection = new SqlConnection("Data Source=localhost;Database=Geo;Integrated Security=SSPI");
connection.Open();
cmd.Connection = connection;
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
return reader;
}
private void btnOk_Click(object sender, EventArgs e)
{
this.Hide();
OnCloseEvent(e);
}
}
public class ItemObject
{
private string key;
private object valueOfKey;
/// <summary>
/// Overloaded constructor.
/// </summary>
/// <param name="key">Key of object.</param>
/// <param name="valueOfKey">Value of object.</param>
public ItemObject(string key, object valueOfKey)
{
this.key = key;
this.valueOfKey = valueOfKey;
}
/// <summary>
/// Default constructor
/// </summary>
public ItemObject()
{
key = string.Empty;
valueOfKey = string.Empty;
}
///<summary>
///Returns a <see cref="T:System.String"></see> that represents the current
///<see cref="T:System.Object">
/// </see>.
///</summary>
///
///<returns>
///A <see cref="T:System.String"></see> that represents the current
///<see cref="T:System.Object">
/// </see>.
///</returns>
public override string ToString()
{
return key;
}
///<summary>
///Serves as a hash function for a particular type.
///</summary>
///
///<returns>
///A hash code for the current <see cref="T:System.Object"></see>.
///</returns>
public override int GetHashCode()
{
return ToString().GetHashCode();
}
/// <summary>
/// Gets or sets Key of object.
/// </summary>
public string Key
{
get { return key; }
set { key = value; }
}
/// <summary>
/// Gets or sets Value of object.
/// </summary>
public object ValueOfKey
{
get { return valueOfKey; }
set { valueOfKey = value; }
}
}
}
I have created the database an every thing. At the moment you have to type in the full post code for it to populate. i want so that you do not have to type in the full post code E.G if the full post code is IG6 6AF you should be able to type any part of it and still be able to search and find the address..
i hope you can help
Thank You
|
|
|
|
|
Use autocomplete on your textbox and bind your postal codes as a datasource, or you can use wildcards for your sql statement albeit you will get multiple results if a search overlaps 2 or more results. I would use wildcards.
|
|
|
|
|
Im sorry to bother you .. but do you think that looking at my code is it very good or is no that good .. becuase i have only just started C# 2 weeks ago and i tought my self .. I dont know how to use wildcard .. if they is an example i can follow i would be happy
|
|
|
|
|