
rbulph
Avatar/Signature-
Posts
398 -
Joined
-
Last visited
Content Type
Profiles
Forums
Blogs
Events
Articles
Resources
Downloads
Gallery
Everything posted by rbulph
-
LOL- almost every post I make gets a comment like the above!! Let me explain what I'm trying to do. I have a number of objects, all drawn in a picturebox. Each has a withevents reference to that picturebox and checks for whether the cursor is over it on the mousemove event. If it is then it sets the cursor to a crosshair. If the cursor is not over any object it should be the default cursor. But where do you set that given that the picturebox's mousemove event occurs before any of the withevents mousemove events? If you set it in the picturebox's mousemove event and the cursor has in fact remained over an object then the user will see the default cursor for a moment before it is switched back to the crosshair by the object. I could redesign things and carry out a check for each object in the picturebox's mousemove event. But I like it this way, it's simple and well encapsulated. One solution I've come up with is to have the owning class set up a new event handler from within its own event handler in an instance of a private class. That handler always seems to occur last. Seems to be good enough for my purposes. Something like the following: Private Sub MouseMoveSub(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.PictureBox1.MouseMove 'This procedure occurs first. MouseOverObj = Nothing lh = New LateHandler(Me.PictureBox1) End Sub Private lh As LateHandler Private Class LateHandler Private ff As PictureBox Private Sub mp2(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) 'Set the cursor to default if not over any object - this occurs last, after any WithEvents handlers. If MouseOverObj Is Nothing then ff.Cursor = Cursors.Default RemoveHandler ff.MouseMove, AddressOf mp2 End Sub Public Sub New(ByVal f As PictureBox) ff = f AddHandler f.MouseMove, AddressOf mp2 End Sub End Class
-
If you have an event which is handled by a number of different handlers, is there an easy way to know when the event has been handled by all of those handlers? Obviously you could keep a record of all of the handlers, but I'd rather something neater.
-
Easy: Public Class Form1 Dim gp As Drawing2D.GraphicsPath Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint gp = New Drawing2D.GraphicsPath gp.AddArc(New Rectangle(50, 50, 200, 200), 0, 145) gp.AddLine(gp.GetLastPoint, New Point(100, 100)) e.Graphics.DrawPath(Pens.Blue, gp) End Sub Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove Dim hp As New Pen(Color.Black, 6) 'Thickness of hypothetical pen is subjective, but 6 seems about right. If gp.IsOutlineVisible(e.Location, hp) Then Me.Cursor = Cursors.Cross Else Me.Cursor = Cursors.Default End If End Sub End Class
-
Does the GraphicsPath have any method for determining whether a given point is within a certain distance of any point on the path? This would seem quite a natural facility for it to provide, but I can't see it anywhere. In its absence it looks as if I will have to call the Flatten method and then test for each pair of points as if they were a short line.
-
Thanks. Not sure that I agree that in your example it would be wrong for OOP to allow an instance of ClassB to be converted to a ClassA. Yes, it would lose the ability to copy files from the Internet, but in making the conversion you must have decided that that's what you want anyway. For me, this is only an issue for events which I'd like the inheriting class to stop raising. Clearly I can do this quite easily just by setting a flag, so it's not that big a problem, and I think I prefer to do this than use code like your example. But it would have been quite elegant to lose the events in a derived class by reducing the object to its base class. My situation is that I am allowing the user to draw objects with the mouse, and once done, the objects are added to a collection for storage. The objects which have been drawn and the objects which are being drawn are both drawn in the same way, but the former needs to use the mousemove event to check if the cursor is over them, while the latter needs to use this same event to redraw the object. It would have been quite neat if I could have just had the drawing object inherit from the drawn object, overriding its mousemove event, and then downgrading it when drawing was completed. Come to think of it, adding and removing handlers might be the best way to deal with this: Public Class Form1 Dim j2 As class2 Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load j2 = New class2(Me) End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click j2.SwitchHandler() End Sub End Class Public Class class2 Private WithEvents f As Form1 Sub New(ByVal ff As Form1) f = ff AddHandler f.MouseMove, AddressOf f_MouseMove End Sub Friend Sub SwitchHandler() RemoveHandler f.MouseMove, AddressOf f_MouseMove AddHandler f.MouseMove, AddressOf f_MouseMove2 End Sub Private Sub f_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Beep() End Sub Private Sub f_MouseMove2(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) f.Text = e.X End Sub End Class Methods and properties are not really an issue for me because you can just have a base class reference to your derived class. The methods and properties of the derived class are then effectively forgotten. It's just events.
-
Is it possible to reduce an object down to its base class? To illustrate what I mean, the idea in the following code would be that the programme stops beeping after the button is pressed because the class2 object is reduced to a class1 object, which doesn't beep. But it doesn't work. Any ideas? Public Class Form1 Dim j1 As class1 Dim j2 As class2 Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim j2 As New class2(Me) End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 'Get rid of the class2 object, I just need the class1 "core" of it now. j1 = CType(j2, class1) j2 = Nothing End Sub End Class Public Class class1 'This class sets the text of the form to the x position of the mouse when the mouse moves. Public WithEvents f As Form1 Public v As Long Private Sub f_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles f.MouseMove f.Text = e.X End Sub Sub New(ByVal ff As Form1) f = ff End Sub End Class Public Class class2 'This class inherits class1 and beeps when the mouse moves over the form. Inherits class1 Sub New(ByVal ff As Form1) MyBase.New(ff) End Sub Public gh As Long Private Sub f_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles f.MouseMove Beep() End Sub End Class
-
I don't see any RequestCancel method. I just see a CancelAsync method, and that seems to cancel the backrgound work without any problems.
-
I thought I posted a reply to this, but it's not there, so I'll post again. Yes, the HttpWebRequest class does have asynchronous methods such as BeginGetResponse. But I ran into problems with accessing controls outside of their thread when doing that, so I took to using BackgoundWorker componenents, one for each web page. And that works fine. It was quite easy in fact.
-
This procedure works, but it is a bit slow. It takes up to a third of a second which becomes a problem where I have a number of pages to get the title of. The command that seems to take the time is "req.GetResponse". What would be helpful would be if I could set this up to run in the background - so I set req up to get a response, and then an event fires when req has got its response. But the HttpWebRequest has no events. Any ideas?
-
If you have a property showing in a propertygrid which is numerical and which need not be specified then you pretty much need a nullable type. Otherwise there's no way that the value shown can be blank rather than zero. For instance if a property represents a maximum value, specifying that this maximum value is zero means something very different from specifying that there is no maximum value. (Although I note that in the properties window in visual studio, the maximum size of a form does not work in this way - if 0,0 is specified that is understood to mean that there is no maximum size. But there could be circumstances where negative values are possible, and a maximum of zero would have real meaning).
-
I find it a bit irritating that the vertical splitter in the propertygrid control is set to the middle of the control initially and also when the user double clicks on it. If the field names are much longer than the values, they may end up partly obscured while the values have more space than necessary. So I thought I'd have a go at fixing this. I've had partial success in this and I thought I'd post the code here in case either it's of use to anyone else or someone can tell me how to fix the problems that I mention in my comments. In the latter case I would be particularly pleased to hear your suggestions. So here it is - you just need a form with a propertygrid control in it. Imports System Imports System.Reflection Imports System.Reflection.Emit Imports Microsoft.VisualBasic Public Class Form1 Private WithEvents g As Control Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load For Each v As Control In Me.PropertyGrid1.Controls If TypeName(v) = "PropertyGridView" Then g = v End If Next setpropgridobject(Me.PropertyGrid1) End Sub Private Sub setpropgridobject(ByVal obj As Object) Me.PropertyGrid1.SelectedObject = obj SetSplitter() End Sub Private Sub g_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles g.DoubleClick 'Problem with this is that the splitter gets set in the middle before being set where I want it, 'and this is visible. If Not g.Cursor Is Cursors.Default Then SetSplitter() End Sub Private Sub GetLongest(ByVal col As Windows.Forms.GridItemCollection, ByRef r As Integer) For Each i As Windows.Forms.GridItem In col r = Math.Max(r, TextRenderer.MeasureText(i.Label, Me.PropertyGrid1.Font).Width) If i.Expanded Then GetLongest(i.GridItems, r) Next End Sub Private Sub SetSplitter() Dim f As Windows.Forms.GridItem = Me.PropertyGrid1.SelectedGridItem Do Until f.Parent Is Nothing f = f.Parent Loop Dim r As Integer GetLongest(f.GridItems, r) Try g.GetType.InvokeMember("MoveSplitterTo", Reflection.BindingFlags.InvokeMethod Or _ Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance, Nothing, g, New Object() {r + 20}) '20 is determined by trial and error, not very good. Catch ex As Exception Debug.Print(ex.ToString) End Try End Sub End Class
-
I have a class which inherits from the PropertyDescriptor Class. I use this, among other things, to alter certain of the attributes of properties at run time. The PropertyDescriptor's Attribute collection is readonly at run time, so you need to create your own propertydescriptors from scratch to do this. If you inherit PropertyDescriptor, you have to override lots of its functions, and in order to return the correct value for each of those functions, I'm holding onto a reference to the original PropertyDescriptor in the custom one. This works, but it seems very duplicative and just wrong, intuitively. When I call MyBase.New I send all the attributes for the new custom propertydescriptor as a constuctor argument, so the information which the functions return should be obtainable from those surely? Below is the code I'm using, so hopefully you can see what I mean from it. Can anyone tell me how I should be doing this better? Friend Class CustomPropertyDescriptor Inherits System.ComponentModel.PropertyDescriptor Private MyPD As PropertyDescriptor 'Reference to original propertydescriptor. Public Sub New(ByVal f As PropertyDescriptor, ByVal aa() As Attribute) 'aa is the new set of attributes, adapted from the attributes of f. MyBase.New(f.DisplayName, aa) MyPD = f End Sub Public Overrides Function CanResetValue(ByVal component As Object) As Boolean Return MyPD.CanResetValue(component) End Function Public Overrides ReadOnly Property ComponentType() As System.Type Get Return MyPD.ComponentType End Get End Property Public Overrides Function GetValue(ByVal component As Object) As Object Return MyPD.GetValue(component) End Function Public Overrides ReadOnly Property IsReadOnly() As Boolean Get Return MyPD.IsReadOnly End Get End Property Public Overrides ReadOnly Property PropertyType() As System.Type Get Return MyPD.PropertyType End Get End Property Public Overrides Sub ResetValue(ByVal component As Object) MyPD.ResetValue(component) End Sub Public Overrides Sub SetValue(ByVal component As Object, ByVal value As Object) MyPD.SetValue(component, value) 'this will call the Property Set method. End Sub End Class
-
Well in fact I already have a custom property descriptor class used for all my properties so all I need is to do this in that class: Public Overrides Function ShouldSerializeValue(ByVal component As Object) As Boolean Return False End Function This works perfectly to prevent things appearing in bold. What I find a bit puzzling is that serialization continues to work perfectly well in spite of me doing this, i.e. the classes can be saved no problem. I was thinking I would have to amend the return value of the function when serializing, but I don't. Maybe the name of the function isn't a very clear indication of what it actually does.
-
If anyone does investigate this further, I'd love to know how you control whether the custom drop down extends all the way across the values column. Currently it always extends at least this far, but that's not very good where the dropdown is a non-resizeable control like the monthview. You get a lot of unnecessary white space surrounding the control when there's more room than is necessary.
-
Have a look. It's well worth knowing about.
-
Thanks for posting this, but you do realise that I'm referring to the PropertyGrid, which is a specific control in the toolbox, don't you? It doesn't have a TableStyles property. So I can't see how I would use your code unfortunately.
-
Yes, that would be very helpful, please. I can navigate the cells through the GridItems collection, starting with the SelectedGridItem, and can then get values using the GridItem's value property, but beyond that I wouldn't know how to go about what you do.
-
Is there any way that I can get the property grid to forget the idea of showing non-default values in bold? Not every property logically has a default value, and I find it distracting that, for me, the column with the values is all bold, because I don't have any default values. I was wondering if I might be able to substitute all property descriptors for custom ones which inherit from PropertyDescriptor, and override a function that determines whether the value is the default one, but had no success when I looked into this. Any other ideas?
-
Alright then. It's to do with a situation where different people might analyse a given situation in different ways. This is not the actual code, but illustrates what I mean in a very simplified manner. 'This is WindowsApplication1. It has no reference to the plug-in. Public Class Form1 Implements IDataHandler Public Sub AddData(ByVal sPage As String) Implements IDataHandler.AddData Me.ComboBox1.Items.Add(sPage) End Sub Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim obj As New a Dim PlugInInst As IAnalysis 'Code to find the plug-in and set PlugInInst to an instance omitted. PlugInInst.SetHandler(Me) PlugInInst.Analyse(obj) PlugInInst.SetHandler(Nothing) End Sub End Class Public Class abc End Class Public Class a Inherits abc End Class Public Class b Inherits abc End Class Public Interface IAnalysis Sub Analyse(ByVal x As abc) Sub SetHandler(ByVal Handler As IDataHandler) End Interface Public Interface IDataHandler Sub AddData(ByVal sPage As String) End Interface and 'This is the plug-in. It references WindowsApplication1. Public Class Class1 Implements WindowsApplication1.IAnalysis Private MyHandler As WindowsApplication1.IDataHandler Public Sub Analyse(ByVal x As WindowsApplication1.abc) Implements WindowsApplication1.IAnalysis.Analyse Dim ty As Type = x.GetType Select Case True Case ty Is GetType(WindowsApplication1.a) MyHandler.AddData("http://www.imdb.com/Glossary/A") MyHandler.AddData("http://alpha.org/") Case ty Is GetType(WindowsApplication1.b) MyHandler.AddData("http://www.beta-uk.org/") End Select End Sub Public Sub SetHandler(ByVal Handler As WindowsApplication1.IDataHandler) Implements WindowsApplication1.IAnalysis.SetHandler MyHandler = Handler End Sub End Class
-
An interface is implemented by the plug-in to receive the object. But you can't have the objects implement an interface to do what the plug-in is to do, and so avoid any demand for a Select Case structure in the plug-in, because they are defined in the class library, not the plug-in.
-
I'm not trying to write a blind plug-in. The plug-in expects an object of a certain base type. As I say, I'm using the class structure to represent real world information. The plug-in receives the object through the interface. The plug-in then determines which out of various classes that implement the base type the object is. Maybe it also examines some properties of the object. It then returns information accordingly. It never calls any methods of the object. Having an interface to do this for each type of object would require the functionality of the plug-in to go into the class library, which would defeat the purpose of having a plug-in.
-
Actually I don't entirely accept this. Suppose you're using an object system to model a real world situation, and you have a facility for plug-ins to examine those objects. They may need to know what type of object they have in order to take appropriate action. They're not examining the type of the object in order to call one of its methods. They 're just examining it to get information about the situation presented to them. It would be much simpler if they could use a "Select Case Object.GetType" structure than an "If Then, ElseIf" structure. But it seems this isn't possible, unless there's some way that I can define the "=" operator for the System.Type type, and I can't find such a way.
-
I've done this and it's made things a lot simpler. There were even more "hoops" that I'd had to jump through to communicate between the class library and the exe than I had remembered, and now I have none of that. I also note that in the Project's property page you can declare a Root Namespace, so can give your project a namespace like "MyClassLibrary", so that the plugins import that rather than the name of your project, which seems more intuitive, and removes any concerns I might have had about a plug-in writer expecting to access a class library.
-
Thanks for the overview. As regards the host interface which you refer to, I prefer to use this than have functions which return all the information. This is because the methods which I require plug-ins to implement can return one or more objects. It's easier for the plug-in writer to have a reference to an object in the application which implements the host interface and be able to go "DataHandler.Add X" with this for each object than for him to have to worry about building up a collection/array of these objects and then return that at the end of the function. I suppose I could also send a ByRef Collection object to each plug-in interface method to be filled, but that would rather lengthen the method declarations. As I say, I also have events in the DLL which inform the exe when properties of certain objects have changed, because the display shown in the exe reflects some of those properties and needs to be updated when they have changed. I think that to avoid that I may now go and merge my class library into my exe. One thing that occurs to me in doing this is that I need to make sure that all forms and user controls in the exe are Friend classes - I certainly don't want the plug-ins being able to access them. When I add a new form or user control to a project it is Public by default so I will have to watch it - I don't know if there is a way to change this?