Updating a pictureBox.Image from another class - c#

Update: I am pretty sure i found out the reason why, i will update this question with my findings as soon as i got it for sure. It is related to having 2 usercontrol's placed above each other and i draw on the one behind thats why i don't see the changes! I had to check the hashcode of the timer to figure out that 2 timers are involved.
I am developing a winforms application and i have applied the MVP-pattern to separate the responsibilities and stuff... (applied the passive-view approach, and also fire events to require the corresponding presenter to perform an action).
I have also checked other questions here and many other relevant topics, but nothing helped...
Problem description: I added a UserControl (SubView) into a SplitContainer's panel which is placed inside a Form (MainView). This SubView has its own presenter (SubPresenter) that requires it to switch its background (SwitchBackground(), picturebox) whenever that is triggered by the user from the MainView.
Problem: The SwitchBackground() method gets executed (i debugged that) when that is triggered from the MainView (actually from the MainPresenter), but the changes are not displayed on the SubView. I have also examined (or at least tried to do that correctly :) ) if the method requires switching the context into the GUI thread or something by checking the InvokeRequired.
I would be glad to have any recommendations regarding this issue, because i am stuck there and can't go further with programming...
Here is an example application i wrote to illustrate the problem (for the complete example project, i uploaded it on github: link):
SubView:
public interface ISubView
{
void StartSwitchingBackground();
void StopSwitchingBackground();
}
public partial class SubView : UserControl, ISubView
{
private Bitmap plot;
private Brush brush1;
private Brush brush2;
private int drawCount;
private Timer timer;
public SubView()
{
InitializeComponent();
brush1 = new SolidBrush(Color.Yellow);
brush2 = new SolidBrush(Color.Blue);
timer = new Timer();
timer.Tick += OnTick;
}
private void OnTick(object sender, EventArgs e)
{
SwitchBackground();
}
private void SwitchBackground()
{
if (InvokeRequired)
Console.WriteLine("InvokeRequired"); // never happens, so i assume it is not considered a call from another thread...
if (plot == null || plot.Size != pictureBox.Size)
plot = new Bitmap((int)(pictureBox.Width*0.8), (int)(pictureBox.Height*0.8));
using (Graphics g = Graphics.FromImage(plot))
{
int x = plot.Width / 2;
int y = plot.Height / 2;
int w = plot.Width / 4;
int h = plot.Height / 4;
if (drawCount % 2 == 0)
{
g.Clear(Color.White);
g.FillRectangle(brush1, (x - w) / 2, (y - h) / 2, w, h);
}
else
{
g.Clear(Color.Black);
g.FillRectangle(brush2, (x - w) / 2, (y - h) / 2, w, h);
}
drawCount++;
}
pictureBox.Image = plot;
}
public void StartSwitchingBackground()
{
if (!timer.Enabled)
timer.Start();
Console.WriteLine("started");
}
public void StopSwitchingBackground()
{
if (timer.Enabled) timer.Stop();
Console.WriteLine("stopped");
}
private void btnStartSwitching_Click(object sender, EventArgs e)
{
StartSwitchingBackground();
}
private void btnStopSwitching_Click(object sender, EventArgs e)
{
StopSwitchingBackground();
}
}
SubPresenter:
public class SubPresenter
{
private ISubView view;
public SubPresenter(ISubView view)
{
this.view = view;
}
public void SwitchBackground()
{
view.StartSwitchingBackground();
}
public void StopSwitching()
{
view.StopSwitchingBackground();
}
}
MainView:
public interface IMainView
{
ISubView SubView { get; }
event EventHandler SwitchEventTriggered;
event EventHandler StopSwitchEventTriggered;
}
public partial class MainView : Form, IMainView
{
private SubView subView;
public MainView()
{
InitializeComponent();
subView = new SubView();
splitContainer1.Panel2.Controls.Add(subView);
subView.Dock = DockStyle.Fill;
subView.Anchor = AnchorStyles.Top & AnchorStyles.Left;
splitContainer1.Resize += splitContainer1_Resize;
}
void splitContainer1_Resize(object sender, EventArgs e)
{
subView.Size = splitContainer1.Panel2.Size;
}
private void btnStart_Click(object sender, EventArgs e)
{
StartSwitching();
}
private void StartSwitching()
{
OnStartSwitchingTriggered();
}
private void OnStartSwitchingTriggered()
{
var handler = SwitchEventTriggered;
if (handler == null) return;
handler(this, EventArgs.Empty);
}
private void btnStop_Click(object sender, EventArgs e)
{
StopSwitching();
}
private void StopSwitching()
{
OnStopSwitching();
}
private void OnStopSwitching()
{
var handler = StopSwitchEventTriggered;
if (handler == null) return;
handler(this, EventArgs.Empty);
}
public ISubView SubView { get { return this.subView; } }
public event EventHandler SwitchEventTriggered;
public event EventHandler StopSwitchEventTriggered;
}
MainPresenter:
public class MainPresenter
{
private readonly IMainView mainView;
private readonly SubPresenter subPresenter;
public MainPresenter(IMainView view)
{
this.mainView = view;
this.subPresenter = new SubPresenter(mainView.SubView);
mainView.SwitchEventTriggered += OnSwitchEventTriggerd;
mainView.StopSwitchEventTriggered += OnStopSwitchEventTriggered;
}
private void OnStopSwitchEventTriggered(object sender, EventArgs e)
{
subPresenter.StopSwitching();
}
private void OnSwitchEventTriggerd(object sender, EventArgs e)
{
subPresenter.SwitchBackground();
}
}
Entry Point:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainView mainView = new MainView();
MainPresenter mainPresenter = new MainPresenter(mainView);
Application.Run(mainView);
}
}

You have two instances of SubView in MainView.
Use only subView1, you can delete all occurrences of subView;
The minimum fix is to replace:
public ISubView SubView { get { return this.subView; } }
with:
public ISubView SubView { get { return this.subView1; } }

Related

C#-Winforms-How to use instance objects in different subforms?

I have a "MainForm" and a "GraphicsForm". Clicking "New" on the main form, a "GraphicsForm" will be created.
The problem is that when I create multiple "GraphicsForm", and when I want to save the content of one of the "GraphicsForm", I need to clicking "Save" on the "MainForm" and the program will save the content of the active "GraphicsForm" to a file, I don't know how to pass the content of this "GraphicsForm" to "MainForm" for storage.
MainForm.cs
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private ToolStripMenuItem _winMenuItem = new ToolStripMenuItem();
private GraphicsForm _graphicsForm;
private int _counter = 1;
private void New_Click(objec sender, EventArgs e)
{
_winMenuItem.Name = "Win";
_winMenuItem.Text = "Windows";
int item = MainMenuStrip.Items.IndexOf(_winMenuItem);
if (item == -1)
{
MainMenuStrip.Items.Add(_winMenuItem);
MainMenuStrip.MdiWindowListItem = _winMenuItem;
}
_graphicsForm = new GraphicsForm();
_graphicsForm.Name = string.Concat("Win_", _counter.ToString());
_graphicsForm.Text = _graphicsForm.Name;
_graphicsForm.MdiParent = this;
_graphicsForm.Show();
_graphicsForm.WindowState = FormWindowState.Maximized;
_counter++;
}
private void Save_Click(object sender, EventArgs e)
{
... // Problem here
}
private void Open_Click(object sender, EventArgs e)
{
... // Problem here
}
}
GraphicsForm.cs
public partial class GraphicsForm : Form
{
//StorageDoc is a class to manage all the graphics drawn by the user in the form.
private StorageDoc _storageDoc = new StotageDoc();
public GraphicsForm()
{
InitializeComponent();
}
private Canvas_MouseDown()
{
}
private Canvas_Paint()
{
}
...
Because MainForm is a MDI form, it is easy to use ActiveMdiChild to get the active child form.
class MainForm : Form
{
public void OnSaveButtonClick(object sender, EventArgs e)
{
if(ActiveMdiChild is GraphicsForm g)
Save(g);
}
}
I'm sure this has been answered before but basically, you pass in an instance of the 'data storage' to the new form.
interface ISaveForm
{
void Save();
}
class MainForm
{
private DataStorage _dataStorage;
private ICollection<ISaveForm> _forms = new List<ISaveForm>();
public void OnNew()
{
var subForm = new GraphicsForm(_dataStorage);
subForm.Show();
_forms.Add(subForm);
}
public void OnSave()
{
foreach(var form in _forms)
{
form.Save();
}
}
}
class GraphicsForm : Form,ISaveForm
{
private DataStorage _dataStorage;
public GraphicsForm(DataStorage dataStorage)
{
_dataStorage = dataStorage;
}
public void Save()
{
}
}

How to use an event between 2 classes

I have an class with an event, then in another .cs file i have another class where I subscribe to the event. But the event is never successfully triggered, and for some reason the event is null. What am I doing wrong?
First class:
class TestClass
{
public static void CountStart()
{
int CountVal = 0;
do
{
CountVal = CountVal + 1;
if (CountVal % 5 == 0)
{
SimpleEventSender EventSender = new SimpleEventSender();
EventSender.StartEvent();
}
Thread.Sleep(1000);
} while (CountVal < 100);
}
}
The event class:
public class SimpleEventSender
{
public event EventHandler NewEvent;
public void StartEvent()
{
if (NewEvent != null)
{
NewEvent(this,null);
}
}
}
And the class where I subscribe to the event:
public partial class Form1 : Form
{
public Form1()
{
SimpleEventSender newevent1 = new SimpleEventSender();
newevent1.NewEvent += new_event;
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
TestClass class1 = new TestClass();
TestClass.CountStart();
}
public void new_event(object sender, EventArgs e)
{
MessageBox.Show("multiple of 5 reached");
}
}
I have tried following the answers at "Notify when event from another class is triggered" but it didnt seem to work.
The event handlers are associated with an instance of the class (SimpleEventSender in this case).
You're creating multiple SimpleEventSender instances:
One in the Form1 constructor, where you subscribe to the event
One every 5 iterations of CountStart, where you raise the event - but on a new instance of SimpleEventSender, that doesn't have any subscribers
You almost certainly want to use a single instance of SimpleEventSender, e.g.
// The form now retains a reference to the instance of SimpleEventSender
public partial class Form1 : Form
{
private readonly SimpleEventSender eventSender;
public Form1()
{
eventSender = new SimpleEventSender();
eventSender.NewEvent += new_event;
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
TestClass class1 = new TestClass();
TestClass.CountStart(eventSender);
}
public void new_event(object sender, EventArgs e)
{
MessageBox.Show("multiple of 5 reached");
}
}
// TestClass now *accepts* an EventSender rather than creating its own instances
class TestClass
{
public static void CountStart(SimpleEventSender eventSender)
{
// Variable name modified to be more conventional
// A "for" loop would be more idiomatic too
int count = 0;
do
{
count++;
if (count % 5 == 0)
{
eventSender.StartEvent();
}
Thread.Sleep(1000);
} while (count < 100);
}
}
That's because you're assigning this function to the different event of instance of SimpleEventSender.
public Form1()
{
SimpleEventSender newevent1 = new SimpleEventSender();
// You are subscribing to the event of this instance
newevent1.NewEvent += new_event;
InitializeComponent();
}
-
public static void CountStart()
{
int CountVal = 0;
do
{
CountVal = CountVal + 1;
if (CountVal % 5 == 0)
{
// But here you are creating another instance of SimpleEventSender so it doesn't have anything subscribed to it
SimpleEventSender EventSender = new SimpleEventSender();
EventSender.StartEvent();
}
Thread.Sleep(1000);
} while (CountVal < 100);
}
In other words - you are subscribing to one object in the Form1() constructor and you are calling "StartEvent()" function from totally different object in CountStart() function.

c# change panel color from another form

I'm stuck with the following thing.
I want to change the panel color from another Form(ColorForm).
Is it possible?
Code From the MainForm:
public void upperpanel_Paint(object sender, PaintEventArgs e)
{
}
I don't know how to access that upperpanel_Paint in my ColorForm.
I'm opening ColorForm From SettingsForm
Mainform > SettingsForm > ColorForm
public partial class SettingsForm : Form
{
public static event ColourSettingChangedDelegate ColourSettingsChangedEvent;
public delegate void ColourSettingChangedDelegate(Color color);
List<string> adreses;
List<string> bookmarki;
void SelectColour()
{
using (ColorForm colourForm = new ColorForm())
{
if (colourForm.ShowDialog() == DialogResult.OK)
{
//Update colour setting and fire event
OnColourSettingsChanged(colourForm.SelectedColor);
}
}
}
public SettingsForm(List<string> adr, List<string> s)
{
InitializeComponent();
adreses = adr;
bookmarki = s;
}
private void Historyb_Click(object sender, EventArgs e)
{
foreach (Form form in Application.OpenForms)
{
if (form.GetType() == typeof(HistoryForm))
{
form.Activate();
return;
}
}
HistoryForm hf1 = new HistoryForm(adreses);
hf1.Show();
}
private void Bookmarksb_Click(object sender, EventArgs e)
{
BookmarksForm booklist = new BookmarksForm();
booklist.SetAllBookmarks(bookmarki);
booklist.ShowDialog();
}
private void Colorb_Click(object sender, EventArgs e)
{
SelectColour();
}
private void OnColourSettingsChanged(Color color)
{
if (ColourSettingsChangedEvent != null)
ColourSettingsChangedEvent(color);
}
}
Code from ColorForm:
public partial class ColorForm : Form
{
public ColorForm()
{
InitializeComponent();
}
private void Panelcolor_Click(object sender, EventArgs e)
{
ColorDialog colorDlg = new ColorDialog();
colorDlg.AllowFullOpen = true;
colorDlg.AnyColor = true;
if (colorDlg.ShowDialog() == DialogResult.OK)
{
upperpanel.BackColor = colorDlg.Color;
}
}
}
Thank you!
Perhaps it would be better to fire a global event when the settings change, or the particular form colour setting changes, and listen for that event on the form where you need to take action.
for example see this pseudo code:
ColourForm:
Form used just to pick a colour, and store the result in a property.
class ColourForm
{
public Color SelectedColor {get;set;}
private void Panelcolor_Click(object sender, EventArgs e)
{
ColorDialog colorDlg = new ColorDialog();
colorDlg.AllowFullOpen = true;
colorDlg.AnyColor = true;
if (colorDlg.ShowDialog() == DialogResult.OK)
{
this.SelectedColor = colorDlg.Color;
}
}
void Cancel()
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
void Save()
{
this.DialogResult = DialogResult.OK;
this.Close();
}
}
SettingsForm:
The main form for updating settings, this creates a colour picker form and saved the settings and fires an event if the dialog result of the colour form is an 'ok' result.
class SettingsForm
{
public static event ColourSettingChangedDelegate ColourSettingsChangedEvent;
public delegate void ColourSettingChangedDelegate(Color color);
void SelectColour()
{
using (ColourForm colourForm = new ColourForm())
{
if (colourForm.ShowDialog() == DialogResult.OK)
{
//Update colour setting and fire event
OnColourSettingsChanged(colourForm.SelectedColour);
}
}
}
private void OnColourSettingsChanged(Color color)
{
if (ColourSettingsChangedEvent!=null)
ColourSettingsChangedEvent(color);
}
}
On you Main Form:
The Main form listens for a settings / colour changed event and changes the panel colour to the colour specified in the event when it fires.
class MainForm()
{
//Constructor
MainForm()
{
SettingsForm.ColourSettingsChangedEvent += ColourSettingsChanged;
}
void ColourSettingsChanged(Color color)
{
upperpanel.BackColor = color;
}
}
it would be better to have some kind of settings manager class than have the event on the settings form itself but you should get the idea

Delegate doesn't notify the method

Would you look at my code and tell me where I went wrong? in following code I am trying to send a notification to myMethod() method when Form1 gets maximized.
Thanks!
namespace WindowsDelegate1
{
public delegate void ChangedEventHandler(object sender, EventArgs e);
class myForm : Form
{
public event ChangedEventHandler Changed;
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
Changed(this,e);
}
public override System.Drawing.Size MaximumSize
{
//get
//{
// return base.MaximumSize;
//}
set
{
base.MaximumSize = value;
OnChanged(EventArgs.Empty);
}
}
}
}
namespace WindowsDelegate1
{
class EventListener
{
private myForm TheForm;
public EventListener(myForm theform)
{
TheForm = theform;
TheForm.Changed += new ChangedEventHandler(myMethod);
}
private void myMethod(object sender, EventArgs e)
{
MessageBox.Show("hey, window should be maximized now!");
}
public void Detach()
{
TheForm.Changed -= new ChangedEventHandler(myMethod);
TheForm = null;
}
}
}
Here is the testing unit / or main()
namespace WindowsDelegate1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
myForm f = new myForm();
EventListener listener = new EventListener(f);
f.ShowDialog();
f.WindowState = FormWindowState.Maximized;
listener.Detach();
}
}
}
What's probably happening is the event is either fired after your .Detach() call, or is never fired at all. I would start by removing the listener.Detach() call. Generally, you attach to events when the form is created or when it loads and detach when it is unloading.
Other than that, your Detach method is problematic because it tries to remove a different ChangedEventHandler instance than the one added. If you're wrapping your methods in ChangedEventHandler you need to store the instance you added.
Thank you for sharing your ideas!
I fixed it by removing the property (not idea why I used that!!) and using method instead by:
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
OnChanged(EventArgs.Empty);
}
I have updated my source code above too

Delegates and events?

This question is related to c#.The scenario is that when i click the button the operations like File reading,Data manipulation ,and file dumping are going to be happened.After the completion of each operation i will update the status(i.e,File reading completed,data manipulation completed) in the label which is in UI(FORM-frmTesting)
The button click event is
namespace frmTesting
{
public partial class Form1 : Form
{
private void button1_Click_1(object sender, EventArgs e)
{
class1 l_objClass1 = new class1();
l_objClass1.DoOperation();
}
}
public class class1
{
public int DoOperation()
{
ReadTextFile();
ParsingData();
SaveTextFile();
return 0;
}
private int ReadTextFile()
{
//Read the text File
return 0;
}
private int ParsingData()
{
// Data manipulation
return 0;
}
private int SaveTextFile()
{
// save the file
return 0;
}
}
}
Is it possible by using Delegates and Events....if you have any queries plz revert back me
You'll have to modify class1 to broadcast events that other classes can listen to:
public class class1
{
// Not necessary, but will allow you to add custom EventArgs later
public delegate void StatusChangedEventHandler(object sender, EventArgs e);
public event StatusChangedEventHandler FileRead;
public event StatusChangedEventHandler FileParsed;
public event StatusChangedEventHandler FileSaved;
public int DoOperation()
{
ReadTextFile();
ParsingData();
SaveTextFile();
return 0;
}
private int ReadTextFile()
{
//Read the text File
OnFileRead(EventArgs.Empty);
return 0;
}
private int ParsingData()
{
// Data manipulation
OnFileParsed(EventArgs.Empty);
return 0;
}
private int SaveTextFile()
{
// save the file
OnFileSaved(EventArgs.Empty);
return 0;
}
protected virtual void OnFileRead(EventArgs e)
{
if(FileRead != null)
FileRead(this, e);
}
protected virtual void OnFileParsed(EventArgs e)
{
if(FileParsed != null)
FileParsed(this, e);
}
protected virtual void OnFileSaved(EventArgs e)
{
if(FileSaved != null)
FileSaved(this, e);
}
}
And then have your form listen for those events and change its label appropriately:
public partial class Form1 : Form
{
private void button1_Click_1(object sender, EventArgs e)
{
class1 l_objClass1 = new class1();
l_objClass1.FileRead +=
delegate { lblStatus.Text = "File Read..."; };
l_objClass1.FileParsed +=
delegate { lblStatus.Text = "File Parsed..."; };
l_objClass1.FileSaved +=
delegate { lblStatus.Text = "File Saved..."; };
l_objClass1.DoOperation();
}
}
The short answer is yes. You add events to class1 and add handlers to Form1 with the appropriate logic. Below is a sample of how to do this
public partial class Form1 : Form
{
private void button1_Click_1(object sender, EventArgs e)
{
class1 obj = new class1();
obj.FileReadingComplete += HandleFileReadingComplete;
obj.DataManipulationComplete += HandleDataManipulationComplete;
obj.DoOperation();
obj.FileReadingComplete -= HandleFileReadingComplete;
obj.DataManipulationComplete -= HandleDataManipulationComplete;
}
private void HandleFileReadingComplete(object sender, EventArgs args){
//code here
}
private void HandleDataManipulationComplete(object sender, EventArgs args)
{
//code here
}
}
public class class1
{
public event EventHandler FileReadingComplete;
public event EventHandler DataManipulationComplete;
public int DoOperation()
{
ReadTextFile();
OnFileReadingComplete();
ParsingData();
OnDataManipulationComplete();
SaveTextFile();
return 0;
}
private int ReadTextFile()
{
//Read the text File
return 0;
}
private int ParsingData()
{
// Data manipulation
return 0;
}
private int SaveTextFile()
{
// save the file
return 0;
}
public void OnFileReadingComplete()
{
EventHandler handler = FileReadingComplete;
if (handler != null) {
handler(this, EventArgs.Empty);
}
}
public void OnDataManipulationComplete()
{
EventHandler handler = DataManipulationComplete;
if (handler != null) {
handler(this, EventArgs.Empty);
}
}
}

Categories

Resources