I am trying to optimize my code a bit by either using delegates or using a class instance as parameter. I am pretty new to C# and I am not yet sure which one is the better approach assuming im on the right track in the first place. But my problem relates to sending a class instance as parameter. Let me explain. I am trying to follow this logic but im failiing....
I have created a VSTO Ribbon with a few buttons. It looks somewhat like this:
Now, I am now trying to add some functionality to the buttons, so a click on each button opens a new TaskPane.
I wrote this code for the Calendar Ribbon button which sits in the GSMRibbon.cs
note: I think that for the more experienced programmers this code will be quite easy to understand but in case you guys dont understand something please let me know in the comments i will explain ).
namespace GSM
{
public partial class GSMRibbon
{
private void GSMRibbon_Load(object sender, RibbonUIEventArgs
{
}
private CustomTaskPane taskPane;
private CustomTaskPane TaskPane
{
get
{
return this.taskPane;
}
}
private void vendors_calendar_Click(object sender, RibbonControlEventArgs e)
{
string newTitle = "PO Calendar";
if (TaskPane != null)
{
if (TaskPane.Title != newTitle)
{
Globals.ThisAddIn.CustomTaskPanes.Remove(TaskPane);
CreateTaskPane(newTitle);
}
else
{
taskPane.Visible = true;
}
}
else
{
CreateTaskPane(newTitle);
}
}
private void CreateTaskPane(string title)
{
var taskPaneView = new CalendarView();
taskPane = Globals.ThisAddIn.CustomTaskPanes.Add(taskPaneView, title);
taskPane.Visible = true;
}
}
}
Ok. What I wanted to do was to modify the CreateTaskPane function adding a class parameter (does this make sense?) so I can re-use this function multiple times for different buttons on the ribbon. I have created a separate View for each of the buttons, but I am not sure how to pass the View.
So, im after something like this: (CalendarView is the name of the View)
CreateTaskPane(new CalendarView(), newTitle);
and the function something like:
private void CreateTaskPane(object typeOfView, string title)
{
var taskPaneView = new (typeOfView)Object;
taskPane = Globals.ThisAddIn.CustomTaskPanes.Add(taskPaneView, title);
taskPane.Visible = true;
}
I really hope you understand what I am trying to but being unable to do myself. I appreciate any attempt to help. Thanks
You can use generics to do this:
private void CreateTaskPane<T>(string title) where T : UserControl, new()
{
T taskPaneView = new T();
taskPane = Globals.ThisAddIn.CustomTaskPanes.Add(taskPaneView, title);
taskPane.Visible = true;
}
You would then call it via:
CreateTaskPane<CalendarView>(newTitle);
Alternatively, you could write this as:
private void CreateTaskPane<T>(T taskPaneView, string title) where T : UserControl
{
taskPane = Globals.ThisAddIn.CustomTaskPanes.Add(taskPaneView, title);
taskPane.Visible = true;
}
Then call via:
CreateTaskPane(new CalendarView(), newTitle);
What you seem to be looking for is Generics
The function you would end up with is something like:
private void CreateTaskPane<T>(string title) where T : UserControl, new()
{
var taskPaneView = new T();
taskPane = Globals.ThisAddIn.CustomTaskPanes.Add(taskPaneView, title);
taskPane.Visible = true;
}
// Later on..
CreateTaskPane<CalenderTaskPane>("Calender");
Related
First of all i am kinda new to C# but i understand enough to work mostly alone.
Now i have a problem that i have for some time with Windows Forms.
I'm trying to create a new Instance of a class called "FiestaService" which includes some variables (string, string, Label, Button).
I want to insert label1 and loginBtn into FiestaService Login so i can call it inside SCStatusCheck().
With the current code it only tells me
a field initializer cannot reference the non-static field
at the label = label1 and the same for button = loginBtn.
public partial class Main : Form
{
public Main()
{
InitializeComponent();
}
public List<FiestaService> FS = new List<FiestaService>();
public FiestaService Login = new FiestaService() {
serviceDataName = "Crestia_Login",
serviceTextName = "Login",
label = label1, // this is the point where i am stuck with the label1
button = loginBtn // same
};
private void Main_Load(object sender, EventArgs e)
{
label1.Text = Login.serviceTextName;
FS.Add(Login);
//... here are some more of them
}
public void SCStatusCheck() // is called with a 2 seconds Timer inside the Main_Load method
{
foreach(var service in FS) {
ServiceController SC = new ServiceController(service.serviceDataName);
if (SC.Status.Equals(ServiceControllerStatus.Running)) {
service.label.BackColor = Color.LightGreen;
service.button.Text = "Stop";
}
}
}
}
public class FiestaService
{
public string serviceDataName;
public string serviceTextName;
public Label label;
public Button button;
}
The last thing i want to mention is that i did try it with
public static Main main = new Main();
and
public static FiestaService Login = new FiestaService() { ..., label = main.label1, button = main.loginBtn };
but after starting to debug it tells me
The object reference was not set to an object instance.
That "is" working ..kinda.. and i can call them with the Login FiestaService but the SCStatusCheck() method can't change anything related to the Label/Button.
Thanks for everyone! If you see anything that i could do much better than i did feel free to correct me!
..and yes i have already read some posts. However, none of them led to a suitable solution.
I think I see your problem - the labels/buttons are created on this line:
InitializeComponent();
So you should defer creating the FiestaService instances until after this. Maybe like this:
public FiestaService Login;
public Main()
{
InitializeComponent();
Login = new FiestaService() {
serviceDataName = "Crestia_Login",
serviceTextName = "Login",
label = label1, // this is the point where i am stuck with the label1
button = loginBtn // same
};
}
The way you have it, when you do this:
new FiestaService() {
It happens before the labels are created, so all you do is set them to null
Also, think how you can separate the data from the UI. It is not good practice to mix it up like this, but if it works and you are just learning, don't worry too much.
I am having trouble understanding why my databindings do not seem to work with my custom class. I made (hacked) my class extend the Control class to add the databindings functionality but it doesn't actually bind to my custom property.
My code for my custom class is:
public class RadioButtonSet : System.Windows.Forms.Control
{
private Dictionary<System.Windows.Forms.RadioButton, int> buttonList;
private int selectedValue;
public RadioButtonSet()
{
buttonList = new Dictionary<System.Windows.Forms.RadioButton, int>();
}
public void AddButton(System.Windows.Forms.RadioButton button, int buttonValue)
{
if (this.buttonList.ContainsKey(button))
throw new Exception("Button set already contains specified button");
else if (buttonValue <= 0)
throw new Exception("Cannot add specified key to button set");
else if (button == null)
throw new Exception("Parameter button cannot be null");
else
{
button.CheckedChanged += button_CheckedChanged;
this.buttonList.Add(button, buttonValue);
}
}
private void setSelectedButton()
{
this.buttonList.FirstOrDefault(x => x.Value == this.selectedValue).Key.Checked = true;
}
private void button_CheckedChanged(object sender, EventArgs e)
{
System.Windows.Forms.RadioButton btn = sender as System.Windows.Forms.RadioButton;
this.selectedValue = this.buttonList[btn];
}
public int SelectedButton
{
get
{
return selectedValue;
}
set
{
selectedValue = value;
setSelectedButton();
}
}
}
And I try to bind to this class using the following, where rbs_admin is an instance of my custom class:
rbs_admin.DataBindings.Add("SelectedButton", datatable, "admin");
I do not know what information may help so here goes.
I get the information to bind from a datatable which is populated by a data adapter. This custom class is not in it's own file, its part of another static class in my project.
I just dont understand as I created a custom textbox with the same custom property and it binds and works fine.
Any help is much appreciated.
Im talking about something like this:
someListControl.DataSource = datatable;
someListControl.DisplayMember = "someAnotherColumnName"
rbs_admin.DataBindings.Add("SelectedButton", datatable, "admin");
Then, selecting an item from list control will cause your control to update its binding according to the selected item.
So i have 2 forms.
Form 1 is my main form, and form 2 is where I enter text in a textbox to be displayed on a label on form 1. Also the button to 'confirm' and actually change the entered text of my label is on form 2 which needs to stay that way.
for some reason this does not work.
Form 2 has a text-box and a button, when I press the button, it changes the string value of the designated string.
This string is linked to a label on form 1. the string is being changed so that is not the problem I confirmed this by a adding a button which pops up a message box showing the new string value.
While searching for an answer I found that is must be some sort of refreshing problem, I tried a lot of methods with no success. Only methods that did work where those who would put my button onto form 1 instead of 2.
I've been googling for 3 hours straight on how to fix this problem but either the methods don't work or they change my button from form 2 to my main form (form 1).
Please don't call me lazy I really can't find a method that works!
EDIT:
Code
GameScreen.cs
namespace _2pGame
{
public partial class GameScreen : Form
{
public GameScreen()
{
InitializeComponent();
P1NameLabel.Text = gm.P1Name;
P1ClassLabel.Text = gm.P1Class;
P2NameLabel.Text = gm.P2Name;
P2ClassLabel.Text = gm.P2Class;
}
private void PlayerInfoButton_Click(object sender, EventArgs e)
{
PlayerInfo playerinfoload = new PlayerInfo();
playerinfoload.Show();
}
}
}
PlayerInfo.cs
namespace _2pGame
{
public partial class PlayerInfo : Form
{
public PlayerInfo()
{
InitializeComponent();
}
public void ConfirmPlayerInfo_Click(object sender, EventArgs e)
{
gm.P1Class = P1ClassChoice.Text;
gm.P1Name = P1TextBox.Text;
gm.P2Class = P2ClassChoice.Text;
gm.P2Name = P2TextBox.Text;
}
}
}
Refs.cs
namespace _2pGame
{
public partial class gm
{
public static string
P1Class,
P2Class,
P1Name,
P2Name;
}
}
An approach to this very well know situation is through delegates....
In your PlayerInfo form declare
public partial class PlayerInfo : Form
{
// define the delegate type (a parameterless method that returns nothing)
public delegate void OnConfirmPlayer();
// declare a public variable of that delegate type
public OnConfirmPlayer PlayerConfirmed;
.....
public void ConfirmPlayerInfo_Click(object sender, EventArgs e)
{
gm.P1Class = P1ClassChoice.Text;
gm.P1Name = P1TextBox.Text;
gm.P2Class = P2ClassChoice.Text;
gm.P2Name = P2TextBox.Text;
// Check is someone is interested to be informed of this change
// If someone assign a value to the public delegate variable then
// you have to call that method to let the subscriber know
if (PlayerConfirmed != null)
PlayerConfirmed();
}
}
Then in your GameScreen form, just before showing the PlayerInfo form, set the public PlayerInfo.PlayerConfirmed to a method into the GameScreen form class
private void PlayerInfoButton_Click(object sender, EventArgs e)
{
PlayerInfo playerinfoload = new PlayerInfo();
// Subscribe to the notification from PlayerInfo instance
playerinfoload.PlayerConfirmed += PlayerHasBeenConfirmed;
playerinfoload.Show();
}
// Method that receives the notification from PlayerInfo
private void PlayerHasBeenConfirmed()
{
P1NameLabel.Text = gm.P1Name;
P1ClassLabel.Text = gm.P1Class;
P2NameLabel.Text = gm.P2Name;
P2ClassLabel.Text = gm.P2Class;
}
This approach has the advantage to avoid a coupling between the GameScreen and the PlayerInfo. No need to know inside the PlayerInfo the existance of a GameScreen form and the name of its properties. You just publish a delegate that a subscriber could register to be informed of the changes and let the subscriber acts on its own code.
You need a reference to your main form and assign the textbox values each time they need to be updated.
public partial class PlayerInfo : Form
{
private readonly GameScreen _main;
public PlayerInfo(GameScreen main)
{
_main = main;
InitializeComponent();
}
public void ConfirmPlayerInfo_Click(object sender, EventArgs e)
{
gm.P1Class = P1ClassChoice.Text;
gm.P1Name = P1TextBox.Text;
gm.P2Class = P2ClassChoice.Text;
gm.P2Name = P2TextBox.Text;
main.P1NameLabel.Text = gm.P1Name;
main.P1ClassLabel.Text = gm.P1Class;
main.P2NameLabel.Text = gm.P2Name;
main.P2ClassLabel.Text = gm.P2Class;
}
}
You also need to pass the reference when the PlayerInfo form is created
private void PlayerInfoButton_Click(object sender, EventArgs e)
{
PlayerInfo playerinfoload = new PlayerInfo(this); //pass ref to self
playerinfoload.Show();
}
Note that there are other better ways to do this, but this is the easiest that I can think of.
You can probably look at events or Mediator pattern if you want something better.
I'm a beginner at C# who's decided to create a textbased adventure game in a Winform, but I've been struggling with updating the form whenever it needs to update. In the past, I have used something.Invalidate(); to update an image, but apparently that doesn't work for an entire form.
I have a set of labels that display text based on an integer and whenever the value of the integer updates, I'd like the form to show that.
What I have tried thus far:
public partial class GameWindow : Form
{
public void buttonInventory_Click(object sender, EventArgs e)
{
Basic.HP = Basic.HP++;
this.Refresh();
}
}
While the HP updates, the form doesn't show it. Is there anything else I should use than Refresh();? A lot of googling mostly resulted in explanations about Backgroundworkers, but do I really need another thread for something as simple as this?
Why not just make a separate routine for updating values, that you call after every value change. IE: (Note - I don't program in C#):
public partial class GameWindow : Form
{
public void buttonInventory_Click(object sender, EventArgs e)
{
Basic.HP = Basic.HP++;
updateValues();
}
public void updateValues()
{
hp.text = HealthInteger;
basic.text = BasicInteger;
}
}
And call this for evey value change.
A label can't be bind to a string value like an image can be to a picture box.
The simplest solution here is too explicitly set the Text property of the label each time your HP property is changed :
private void RefreshFormWithModel(Basic basic)
{
labelHP.Text = basic.HP;
}
public void buttonInventory_Click(object sender, EventArgs e)
{
Basic.HP = Basic.HP++;
this.RefreshFormWithModel(Basic);
}
If you really want complex binding, here is some lectures.
I'm going to assume the class name of Basic is just Basic
in the class Basic
private int hp;
public int HP
{
get { return hp; }
set { hp = value; HP_Changed(); }
}
public event EventHandler HPChanged;
private void HP_Changed()
{
if (HPChanged != null) { HPChanged(this, new EventArgs()); }
}
in the GameWindow
//where ever you create a new Basic, add to the event handler
Basic Basic = new Basic();
Basic.HPChanged += Basic_HPChanged;
private void Basic_HPChanged(object sender, EventArgs e)
{
Basic b_sender = (Basic)sender;
int NewHealth = b_sender.HP;
//Update whatever value needs to be updated, here
}
Then whenever the HP of the Basic is changed, it will fire an event in the GameWindow to update the appropriate field.
How can I go about accessing the result of an if statement in a user control?
UserControl code:
public bool SendBack(bool huh)
{
if(huh)
huh = true;
else huh = false;
return huh;
}
And in a separate project i am trying to access it like this:
private void button1_Click(object sender, EventArgs e)
{
MyControl.TextControl t = (MyControl.TextCOntrol)sender;
if(t.SendBack(true))
{
// Do something.
}
}
In this case I thing the sender will be the button1, so it will not be castable to your usercontrol...
You will need a reference form the container (form/panel/...) that contains your usercontrol.
Also, I know this might be for simplicity but you can change
public bool SendBack(bool huh)
{
if(huh)
huh = true;
else huh = false;
return huh;
}
to
public bool SendBack(bool huh)
{
return huh;
}
You might also want to take a look at Control.ControlCollection.Find Method
Searches for controls by their Name
property and builds an array of all
the controls that match.