How to resize images efficiently in C#? - c#

I was searching for around 4 months in order to solve image-flicker problem for my 180 controls, (similar to image-boxes) which are inserted into a flow-layout-panel.
I nearly tried everything (Enabling double flicker and so on...) and nothing worked for me. But, today I figured a solution and that's by setting image size-mode to normal instead of zoom or to resize my images to fit exactly into those controls similar to picture-boxes. So I chose the second option and here is my code:
public MovieControl(Movies M1, bool show, MainForm current)
{
InitializeComponent();
Current = current;
if (M1.movie_Banner == "") bunifuImageButton1.Image = Properties.Resources.no_image_available;
else
{
WebClient client = new WebClient();
Stream stream = client.OpenRead(M1.movie_Banner);
Bitmap bitmap; bitmap = new Bitmap(stream);
if (bitmap != null)
bunifuImageButton1.Image = new Bitmap((Image)bitmap, new Size(139, 208));
stream.Flush();
stream.Close();
client.Dispose();
}
bunifuImageButton1.Tag = M1;
M1 = null;
}
Also, I want to mention that this code is being called by a thread using the following lines:
private void RunMoviesThread()
{
ClearMovies();
LoadMoviesThread = new Thread(() => LoadMoviesControls());
LoadMoviesThread.Start();
}
private void LoadMoviesControls()
{
int x = 1;
if (MPageDropDown.InvokeRequired)
this.Invoke((MethodInvoker)delegate { if (MPageDropDown.SelectedItem != null) x = int.Parse(MPageDropDown.SelectedItem.ToString()); });
else if (MPageDropDown.SelectedItem != null) x = int.Parse(MPageDropDown.SelectedItem.ToString());
for (int i = (x - 1) * Max; i < MoviesList.Count() && i < x * Max; i++)
{
MovieControl tmp = new MovieControl(MoviesList[i], ShowMError, this);
if (tmp.InvokeRequired || MoviesFlowPanel.InvokeRequired)
{
MovieControl m1 = null;
try
{
m1= new MovieControl(MoviesList[i], ShowMError, this);
}
catch { }
this.Invoke((MethodInvoker)delegate { MoviesFlowPanel.Controls.Add(m1); });
}
else
MoviesFlowPanel.Controls.Add(tmp);
tmp = null;
}
}
It worked very well, instead of the fact that it took a very long time to process. for example; and image could take about half a second and up to 20 seconds in some rare cases! could you help me figure a more efficient way...
Please Note: my images are previously uploaded on the internet and this code is written in C#. Also, without the resizing process it took only few seconds to load.
**Max value is set to 180
Thanks in advance.

Well I don't know how much this will help you, but it's worth a shot. I am not entirely confident that WebClient can be parallelized like this, but a similar example appears on page 187 of Pro .NET Performance: Optimize Your C# Applications so I am going to take a leap and say that you can use WebClient from multiple threads. I have added a new method which you could just call from your UI, although it is making a check to be sure it doesn't need to be invoked. This method reads the page number and then launches the background process which will download and resize the images and then after it does all of that it will create the controls in the UI thread.
private void GetPageAndLoadControls()
{
if (MPageDropDown.InvokeRequired)
{
MPageDropDown.Invoke((MethodInvoker)(() => GetPageAndLoadControls();));
return;
}
var page = MPageDropDown.SelectedItem != null ?
int.Parse(MPageDropDown.SelectedItem.ToString()) - 1 : 0;
Task.Run(() => LoadMoviesControls(page));
}
private void LoadMoviesControls(int page)
{
var dict = new ConcurrentDictionary<string, Bitmap>();
var movies = MovieList.Skip(page * Max).Take(Max).ToList();
using (var client = new WebClient())
{
Parallel.ForEach(movies, (m) =>
{
Stream stream = null;
Bitmap bmp = null;
try
{
if (!string.IsNullOrWhitespace(m.movie_Banner)
{
stream = client.OpenRead(s);
bmp = new Bitmap(stream);
// Note: I am guessing on a key here, that maybe there is a title
// use whatever key is going to be best for your class
dict.TryAdd(m.movie_Title, new Bitmap(bmp, 139, 208));
}
else dict.TryAdd(m.movie_Title, Properties.Resources.no_image_available);
}
finally
{
bmp?.Dispose();
stream?.Dispose();
}
});
}
// Here we have to invoke because the controls have to be created on
// the UI thread. All of the other work has already been done in background
// threads using the thread pool.
MoviesFlowPanel.Invoke((MethodInvoker)() =>
{
foreach(var movie in movies)
{
Bitmap image = null;
dict.TryGetValue(movie.movie_Title, out image);
MoviesFlowPanel.Controls.Add(
new MovieControl(movie, image, ShowMError, this);
}
});
}
// Changed the constructor to now take an image as well, so you can pass in
// the already resized image
public MovieControl(Movies M1, Bitmap image, bool show, MainForm current)
{
InitializeComponent();
Current = current;
bunifuImageButton1.Image = image ?? Properties.Resources.no_image_available; // Sanity check
bunifuImageButton1.Tag = M1;
}
Edit
Something that occurred to me after writing this and thinking more about it, you did not post the code for ClearMovies(), but from the posted code I am assuming that you are disposing the previous 180 controls and creating 180 new ones. What would be better would be to use the same approach I have shown in the code above and then instead of "creating" new controls you just update the existing ones. You could add an update method to your user control that just changes the image and the Movie item that is being stored in the control. This would save the overhead of creating new controls each time which should improve the performance. You may need to call Invalidate at the end of the Update method. Not sure and since I can't really test this all out, just wanted to mention it. Also, instead of using the Tag property to hold the Movie object, I would just add a member variable to your UserControl. This would add type safety and make it clearer what is being held where.
public class MovieControl : Control
{
public Movies Movie { get; protected set; }
// Changed the constructor to now take an image as well, so you can
// pass in the already resized image
public MovieControl(Movies M1, Bitmap image, bool show, MainForm current)
{
InitializeComponent();
Current = current;
bunifuImageButton1.Image = image ?? Properties.Resources.no_image_available; // Sanity check
Movie = M1;
}
public void UpdateMovieAndImage(Movies movie, Image image)
{
// Only dispose if it isn't null and isn't the "No Image" image
if (bunifuImageButton1.Image != null
&& bunifuImageButton1.Image != Properties.Resources.no_image_available)
{
binifuImageButton1.Image.Dispose();
}
bunifuImageButton1.Image = image;
Movie = movie;
}
}

Related

In WPF, how do I change the source of an image programatically? [duplicate]

This question already has answers here:
How to load image to WPF in runtime?
(2 answers)
Closed 1 year ago.
I have a custom control with a default image that I want to change based on which iteration of the control it is. For example, I have one for "F1" and "NumLock" and so on. In the constructor of the control, I have this:
public FixerBox(Dictionary<string,string> deets)
{
InitializeComponent();
btnOff();
this.FixerTitle.Text = deets["Title"];
this.FixerDesc.Text = deets["Description"];
this.FixerTags.Text = deets["Tags"];
this.FixerImg.Source = new BitmapImage(new Uri(deets["Img"], UriKind.Relative));
}
The bitmap stuff was based on another answer and produces this:
Below is the control itself showing that it's correctly getting the title, tags, and description, but the image is bunk (on the left side, that thin grey line is the border that should be around the image).c#
If I was using HTML/CSS, I could right-click the image to see what exactly its properties are, but I don't know how to get that kind of information using WPF. The best I could manage was in the top area is a status window where I've manually printed a "Tostring" output of the first controls image source data. Near as I can tell, it's all correct, but there's no actual image there. Every subsequent control has the same output (one thin line where the image should be).
EDIT Per comments, here is some more of the information. The main XAML file loads up the controls like so in its constructor:
public partial class MainWindow : Window
{
private Fixers fixers = new Fixers();
// This is the custom control consisting mostly of various boxes
private Dictionary<string,FixerBox> fixerBoxes = new Dictionary<string, FixerBox> { };
public MainWindow()
{
InitializeComponent();
var fixNames = fixers.FixerNames();
foreach (string key in fixNames)
{
fixerBoxes[key] = new FixerBox(fixers.GetFix(key));
FixersArea.Children.Add(fixerBoxes[key]);
}
StatusBox.Text += fixerBoxes["F1"].FixerImg.Source.ToString();
}
}
The fixers variable is of class Fixers which consists of the below (abbreviated to show just the F1 function for brevity):
class Fixers
{
private string ClearWS(string str)
{
var first = str.Replace(System.Environment.NewLine, "");
return first.Replace("\t", "");
}
// Loads registry functions
private Regis regStuff = new Regis();
// Loads preferences from the file
private Prefs prefs = new Prefs();
// A timer to make sure the system behaves
private Timer watcher;
// Watcher action toggles
private bool watchNumL = false;
// Translation array from fix shortname to various data about them
private Dictionary<string, Dictionary<string, string>> fixers = new Dictionary<string, Dictionary<string, string>>
{
["F1"] = new Dictionary<string,string> {
["PrefName"] = "KillF1UnhelpfulHelp",
["Img"] = #"/graphics/F1key.png",
["Title"] = #"Diable F1 ""Help"" function",
["Description"] = #"
Have you ever hit the F1 key by accident and had a distracting and unhelpful window or webpage open as a result?
Windows set the F1 key to a generic help function that basically never helps and always gets in the way.
Enable this control to disable that obnoxious design choice. Note that some programs still respond to F1 on their own accord,
but this will stop the default Windows behavior in things like Windows Explorer at least.
",
["Tags"] = "#Keyboard,#Rage"
},
};
public Fixers()
{
// The readability hack above with multi-line strings introduces a bunch of extra whitespace. Let's clear that out
foreach (var fixKey in fixers.Keys)
{
fixers[fixKey]["Description"] = ClearWS(fixers[fixKey]["Description"]);
}
}
public List<string> FixerNames()
{
return fixers.Keys.ToList();
}
public bool IsFixed(string which)
{
// If we're watching, it's fixed
if ("NumL" == which) return watchNumL;
// For anything registry related
return regStuff.IsFixed(which);
}
public Dictionary<string,string> GetFix(string which)
{
return fixers[which];
}
}
if you use binding, you can create in your ViewModel a string, in which is stored the path of your image, then you can easily change programatically its path.
Then in XAML just bind image's source to the string.
In my case I have a list of objects, with the property `ImageName' :
<Image Source="{Binding DataContext.SelectedMacro.ImageName,
RelativeSource={RelativeSource AncestorType=Window}}"/>

Getting the bytes[] / stream of an Image or ImageSource in Xamarin.Forms?

I've been digging into Xamarin.Forms.Labs to learn more about how Images are accessed and manipulated. Saving an image to an Android/iOS photo gallery works fine, as does retrieving an image from either, at least with user interaction.
The problem is that I'd like to be able to save some files internally to the program. Actually saving a file itself doesn't seem to be a problem - I've written up an interface/DependencyService solution for that.
What I can't seem to do is access the bytes[] or stream of the image data itself with a Xamarin.Forms Image or ImageSource. Reading a stream into the ImageSource is relatively straightforward with the static method, so how do I get these bytes out in order to save the file within the program itself?
To frame this: I'm working on an app right now where the user takes/selects pictures to include within a form, and the form is eventually posted to a website. So being able to actually save the pictures, or access the data itself in order to transfer it, is pretty key.
One really hacky way to do that would be to write a custom renderer for your image. Then the renderer will get the byte[]/Stream from the native control. Here's a very rough implementation without any error handling to help you get started:
public class MyImage : Image
{
public Func<byte[]> GetBytes { get; set; }
}
Here's the iOS renderer:
public class MyImageRenderer : ImageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
var newImage = e.NewElement as MyImage;
if (newImage != null)
{
newImage.GetBytes = () =>
{
return this.Control.Image.AsPNG().ToArray();
};
}
var oldImage = e.OldElement as MyImage;
if (oldImage != null)
{
oldImage.GetBytes = null;
}
}
}
The Android one is a little more involved:
public class MyImageRenderer : ImageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
var newImage = e.NewElement as MyImage;
if (newImage != null)
{
newImage.GetBytes = () =>
{
var drawable = this.Control.Drawable;
var bitmap = Bitmap.CreateBitmap(drawable.IntrinsicWidth, drawable.IntrinsicHeight, Bitmap.Config.Argb8888);
drawable.Draw(new Canvas(bitmap));
using (var ms = new MemoryStream())
{
bitmap.Compress(Bitmap.CompressFormat.Png, 100, ms);
return ms.ToArray();
}
};
}
var oldImage = e.OldElement as MyImage;
if (oldImage != null)
{
oldImage.GetBytes = null;
}
}
}
And finally, the usage is the kind of obvious:
var bytes = myImage.GetBytes?.Invoke();
Extending the idea to support streams should be pretty straightforward. The obvious caveat is that the forms control (MyImage) retains a reference to its renderer via GetBytes(), but that seems to be unavoidable.

Lightswitch export all rows to CSV

I am using c# and VS2012 on a lightswitch web-application,
I wish to export my data to CSV (on a search screen!), but can't reach any POC,
As i understand there are 2 main problems - a savefiledialog must be caused directly from a user button and in it must happened in the main dispatcher,
I used this code :
partial void mySearchScreen_Created()
{
var CSVButton = this.FindControl("ExportToCSV");
CSVButton.ControlAvailable += ExportCSV_ControlAvailable;
}
private void ExportCSV_ControlAvailable(object sender, ControlAvailableEventArgs e)
{
this.FindControl("ExportToCSV").ControlAvailable -= ExportCSV_ControlAvailable;
Button Button = (Button)e.Control;
Button.Click += ExportCSV_Click;
}
private void ExportCSV_Click(object sender, System.Windows.RoutedEventArgs e)
{
Microsoft.LightSwitch.Details.Client.IScreenCollectionProperty collectionProperty = this.Details.Properties.mySearch;
var intPageSize = collectionProperty.PageSize;
//Get the Current PageSize and store to variable
collectionProperty.PageSize = 0;
var dialog = new SaveFileDialog();
dialog.Filter = "CSV (*.csv)|*.csv";
if (dialog.ShowDialog() == true) {
using (StreamWriter stream = new StreamWriter(dialog.OpenFile())) {
string csv = GetCSV();
stream.Write(csv);
stream.Close();
this.ShowMessageBox("Excel File Created Successfully. NOTE: When you open excel file and if you receive prompt about invalid format then just click yes to continue.", "Excel Export", MessageBoxOption.Ok);
}
}
collectionProperty.PageSize = intPageSize;
//Reset the Current PageSize
}
private string GetCSV()
{
StringBuilder csv = new StringBuilder();
int i = 0;
foreach (var orderRow_loopVariable in mySearch) {
var orderRow = orderRow_loopVariable;
////HEADER
if (i == 0) {
int c = 0;
foreach (var prop_loopVariable in orderRow.Details.Properties.All().OfType<Microsoft.LightSwitch.Details.IEntityStorageProperty>()) {
var prop = prop_loopVariable;
if (c > 0) {
csv.Append(",");//Constants.vbTab
}
c = c + 1;
csv.Append(prop.DisplayName);
}
}
csv.AppendLine("");
////DATA ROWS
int c1 = 0;
foreach (var prop_loopVariable in orderRow.Details.Properties.All().OfType<Microsoft.LightSwitch.Details.IEntityStorageProperty>()) {
var prop = prop_loopVariable;
if (c1 > 0) {
csv.Append(",");//Constants.vbTab
}
c1 = c1 + 1;
csv.Append(prop.Value);
}
i = i + 1;
}
if (csv.Length > 0) {
return csv.ToString(0, csv.Length - 1);
} else {
return "";
}
}
This works, but it only get's me the first page items,
On another thing i had to do i solved that problem by using this code :
this.DataWorkspace.myDataContextData.MySearch(...).Execute();
Yet trying that instead of just using 'MySearch' gives me the following error :
t is not valid to call Execute() on a different Dispatcher than the ExecutableObject's Logic Dispatcher.
Why is it so difficult to do such a basic thing related to data (export to csv/excel) on a system build for handling data ?
Any ideas ?
The simplest workaround if this is the only use of the search screen would be to turn off paging. To do this go to the screen designer, highlight the query on the left, and in properties uncheck 'support paging.'
I'm not sure what the limitations are, but you can run some code in a different dispatcher using:
this.Details.Dispatcher.BeginInvoke(() =>
{
//This runs on main dispatcher
});
I don't think there's anything wrong with your code, but I've noticed that it takes a while to reset the page size on a large collection, in which time the rest of your code continues to execute. I think that's why you only get the first page. The only solution I've found is to wait.
When the "File Download - Security Warning" dialog pops up, keep an eye on the 'busy' indicator on the screen's tab and also the 'Page x of y' status at the bottom of the grid if you can see it. Only when the busy indicator has gone and the status just says 'Page' should you click OK to continue.
I haven't figured out a way of doing this programmatically so it's not a very helpful feature unless you have a very tightly controlled user population. But if it's just you and a couple of power users, it is workable. I'm also not sure if this has been improved on in versions after VS2012.
There can be a downside to the other answer of taking the paging off the query entirely. I've tried that workaround when the grid collection was being displayed in a modal window and the window became uncloseable if there were too many rows in the grid.
Phil

Comparing two images from the clipboard class

In a C# winform app. I'm writing a Clipboard log manager that logs text to a log file, (everytime a Ctrl+c/x is pressed the copied/cut text gets appended to the file)
I also did the same for images, that is, if you press "prtScreen", the screen shot you took will also go to a folder.
I do that by using a timer, inside I have something which 'looks' like this:
if (Clipboard.ContainsImage())
{
if (IsClipboardUpdated())
{
LogData();
UpdateLastClipboardData();
}
}
This is how the rest of the methods look like:
public void UpdateLastClipboardData()
{
// ... other updates
LastClipboardImage = Clipboard.GetImage();
}
// This is how I determine if there's a new image in the clipboard...
public bool IsClipboardUpdated()
{
return (LastClipboardImage != Clipboard.GetImage());
}
public void LogData()
{
Clipboard.GetImage().Save(ImagesLogFolder + "\\Image" + now_hours + "_" + now_mins + "_" + now_secs + ".jpg");
}
The problem is: inside the update method, "LastClipboardImage != Clipboard.GetImage()" is always returning true!
I even did the following inside the update method:
Image img1 = Clipboard.GetImage();
Image img2 = Clipboard.GetImage();
Image img3 = img2;
bool b1 = img1 == img2; // this returned false. WHY??
bool b2 = img3 == img2; // this returned true. Makes sense.
Please help, the comparison isn't working... why?
A little test. Call two times the GetImage method for the same image
void Main()
{
Image bmp1 = Clipboard.GetImage();
Image bmp2 = Clipboard.GetImage();
if(bmp1 != null && bmp1 == bmp2)
Console.WriteLine("True");
else
Console.WriteLine("False");
}
it returns always false. So every time you call Clipboard.GetImage() you get a different image instance and thus you cannot compare then using a simple == operator
You are comparing two different instances of an Image object and of course they are not the same.
If you really want to compare the image down to the pixel level you need a more invasive (and performance hungry method) like this
bool ImagesAreDifferent(Image img1, Image img2)
{
Bitmap bmp1 = new Bitmap(img1);
Bitmap bmp2 = new Bitmap(img2);
bool different = false;
if (bmp1.Width == bmp2.Width && bmp1.Height == bmp2.Height)
{
for (int i = 0; i < bmp1.Width; i++)
{
for (int j = 0; j < bmp1.Height; j++)
{
Color col1 = bmp1.GetPixel(i, j);
Color col2 = bmp2.GetPixel(i, j);
if (col1 != col2)
{
i = bmp1.Width + 1;
different = true;
break;
}
}
}
}
return different;
}
Notice how this is possible because the Color structure defines an Equality operator that checks if the color RGB values are the same between two colors
Image checks equality with the Object.equals, which tests for reference equality with reference types, not semantic equality. This is why img2 == img3 is true, as you have previously copied img2's reference into img3. However, for img1 and img2, you called Clipboard.GetImage which constructs a new image object.
If you actually want to test if two image objects contain the same data, you will need to write your own method - perhaps an extension method if you don't want to subclass Image.
public static class ImageExtensions
{
public static bool MyEquals(this Image x, Image y)
{
// compute and return your definition of equality here
}
}
Note that the == operator will not call this method automatically, and you will have to check equality with Image.MyEquals.
I think rather than comparing images you can change the program logic to overcome this problem.
How about capture event of new item added to clipboard and write in to log?
you can try code sample code from below link
http://www.codeproject.com/Tips/467361/Using-Clipboard-Csharp-4-0-Wrapper-inside
// Use the "ClipboardManager" to manage in a more comprehensive the clipboard
// I assume that "this" is a Form
ClipboardManager manager = new ClipboardManager(this);
// Use "All" to handle all kinds of objects from the clipboard
// otherwise use "Files", "Image" or "Text"
manager.Type = ClipboardManager.CheckType.All;
// Use events to manage the objects in the clipboard
manager.OnNewFilesFound += (sender, eventArg) =>
{
foreach (String item in eventArg)
{
Console.WriteLine("New file found in clipboard : {0}", item);
}
};
manager.OnNewImageFound += (sender, eventArg) =>
{
Console.WriteLine("New image found in clipboard -> Width: {0} , Height: {1}",
eventArg.Width, eventArg.Height);
};
manager.OnNewTextFound += (sender, eventArg) =>
{
Console.WriteLine("New text found in clipboard : {0}", eventArg);
};
// Use the method "StartChecking" to start capturing objects in the clipboard
manager.StartChecking();
// Close the capturing
manager.Dispose();

C#, variable value as a part of an object name?

For example i have a player object with a list of suits of cards it has.
player.card[0] = "diamonds";
player.card[1] = "clubs";
player.card[2] = "spades";
etc...
also i have 4 hidden pictureboxes with an image of suites ( pb_spades , pb_hearts m etc. )
and another 4 pictureboxs ( pb_playerCard1 , pb_playerCard2 , etc. ) to which I have to assign an image from a hidden pb corresponding to the suit of card the player object has.
Therefor
if ( player.card[0] == "diamonds" ) { pb_playerCard1.Image = pb_diamonds.Image; }
of course, doing it all with IFs would take quite a long time... Can I somehow use variable value as a part of an objects name?
kinda
for (int i = 1; i != 5; i++)
{
pb_playerCard+'i'.Image = pb_+'player.card[i+1]'.Image;
}
I don't think that you can use a value as a part of the control name. But, one can use an aray of controls. You will have to change many declarations and initializations of your picture boxes and put them into an array, but you will be able to write more descriptive and readable code.
Create a class Suite that has all properties, like this:
class Suite {
public string Name { get; set; }
public Image Image { get; set; }
and then create a static object for each color:
public static Diamonds = new Suite { Name = "Diamonds", Image = Resources.DiamondImage };
// ...
}
Now you can use Suite.Diamonds.
Even better is to use a Flyweight pattern to avoid the static fields. You use the Flyweight to implement the Card class.
First, there's no reason to have a hidden PictureBox control just so you can use it's Image property to store an image. Just create Image objects.
You could store the images in a dictionary, indexable by name:
var cards = new Dictionary<string, Image>() {
{ "diamonds", Image.FromFile("diamonds.jpg") }
{ "clubs", Image.FromFile("clubs.jpg") }
//...
};
Then instead of this:
if ( player.card[0] == "diamonds" ) { pb_playerCard1.Image = pb_diamonds.Image; }
You would write:
pb_playerCard1.Image = images[player.card[0]];
This code is still not great (any time you see variables like foo1, foo2, foo3, you should be putting those in an array so they can be indexed by number). The next step might be to refactor the code so you have something like:
pb_playerCard[0].Image = images[player.card[0]];
Or:
pb_playerCard[0].Image = player.card[0].Image;
thanks again guys, I got it working using a List to store card suites for the player objects and a dictionary that stores image references.
Dictionary<CardType, System.Drawing.Image> newDeckImages = new Dictionary<CardType, System.Drawing.Image>();
...
newDeckImages.Add(CardType.Diamonds, pb_diamonds.Image);
newDeckImages.Add(CardType.Hearts, pb_hearts.Image);
newDeckImages.Add(CardType.Clubs, pb_clubs.Image);
newDeckImages.Add(CardType.Spades, pb_spades.Image);
...
private void showMyCards()
{
pb_termCard1.Image = newDeckImages[Terminator.cards[0]];
pb_termCard2.Image = newDeckImages[Terminator.cards[1]];
pb_termCard3.Image = newDeckImages[Terminator.cards[2]];
pb_termCard4.Image = newDeckImages[Terminator.cards[3]];
}
The main class Player and the associated enumeration should be like this:
class Player
{
public Player(int n)
{
Cards = new List(n);
}
public IList Cards { get; private set; }
}
enum CardType
{
Diamond,
Club,
Spade,
Heart
}
Another method GetPictureBox() should in the winform partial class
public PictureBox GetPictureBox(string pictureBoxName)
{
if(this.Controls.ContainsKey(pictureBoxName))
{
Control control = this.Controls[pictureBoxName];
PictureBox pictureBox;
if ((pictureBox = control as PictureBox) != null)
{
return pictureBox;
}
}
return null;
}
Use this method in the class using the player instance.
Player player = new Player(3);
player.Cards.Add(CardType.Diamond);
player.Cards.Add(CardType.Club);
player.Cards.Add(CardType.Spade);
Dictionary dictionary = new Dictionary();
dictionary.Add(CardType.Diamond, pb_diamonds);
dictionary.Add(CardType.Spade, pb_spades);
dictionary.Add(CardType.Club, pb_clubs);
dictionary.Add(CardType.Heart, pb_hearts);
Finally the assignment to the image property,
for (int i = 1; i < 5; i++)
{
string playercardPictureBoxName = string.Concat("pb_playercard", i.ToString());
PictureBox pictureBox = this.GetPictureBox(playercardPictureBoxName);
if (pictureBox != null)
{
pictureBox.Image = dictionary[player.Cards[i - 1]].Image;
}
}
No, not how you're doing it, and although similar things can be achieved with reflection, what you want here is probably closer to just using FindControl for the "pb_playerCard" part and a simple dictionary of type Dictionary<CardSuitEnum, WhateverTheTypeOfImageIs> which you can populate with the pb_diamonds.Image etc.
Apols, I don't know what type .Image holds and can't be bothered to look it up :)

Categories