I'm trying to generate images using ImageTools, and my code works and successfully creates images...but only if I have user input before trying to create the images!
If I try to generate the images in the New sub, for example, the images are created, but they only contain the textbox control from my canvas and not the image (my control consists of text + image). So the image is being created...but it's only rendering partial content.
If I put a button on my page and generate my images from the button click even handler the images are generated correctly.
So what am I doing wrong here? And how can I get my images to generate without user input (i.e. when the app launches).
I get the exact same results using WriteableBitmap in place of ImageTools, FWIW.
I create stackpanels with a canvas and my text/image elements, then use the standard code to render the images to files in isolated storage. Since it all works perfectly after user input I don't know which parts of the code to provide...I'm basically using unmodified sample code.
Code parts (this is all in my MainPage.XAML.VB):
Public Sub New()
InitializeComponent()
' some code commented out while debugging - not relevant here
SetupHubTiles() ' this is the method that sets up the images (see below)
End Sub
The SetupHubTiles method makes several calls to the following method:
Public Sub CreateHubTile(background As StackPanel, tileImage As String, tiletoupdate As HubTile)
Dim isoStoreTileImage = String.Format("isostore:{0}", tileImage)
'Create a bitmapImage to IsolatedStorage.
Using store As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication()
'Tile image's Height * Width are 173 * 173.
Dim bitmap = New WriteableBitmap(173, 173)
'Render a bitmap from StackPanel.
bitmap.Render(background, New TranslateTransform())
Dim stream = store.CreateFile(tileImage)
bitmap.Invalidate()
bitmap.SaveJpeg(stream, 173, 173, 0, 100)
stream.Close()
End Using
SetHubTileImage(tileImage, tiletoupdate) ' this is what sets a control on the MainPage to display the generated image
End Sub
And finally the button click handler (which I just implemented because the code I'm using works fine in another app, but that app always gets user input before creating images, so I figured it was the only difference between the two apps)
Private Sub StartButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles StartButton.Click
SetupHubTiles()
End Sub
As you can see the code being executed is identical, but I get a different result when I run it directly in my contructor compared to running it from a button click handler.
The goal is for these images to be generated at runtime (without any interaction from the user) to be used in the UI.
I've tried a few different methods of doing this, but I always get the same results - an image is generated with just text when there should be text + image. I am using the same method in other apps with the only difference being those other apps are not creating the images as soon as the app launches, which may be the problem.
It also doesn't seem to make a difference if I change the location/type of controls that I am using to build my images.
Based on your comment, try to invoke your method either later in the loading sequence or use:
Dispatcher.BeginInvoke(() => { GenerateImages(); });
This will queue the function up to be run on the next UI thread tick which should be after any pending layout work which is queued up.
The fact that you are calling this function right at the end of the constructor has no consequence (other than the fact that the variables representing different objects have been initialized). It's all happening in the same UI thread tick, before Layout is kicked off so there is nothing in the StackPanel for you to capture.
To fix this, add your code to the Loaded event of the Page and still wrap it in a Dispatcher.BeginInvoke call so that it is guaranteed to happen after all of your Controls have rendered (at least their first pass, not withstanding any content that is loaded after the startup sequence).
Related
I am trying to change the Image attribute associated with a button when the button is clicked.
I have added the image I want to display (called "Black Pawn.png") as a resource within the solution and (from searching around and looking at similar questions) am trying to set the image attribute to it as shown below:
Private Sub boardButtonA2_Click(sender As Object, e As EventArgs) Handles boardButtonA2.Click
Dim sprites As Object = My.Resources.ResourceManager
boardButtonA2.Image = sprites.GetObject("Black Pawn.png")
End Sub
But when I click the button, the default image on it just disappears and is replaced with nothing instead of Black Pawn.png.
I am pretty new to using Visual Basic and Visual Studio so I may have failed to have added the image as a resource properly but I can see the image in the Solution Explorer above the form the button is in.
Any help or advice would be a great help.
Thanks.
EDIT:
Having thought more about the potential scenario, I'm wondering whether this answer is actually relevant. Your example code uses a hard-coded String but I'm wondering whether the actual String really would be a user selection. I'll leave it here anyway.
ORIGINAL:
There's no reason to use a hard-coded String to get a resource. If the String was from user entry then maybe, depending on the circumstances. As it is though, you should be using the dedicated property for that resource. That means using this:
boardButtonA2.Image = My.Resources.Black_Pawn
Just be aware that a new object is created every time you get a resource from My.Resources. For that reason, don't keep getting the same property over and over. If you need to use a resource multiple times, get it once and assign it to a field, then use that field multiple times, e.g.
Private blackPawnImage As Image
Private Function GetBlackPawnImage() As Image
If blackPawnImage Is Nothing Then
blackPawnImage = My.Resources.Black_Pawn
End If
Return blackPawnImage
End Function
and then:
boardButtonA2.Image = GetBlackPawnImage()
Also, I suggest that you change the name of the property to BlackPawn rather than Black_Pawn. You can change it to whatever you want on the Resources page of the project properties.
EDIT:
If this application is a chess game then you definitely will need every resource image for the playing pieces so you probably ought to get all of them at load and assign them to variables, then use them from those variables over the course of the game.
(I am assuming the question is for WinForms, if it isn't I will remove this answer)
When adding images as resources to a project, you have to pay attention to the name given to the resource after it is imported - the name can be changed if you want.
The image below is from one of my projects:
The name of the files on disk are:
CSV - Excel.png
GreenDot.png
RedDot.png
To fix your problem change the line:
boardButtonA2.Image = sprites.GetObject("Black Pawn.png")
to be:
boardButtonA2.Image = sprites.GetObject("Black_Pawn")
I don't think adding the image by going into Project > Add Existing Item properly added the image as a resource.
I instead went into *Project > (Solution Name) Properties > Resources > Images > Add Existing Item * and added it that way and was then able to get it working using jmcilhinney's method (I think my original method/a variation of it would work too but theirs is better).
I am trying to use the VB.NET program settings to load a background image for every form every time the form is loaded. So far I've managed to get the program to set the background in one form, and that changes the background for every other form. However, when each form is closed and re-opened while the program is running, the background changes back to the default one. I need to somehow change the background once and load it every time the form is opened, so that it doesn't switch back every time the form is re-opened while the program is running. I think there is some way to do this using the My.Settings in VB.NET, but I'm not sure.
This is the code that changes the background for each form:
Me.BackgroundImage = PreviewBackgroundBox.Image
MainForm.BackgroundImage = PreviewBackgroundBox.Image
LogInForm.BackgroundImage = PreviewBackgroundBox.Image
The PreviewBackgroundBox is used to show the user the image before they apply it, and then when they click apply then the image is taken from the PreviewBackgroundBox and set as the background for all the forms.
Could someone help me with this?
Thanks!
Basically you need a dictionary remembering the image is to be displayed for each form. You could store such a dictionary in a module together with methods to handle the logic involved
Private imageDict As New Dictionary(Of String, Image)
Public Sub SetImage(ByVal formName As String, ByVal img As Image)
imageDict(formName) = img
End Sub
Public Function GetImage(ByVal formName As String) As Image
Dim img As Image
If imageDict.TryGetValue(formName, img) Then
Return img
End If
Return Nothing 'Or return a default image
End Function
Note: Dictionaries store some data and associate it with a key that is used to retrieve this data. Here I would use the form's name as Key. You could also use the form's type GetType(Form1) or Me.GetType() and use a Dictionary(Of Type, Image) instead.
Whenever the user selects another image call SetImage in order to remember it. When a form is opened call GetImage to get the remembered image.
I want to use an If statment to check if a specific image from my resources is loaded.
I want to change the image when it is clicked as in:
If PictureBox1.Image = My.Resources.BIKE13 Then
PictureBox1.Image = My.Resources.BIKE13_Helmet
End If
I'm going to use several ElseIf Statements
After some search I found several ways to do it but I have about 20-30 images and I'm and I need a simple method.
(VS 2010)
That code cannot possibly work for two reasons. Firstly, an Image is a reference type object, so to compare like that you would have to use the Is operator rather than =. Secondly, My.Resources creates a new object each time, so even using Is will never give you a match.
What you need to do is use the My.Resources property once and once only and assign the Image object to a member variable. You can then use Is with that field.
E.g.
Private bike13Image As Image = My.Resources.BIKE13
Private bike13HelmetImage As Image = My.Resources.BIKE13_Helmet
and
If PictureBox1.Image Is bike13Image Then
PictureBox1.Image = bike13HelmetImage
End If
I am new to VB.NET. I am developing a game in which the user has to guess the name of the image that appears in a picturebox. The user has to enter the answer in a textbox. If it is right, a button appears and changes the image of the picturebox.
I would like the image of the picturebox to change when the next level button is clicked. I want to make an array which will contain all the images with a string as the answer.
How would I go about changing the image to the next in the array after each next level button click?
Thanks
To Load images to array
Dim imgPictures(8) As Image
imgPictures(0) = Bitmap.FromFile("Filename")
To display images.
Dim i as integer = 0
Next Image
myPictureBox.Image = imgPictures(i++)
Previous Image
myPictureBox.Image = imgPictures(i--)
Make sure you are checking for maximum and minimum array values, otherwise you will get indexoutofrange exceptions.
It's hard to tell exactly what parts of the code you need help with, so I'm guessing here. I went in a different direction than #Anuraj, which hopefully is helpful.
Let's say you create an object-based array:
Dim arr(10) As Object
You then create a Sub in your code that will handle the "next level" button's click event:
Private Sub NextLevelButton(ByVal sender As Object, ByVal e As RoutedEventArgs)
'Do Stuff
End Sub
Now replace 'Do Stuff with code that retrieves the next item in the array, and then changes the image in the UI. Obviously, to do this, you'll need to know which item in the array was most recently used. The easiest way to do this is to create a global variable (outside of all subs in your code) which you will replace each time you add a new image to the UI. The value of this global variable will be the index number of the most recently used image in your array. Then, you just increment by one and grab the next image.
I have a program that gives the user driving directions, I would like the user to be able to print just the directions or just the directions and a map by selecting a corresponding radio button for each option. How can I print just these sections and not the whole form? And how can I use the radio buttons to tell it what to print? Thanks
Updated code:
Private Sub btnprint_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnprint.Click
If rbprintall.Checked = True Then
'Ad code to print all here
ElseIf rbprintdirections.Checked = True Then
lbldirections.DrawToBitmap()
lbldirections2.DrawToBitmap()
lbldirections3.DrawToBitmap()
lbldirections4.DrawToBitmap()
Else
MessageBox.Show("You must select one of the above options!")
End If
If you're using Windows Forms, almost all of the built-in controls can be easily rendered to an image using their DrawToBitmap method. If you simply want to print whatever is currently displayed by a control, this is the easiest way to do it. Do note, however, that there are a couple of caveats to what can be drawn this way. See the linked documentation for details.
Printing in the .NET Framework is also made relatively simple by taking advantage of the functionality provided in the System.Drawing.Printing namespace. If you aren't already experienced with this, Google for some tutorials. For example, there's a pretty good article here from the MSDN Magazine.
So, to accomplish your ultimate goal, you'll need to do the following:
When the user clicks "Print", determine which radio button is currently selected.
Create a temporary bitmap and grab a copy of the appropriate control in that bitmap using the control's DrawToBitmap method. (If you need to print multiple controls, create a separate temporary bitmap for each of them, and then print each of them in the next step.)
Draw that image to the printer using the Graphics.DrawImage method in the PrintPage event handler method for a PrintDocument object you've created.
EDIT: I'm not really sure where your question is with regards to the code that you've posted, but I see two primary problems.
In the first block of your if statement, the comment suggests you don't know how to print all of the controls. You have a couple of different options. If you just want to print every control on the form as it appears, you can simply use the DrawToBitmap method of the Form itself. That will create a bitmap of the form's entire client area, including all of the controls it contains.
If there are still some controls on the form that you don't want to print, even when the user chooses "Print All", you'll need to call the DrawToBitmap methods on each individual control. You might set up a loop so that you don't have to write a line of code for every control, but there's not really another good option.
I suspect you're going to have problems with the second elseif block working as you expect it to, also. Recall that I said above you need to create temporary bitmap images, and then draw into those bitmaps? The DrawToBitmap method takes two parameters:
bitmap: a System.Drawing.Bitmap that you want an image of the control to be drawn into
targetBounds: a System.Drawing.Rectangle that describes the bounds of the control that will be rendered
The code you've shown is missing both of those parameters. In this case, the second (targetBounds) is simple—because you want to draw the entire control, all you need to do is specify it's ClientRectangle property for that parameter.
For the first (bitmap) parameter, you need to do as I mentioned above and create a new Bitmap image by declaring a temporary variable. Then, you need to call the DrawToBitmap method with this temporary bitmap specified.
Maybe I can be clearer with an example. Modify your above code to look like this:
'Declare some class-level variables to hold images of the controls to print
Private bmpDirections1 As System.Drawing.Bitmap
Private bmpDirections2 As System.Drawing.Bitmap
Private bmpDirections3 As System.Drawing.Bitmap
Private bmpDirections4 As System.Drawing.Bitmap
Private Sub btnprint_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnprint.Click
If rbprintall.Checked = True Then
'Add code to print all here
ElseIf rbprintdirections.Checked = True Then
'Draw lbldirections control to bitmap variable named bmpDirections1
bmpDirections1 = New Bitmap(lbldirections.Width, lbldirections.Height)
lbldirections.DrawToBitmap(bmpDirections1, lbldirections.ClientRectangle)
'Draw lbldirections2 control to bitmap variable named bmpDirections2
bmpDirections2 = New Bitmap(lbldirections2.Width, lbldirections2.Height)
lbldirections2.DrawToBitmap(bmpDirections2, lbldirections2.ClientRectangle)
'Draw lbldirections3 control to bitmap variable named bmpDirections3
bmpDirections3 = New Bitmap(lbldirections3.Width, lbldirections3.Height)
lbldirections3.DrawToBitmap(bmpDirections3, lbldirections3.ClientRectangle)
'Draw lbldirections4 control to bitmap variable named bmpDirections4
bmpDirections4 = New Bitmap(lbldirections4.Width, lbldirections4.Height)
lbldirections4.DrawToBitmap(bmpDirections4, lbldirections4.ClientRectangle)
Else
MessageBox.Show("You must select one of the above options!")
End If
End Sub
The only thing to remember is that you need to call the Dispose method on each bitmap variable when you are finished using it. The Dispose method "releases the resources owned by the image", which means that it frees up the memory it was using. There's no reason to keep those large-ish images around if you're not going to use them again. So, you should probably do this after the print job is completed. Once you dispose the bitmap object, you will no longer be able to use the image it contained, but you can always create a new bitmap object and assign it to the variable (as shown in the above code) the next time the user clicks print.
Unrelated: There's no reason to test for If x = True. The simpler If x is the same thing, and considered by most programmers to be better style. Effectively, you're doing a double comparison against True, which isn't necessary.