I am working on a graphing program and I want the user to customize their graph quickly and effectively. I have created a property grid which allows them to do that. I've populated it with the properties of a chart but removes certain elements I don't want the user to have access to. For instance I don't want the user to be able to have access to the accessibility options. So far what I have is
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
magRadioBox.Checked = true;
PropertyGrid propertyGrid1 = new PropertyGrid();
propertyGrid1.CommandsVisibleIfAvailable = true;
propertyGrid1.Text = "Graph and Plotting Options";
propertyGrid1.PropertyValueChanged += propertyGrid1_PropertyValueChanged;
this.Controls.Add(propertyGrid1);
}
private void Form1_Load(object sender, EventArgs e)
{
this.Text = "MY Plot Program";
propertyGrid1.SelectedObject = chart1;
}
private void button1_Click(object sender, EventArgs e)
{//some code that is populating my chart(chart1) with data
.... //chart1 being filled with data
}
private void propertyGrid1_PropertyValueChanged(object s , PropertyValueChangedEventArgs e)
{
//Calling the method that will refresh my chart1
myChart.Invalidate();
}
The above code is for my Form. The my "MyChart" class code below sets up my property grid. I automatically get all of the properties of a chart and then can "cherry pick" the ones I don't want the user to have by setting it to [Browsable(false)]
namespace FFT_Plotter
{
[DefaultPropertyAttribute("Text")]
public class MyChart : Chart
{
public event EventHandler PropertyChanged;
private void OnPropertyChanged(object sender, EventArgs e)
{
EventHandler eh = propertyChanged;
if(eh !=null)
{
eh(sender, e);
}
[BrowsableAttribute(false)]
public new System.Drawing.Color BackColor
{
get { return BackColor; }//Here back color is just an example of a property, not necessarily one that I would make non-Browsable
set {
base.BackColor = value;
OnPropertyChanged(this,EventArgs.Empty);
}
}
}
}
The class above gets me so far as having a property grid that has all the properties of a chart and allows me to hide those properties as I see fit. However now I am stuck in understanding how to connect my chart1 to my property grid. An example would be I've removed the text property from the grid. It's no longer visible to the user. Now I want to be able to say change BackColor in the grid which means my chart1 back color changes.
Ok, I took your code and did the following and now changing the Background Color in the property grid changes the chart's background color:
Form1 -
public partial class Form1 : Form
{
private PropertyGrid propertyGrid1;
public Form1()
{
InitializeComponent();
//propertyGrid1 = new PropertyGrid();
propertyGrid1.CommandsVisibleIfAvailable = true;
propertyGrid1.Text = "Graph and Plotting Options";
propertyGrid1.PropertyValueChanged += propertyGrid1_PropertyValueChanged;
this.Controls.Add(propertyGrid1);
}
private void Form1_Load(object sender, EventArgs e)
{
this.Text = "MY Plot Program";
propertyGrid1.SelectedObject = chart1;
}
private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
{
//Calling the method that will refresh my chart1
chart1.Invalidate();
}
}
[DefaultPropertyAttribute("Text")]
public class MyChart : Chart
{
public event EventHandler PropertyChanged;
private void OnPropertyChanged(object sender, EventArgs e)
{
if(PropertyChanged !=null)
{
PropertyChanged(sender, e);
}
}
[BrowsableAttribute(false)]
public new string Text { get; set; }
[BrowsableAttribute(true)]
public new System.Drawing.Color BackColor
{
get { return base.BackColor; }//Here back color is just an example of a property, not necessarily one that I would make non-Browsable
set
{
base.BackColor = value;
OnPropertyChanged(this,EventArgs.Empty);
}
}
}
Also, in your Form1.Designer.cs file, you have chart1 defined as `System.Windows.Forms.DataVisualization.Charting.Chart you need to change that to be MyChart in the section where it is new'd (need to search for Charting.Chart)
Related
There are two forms, a MainForm and a GraphicsForm.
In MainForm, there are "New" and "Save", "Open" buttons. When clicking the "New", a GraphicsForm created (When the "New" is clicked multiple times, multiple GraphicsForms are created).
The question is, when created multiple GraphicsForms, and the user only wants to save the content in one of them or open a content file to one of them, How to implement this?
MainForm.cs
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private ToolStripMenuItem _winMenuItem = new ToolStripMenuItem();
private GraphicsForm _graphicsForm;
private int _counter = 1;
private ContentDoc _contentDoc = new ContentDoc();
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(_contentDoc);
_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)
{
... // here
}
private void Open_Click(object sender, EventArgs e)
{
... // here
}
}
GraphicsForm.cs
public partial class GraphicsForm : Form
{
//ContentDoc is a class to manage all the graphics drawn by the user in the form.
private ContentDoc _contentDoc = new ContentDoc();
public GraphicsForm(ContentDoc contentDoc)
{
InitializeComponent();
_contentDoc = contentDoc;
}
private Canvas_MouseDown()
{
}
private Canvas_Paint()
{
}
...
The parent form has an ActiveMdiChild property, so you can use the to access the currently-selected GraphicsForm instance:
var activeGraphicsForm = ActiveMdiChild as GraphicsForm;
There are other variations you might use, e.g. pattern matching, depending on the specific details and your preference.
You can then put your saving logic in a public method in GraphicsForm and call it from the parent form. Alternatively, you can put your saving logic in the parent form and expose the data to be saved via one or more public properties in GraphicsForm.
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
I'm working on a class, let's call it MyDataGridView, derived from DataGridView that must be synchronized with the Excel file (i.e. when something changes in the DataGridView, I need to make the same changes to the Excel file).
To detect changes in the DataGridView I use events (for example, RowsAdded, ColumnRemoved, CellValueChanged, etc). But I have a problem with detecting cell whose BackColor have been changed.
The color is changed by the other programmer who uses my class. To do this, he can use the following code:
MyDataGridView myDataGridView;
// create and fill MyDataGridView...
myDataGridView.Rows[0].Cells[0].Style.BackColor = Color.AliceBlue;
My goal is to detect changes of BackColor property to change the Excel file.
To achieve this goal, I (unsuccessfully) tried to use several methods:
CellStyleContentChanged event (problem: can't get the Cell itself from the event handler).
CellFormatting event (problems: event rises so many times and I can't get the reason of its occurrence).
CellStyleChanged event (problem: event only occurs when the Style property changes, but not Style.BackColor).
overriding of DataGridViewCellStyle class (problem: I don't know how to correctly override this class and whether it is possible at all).
Code snippet that will help to reproduce my attempts:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
dataGridView1.CellStyleContentChanged += dataGridView1_CellStyleContentChanged;
dataGridView1.CellFormatting += dataGridView1_CellFormatting;
}
// Goal
private void Form1_Load(object sender, EventArgs e)
{
dataGridView1.Columns.Add("column1", "column1");
dataGridView1.Columns.Add("column2", "column2");
dataGridView1.Rows.Add("cell1", "cell2");
// how to detect this?
dataGridView1.Rows[0].Cells[0].Style.BackColor = Color.AliceBlue;
}
// Attempt #1
void dataGridView1_CellStyleContentChanged(
object sender, DataGridViewCellStyleContentChangedEventArgs e)
{
// how to get cell itself (rowIndex & columnIndex)?
}
// Attempt #2
void dataGridView1_CellStyleChanged(object sender, DataGridViewCellEventArgs e)
{
// event only occurs when the Style property changes, but not Style.BackColor
}
// Attempt #3
void dataGridView1_CellFormatting(
object sender, DataGridViewCellFormattingEventArgs e)
{
// event rises so many times!
// and how to get reason of formatting (i need to detect only color change)?
}
// Attempt #4
// do i need something like this?
public class MyDataGridView : DataGridView
{
public class MyDataGridViewRowCollection : DataGridViewRowCollection
{
public MyDataGridViewRowCollection(DataGridView _dgv) : base(_dgv) { }
public class MyDataGridViewRow : DataGridViewRow
{
public class MyDataGridViewCellCollection : DataGridViewCellCollection
{
public MyDataGridViewCellCollection(DataGridViewRow _dgvRow) :
base(_dgvRow) { }
public class MyDataGridViewCell : DataGridViewCell
{
private new MyDataGridViewCellStyle Style { get; set; }
public class MyDataGridViewCellStyle : DataGridViewCellStyle
{
public new Color BackColor
{
get
{
return base.BackColor;
}
set
{
base.BackColor = value;
// TODO: changes in Excel
// ...
}
}
}
}
}
}
}
}
}
I will be glad to any advice and answers!
Discussion with #TaW prompted me to use several events at once.
We use CellStyleContentChanged event for detect Style.BackColor changes and CellFormatting event to get Cell, whose BackColor have been changed.
Also we need some Collection to store changed colors, because these events don't occur sequentially (see log below).
Remark!! This method only works for user-visible cells (DataGridView should not have scroll bars).
public partial class Form1 : Form
{
string log = "";
List<Color> changedBackColors = new List<Color>();
public Form1()
{
InitializeComponent();
this.dataGridView1.CellStyleContentChanged +=
dataGridView1_CellStyleContentChanged;
this.dataGridView1.CellFormatting +=
dataGridView1_CellFormatting;
this.Shown += Form1_Shown;
}
void Form1_Shown(object sender, EventArgs e)
{
log += "Change Cell[0,0] (color = blue)\r\n";
this.dataGridView1.Rows[0].Cells[0].Style.BackColor = Color.Blue;
log += "Change Cell[0,1] (color = red)\r\n";
this.dataGridView1.Rows[0].Cells[1].Style.BackColor = Color.Red;
}
private void Form1_Load(object sender, EventArgs e)
{
this.dataGridView1.Columns.Add("column1", "column1");
this.dataGridView1.Columns.Add("column1", "column1");
this.dataGridView1.Rows.Add("cell1", "cell2");
}
private void dataGridView1_CellStyleContentChanged(
object sender, DataGridViewCellStyleContentChangedEventArgs e)
{
log += string.Format(
"CellStyleContentChanged occurs for Cell[?,?] (color = {0})\r\n",
e.CellStyle.BackColor.Name);
this.changedBackColors.Add(e.CellStyle.BackColor);
}
private void dataGridView1_CellFormatting(
object sender, DataGridViewCellFormattingEventArgs e)
{
if (this.changedBackColors.Count > 0)
{
if (this.changedBackColors.Contains(e.CellStyle.BackColor))
{
log += string.Format(
"CellFormatting occurs for Cell[{0},{1}] (color = {2})\r\n",
e.RowIndex, e.ColumnIndex, e.CellStyle.BackColor.Name);
this.changedBackColors.Remove(e.CellStyle.BackColor);
// TODO: change excel file
// ...
}
}
}
}
Log:
Change Cell[0,0] (color = blue)
CellStyleContentChanged occurs for Cell[?,?] (color = Blue)
Change Cell[0,1] (color = red)
CellStyleContentChanged occurs for Cell[?,?] (color = Red)
CellFormatting occurs for Cell[0,0] (color = Blue)
CellFormatting occurs for Cell[0,1] (color = Red)
I want to implement a simple feature where user's can customize the Font of labels.
So I have FontEditor form with the following code:
public partial class FontEditor : Form
{
public Font myFont;
public FontEditor(Font myFont)
{
InitializeComponent();
this.myFont = myFont;
propertyGrid1.SelectedObject = this.myFont;
}
private void FontEditor_FormClosing(object sender, FormClosingEventArgs e)
{
DialogResult = DialogResult.OK;
}
}
I use it on a chart control like this:
using (FontEditor fe = new FontEditor(chart1.Titles[0].Font))
{
if (DialogResult.OK == fe.ShowDialog())
{
chart1.Titles[0].Font = fe.myFont;
}
}
When the font editor loads, i can see the following:
If I changed the Size from 18 to 10 and close the window, the SelectedObject (which is my font object from chart title) does not appear to be changing/updating:
Doesn't editing the property grid values should update the SelectedObject?
Okay, I realise the silly mistake. The edits you do on PropertyGrid are actually in propertyGrid1.SelectedObject.
This is how I fixed it:
FontEditor.cs
public partial class FontEditor : Form
{
public FontEditor(Font myFont)
{
InitializeComponent();
propertyGrid1.SelectedObject = myFont;
}
private void FontEditor_FormClosing(object sender, FormClosingEventArgs e)
{
DialogResult = DialogResult.OK;
}
public Font UpdatedFont
{
get { return propertyGrid1.SelectedObject as Font; }
}
}
Usage
using (FontEditor fe = new FontEditor(chart1.Titles[0].Font))
{
if (DialogResult.OK == fe.ShowDialog())
{
chart1.Titles[0].Font = fe.UpdatedFont;
}
}
This now works.
I am creating a graphing program and I would like to allow the user to be able to change the look of the graph they create. Providing them with the opportunity to change the series color, data point size, ect. I am allowing them to do this through the use of a propertyGrid. Through the help of the wonderful people who use stack overflow I was able to import all the properties of a chart into my property grid, however; Now I can't figure out how to connect my chart to the propertyGrid, so when I change something in the grid the chart changes.
So far I have
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
magRadioBox.Checked = true;
PropertyGrid propertyGrid1 = new PropertyGrid();
propertyGrid1.CommandsVisibleIfAvailable = true;
propertyGrid1.Text = "Graph and Plotting Options";
propertyGrid1.PropertyValueChanged += propertyGrid1_PropertyValueChanged;
this.Controls.Add(propertyGrid1);
}
private void Form1_Load(object sender, EventArgs e)
{
this.Text = "MY Plot Program";
propertyGrid1.SelectedObject = chart1;
}
private void button1_Click(object sender, EventArgs e)
{
//some code that is populating my chart(chart1) with data
//....chart1 being filled with data
}
private void propertyGrid1_PropertyValueChanged(object s , PropertyValueChangedEventArgs e)
{
//Getting the MyChart instance from propertyGrid
MyChart myChart = (MyChart)(((PropertyGrid)s.SelectedObject);
//Calling the method that will refresh my chart1
myChart.Invalidate();
}
The above code is for my Form. The my "MyChart" class code is
namespace FFT_Plotter
{
[DefaultPropertyAttribute("Text")]
public class MyChart : Chart
{
public event EventHandler PropertyChanged;
private void OnPropertyChanged(object sender, EventArgs e)
{
EventHandler eh = propertyChanged;
if(eh !=null)
{
eh(sender, e);
}
[BrowsableAttribute(false)]
public new System.Drawing.Color BackColor
{
get { return BackColor; }//Here back color is just an example of a property, not necessarily one that I would make non-Browsable
set
{
base.BackColor = value;
OnPropertyChanged(this,EventArgs.Empty);
}
}
}
}
The class above gets me so far as having a property grid that has all the properties of a chart and allows me to hide those properties as I see fit. However now I am stuck in understanding how to connect my chart1 to the grid I've created. If anyone has any advice on how to do that, it would be incredibly helpful.
You have to add propertyGrid1.SelectedObject = myChartInstance;, then you have to add PropertyValueChanged event listener that will be triggered each time a user changes myChartInstance property via PropertyGrid. So, assuming that you want to redraw the chart each time the change has been done, the code should look like this:
private void propertyGrid1_PropertyValueChanged(object sender, PropertyValueChangedEventArgs e)
{
// Redraw the chart.
chart1.Invalidate();
}
public Form1()
{
InitializeComponent();
magRadioBox.Checked = true;
PropertyGrid propertyGrid1 = new PropertyGrid();
propertyGrid1.CommandsVisibleIfAvailable = true;
propertyGrid1.Text = "Graph and Plotting Options";
// Create your chart.
chart1 = new MyChart();
// Attach your chart to Property Grid.
propertyGrid1.SelectedObject = (MyChart) chart1;
propertyGrid1.PropertyValueChanged += propertyGrid1_PropertyValueChanged;
this.Controls.Add(propertyGrid1);
}
Through the help of the great community on StackOverflow this was the answer I was able to piece together, (standing on the shoulders of giants)
public partial class Form1 : Form {
public Form1()
{
InitializeComponent();
magRadioBox.Checked = true;
PropertyGrid propertyGrid1 = new PropertyGrid();
propertyGrid1.CommandsVisibleIfAvailable = true;
propertyGrid1.Text = "Graph and Plotting Options";
propertyGrid1.PropertyValueChanged += propertyGrid1_PropertyValueChanged;
this.Controls.Add(propertyGrid1);
} private void Form1_Load(object sender, EventArgs e) { this.Text = "MY Plot Program"; propertyGrid1.SelectedObject = chart1; }
private void button1_Click(object sender, EventArgs e) {//some code that is populating my chart(chart1) with data .... //chart1 being filled with data }
private void propertyGrid1_PropertyValueChanged(object s , PropertyValueChangedEventArgs e) {
myChart.Invalidate();
}
This is the code for the MyChart class
namespace FFT_Plotter
{
[DefaultPropertyAttribute("Text")]// This is where the initial position of the grid is set
public class MyChart : Chart
{
public event EventHandler PropertyChanged;
private void OnPropertyChanged(object sender, EventArgs e)
{
EventHandler eh = propertyChanged;
if(eh !=null)
{
eh(sender, e);
}
[BrowsableAttribute(false)]
public new System.Drawing.Color BackColor
{
get { return BackColor; }//Here back color is just an example of a property, not necessarily one that I would make non-Browsable
set {
base.BackColor = value;
OnPropertyChanged(this,EventArgs.Empty);
}
}
}
}
And this declares chart1 to be a MyChart rather than System.Windows...Chart, in the Form1.Designer.CS
...this.chart1 = new FFT_Ploter.MyChart(); ... private FFT_Plotter.MyChart chart1;