In developing a mod for Cities: Skylines, I have run across a problem.
Most of my code, as far as I can tell, works fine - but so far, I haven't got to test it. This is because it should all get called in sequence when a button I have added to the UI gets clicked. The click event on this button is not calling the handler I've assigned to it.
This is where I create the button:
public class LoadingExtension : LoadingExtensionBase
{
public override void OnLevelLoaded(LoadMode mode)
{
Debug.LogDebugMessage("Adding 'Generate City Report' button to UI");
// Get the UIView object. This seems to be the top-level object for most
// of the UI.
var uiView = UIView.GetAView();
// Add a new button to the view.
var button = (UIButton)uiView.AddUIComponent(typeof(UIButton));
// Set the text to show on the button.
button.text = "Generate City Report";
// Set the button dimensions.
button.width = 250;
button.height = 30;
// Style the button to look like a menu button.
button.normalBgSprite = "ButtonMenu";
button.disabledBgSprite = "ButtonMenuDisabled";
button.hoveredBgSprite = "ButtonMenuHovered";
button.focusedBgSprite = "ButtonMenuFocused";
button.pressedBgSprite = "ButtonMenuPressed";
button.textColor = new Color32(255, 255, 255, 255);
button.disabledTextColor = new Color32(7, 7, 7, 255);
button.hoveredTextColor = new Color32(7, 132, 255, 255);
button.focusedTextColor = new Color32(255, 255, 255, 255);
button.pressedTextColor = new Color32(30, 30, 44, 255);
// Enable button sounds.
button.playAudioEvents = true;
// Place the button.
button.transformPosition = new Vector3(-1.0f, 0.97f);
// Respond to button click.
// NOT GETTING CALLED
button.eventClick += ButtonClick;
}
public void ButtonClick(UIComponent component, UIMouseEventParameter eventParam)
{
Debug.LogWarningMessage("HIGH LOGIC: Beginning report generation.");
string now = DateTime.Now.ToFileTime().ToString();
string filepath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string filename = filepath + "\\CityReport-" + now + ".xlsx";
Output fileCreator = new Output(filename);
fileCreator.GenerateReport();
}
}
According to the documentation I've been following, using
button.eventClick += ButtonClick;
should add ButtonClick as the click handler for the button. However, clicking on the button does nothing. The debug message at the start of the handler (the "HIGH LOGIC" message) doesn't get displayed (note: the earlier debug message about adding the button to the UI does). No error messages are displayed either in the game's debug panel or in VS.
I have also tried using new MouseEventHandler(ButtonClick), since the VS inline documentation tells me that the type of eventClick is MouseEventHandler. This doesn't show any errors in VS or the game, but doesn't work either.
(Note: there is official documentation, but it's next to useless.)
Does anyone here have experience with the C:S API? Why is this event not getting called?
You might try taking a small step back to check if a different UI element works; for example, by following the example for adding a UILabel. Registering a click event handler to this one possibly might work, since there's an actual example to follow; although, the documentation says to place this code inside the Start method. I'm not sure, but maybe the UIButton in your code should be placed in a Start method as well.
As an aside, I stumbled upon this link for debugging. Skylines seems to have its own messaging system for debugging.
Something that I did notice in your code is that the first Debug statement uses a different method than the second Debug statement:
Debug.LogDebugMessage("Adding 'Generate City Report' button to UI");
Debug.LogWarningMessage("HIGH LOGIC: Beginning report generation.");
It might not make any difference, but I would test out that Debug.LogWarningMessage call by moving it where the Debug.LogDebugMessage is to see if it actually works.
According to the extremely terse documentation there's a log file called output_log.txt, perhaps there might be some info contained within this file.
Have you tried:
//button.eventClick += ButtonClick;
button.eventClick += (c,e) => {
Debug.LogWarningMessage("HIGH LOGIC: Beginning report generation.");
string now = DateTime.Now.ToFileTime().ToString();
string filepath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string filename = filepath + "\\CityReport-" + now + ".xlsx";
Output fileCreator = new Output(filename);
fileCreator.GenerateReport();
};
Also have you tried doing this?:
//var button = (UIButton)uiView.AddUIComponent(typeof(UIButton));
var button = uiView.AddUIComponent(typeof(UIButton)) as UIButton
(Sorry, i can't coment yet)
It looks like your click handler, as such, is OK. Change your ButtonClick method to:
public void ButtonClick(UIComponent component, UIMouseEventParameter eventParam)
{
Debug.LogWarningMessage("HIGH LOGIC: Beginning report generation.");
}
and then check the debug output. If you have a bug after that line, then the debug message may not be visible.
Related
ASP.NET 4.7.2 Web Forms c# VS 2019
I am trying to use a modalpopupextender to prompt for new data for foreign key fields. Like the form itself, the MPE is built on the fly in code -- in this case the click handler for the hidden button that the Javascript fires off to build and show the MPE.
I read every single article on SO and the ASP forums and tried everything I saw there. No joy. I get the popup perfectly. Hitting OK closes the popup, but never fires the OK Event.
Here is the code:
//Building the form, we do this in OnInit:
// AJAX Update Panel
UpdatePanel PUP = new UpdatePanel()
{
ID = "PUP",
};
PlaceHolder.Controls.Add(PUP);
// HiddenField containing the field name to permit
// creating the correct modalpopup.
HiddenField HFPopupField = new HiddenField()
{
ID = "HF_POPUP"
};
PUP.ContentTemplateContainer.Controls.Add(HFPopupField);
// Create Hidden button to track the popup
Button BPopup = new Button()
{
ID = "BPOPUP",
UseSubmitBehavior = false
};
BPopup.Click += BPopup_Click;
BPopup.Attributes.Add("style", "display: none;");
PUP.ContentTemplateContainer.Controls.Add(BPopup);
// And create the background panel for the popup.
Panel PnlPopup = new Panel()
{
ID = "PNLPOPUP",
CssClass = "MpeBackground"
};
PnlPopup.Attributes.Add("style", "display: none;");
PUP.ContentTemplateContainer.Controls.Add(PnlPopup);
/// Event handler for hidden button.
protected void BPopup_Click(object sender, EventArgs e)
{
[snip -- code to get the dataset that is being filled]
UpdatePanel PUP = Placeholder.FindControlRecursive("PUP");
Table T = new Table()
{
CssClass = "PopupTbl"
};
TableRow TRTitle = new TableRow();
TableCell TCTitle = new TableCell()
{
CssClass = "PopupTitle",
ColumnSpan = 2
};
Label LPopTitle = new Label()
{
Text = [title of the popup]
};
TCTitle.Controls.Add(LPopTitle);
TRTitle.Cells.Add(TCTitle);
DataRow drData = null;
// Add Fields, and also the cancel and Add buttons
foreach (DataColumn DC in dsColumns.Tables[0].Columns)
{
TableRow TRColumn = [create a tablerow with 2 columns, a prompt and the input field]
if (TRColumn != null)
{
T.Rows.Add(TRColumn);
[snip]
}
} // end of foreach(DataColumn DC in dsColumns.Tables[0].Columns)
PnlWindow.Controls.Add(T);
TableRow TRButtons = new TableRow();
TableCell TCButtons = new TableCell()
{
ColumnSpan = 2,
CssClass="PopupButtons"
};
Button MPEBOK = new Button()
{
ID = "MPE" + sFieldName + "_MPEBOK",
Text = "OK",
CausesValidation = false,
UseSubmitBehavior = false
};
MPEBOK.Click += MPEBOK_Clicked;
TCButtons.Controls.Add(MPEBOK);
LiteralControl LCB = new LiteralControl()
{
Text = " "
};
TCButtons.Controls.Add(LCB);
//************************************************************
//*** Postback Trigger ***
//************************************************************
AsyncPostBackTrigger trigger = new AsyncPostBackTrigger()
{
ControlID = MPEBOK.ID,
EventName = "click"
};
PUP.Triggers.Add(trigger);
//************************************************************
//*** Cancel Button ***
//************************************************************
Button MPEBuhBye = new Button()
{
ID = "MPE" + sFieldName + "_BUHBYE",
Text = "Cancel",
UseSubmitBehavior = false
};
TCButtons.Controls.Add(MPEBuhBye);
TRButtons.Cells.Add(TCButtons);
T.Rows.Add(TRButtons);
PnlPopup.Controls.Add(PnlWindow);
AjaxControlToolkit.ModalPopupExtender MPE = new AjaxControlToolkit.ModalPopupExtender()
{
ID = "MPE" + sFieldName,
PopupControlID = "PNLPOPUP",
TargetControlID = "BPOPUP",
BackgroundCssClass = "MpeBackground"
};
// Add the MPE to the UpdatePanel.
PUP.ContentTemplateContainer.Controls.Add(MPE);
// Show the modal popup extender.
MPE.Show();
}
protected void MPEBOK_Clicked(object sender, EventArgs e)
{
[snip - this never fires]
}
I cannot find out what is happening here. Can anyone see something hinky?
Thanks
John.
You can't add a server side button or inject a server side button into the page DOM.
When you drag a asp.net button onto the form, BOTH the "mypage.cs" and mypage.desinger.cs ARE updated. The wire up of the button occurs at design time, and you would have to modify mypage.desinger.cs ALSO and ADD a button event stub.
So you can't do this.
A compromise would be to also add some js and have that HTML button execute a .click() method of a hidden asp.net button you drop into that page (that would give you the post back, and the running behind of a separate button event code stub.
This event resolution occurs at compile time - not at page render time. You have to drop that button onto the page.
I suppose you could adopt a standard that you always place right below that "div" on the page the button (hidden with style=none. And then as noted, have your injected code along with some js execute a click on the hidden button. Or just have the js button code execute a __doPostback("some value") and pick this up in the page on-load event, and then call the routine (function) from on-page load event.
I think better would be to use a jQuery.UI dialog, as that dialog CAN say load + use another different web page into a “div” on the existing page. So you layout, make, and create the nice looking popup form as a separate web page. jQuery is able to remove the “form” and additonal tags out of that page load, and then inject it into the existing page. (that code would be rather hard to re-produce). so jQuery.UI is able to pop up that separate page. however, the buttons on that loaded page (into that div) of course can't really run any code behind in the current page. However, the buttons CAN run local js in the current page. Thus the actions of this injected page would be local to each page. But the popup would not be directly calling a code behind stub.
Now, to adopt jQuery.UI, then you also have to of course adopt jQuery. So that is two extra libraries you need. (but, jQuery you likely already have).
However, I suppose the whole point of using the ajax toolkit is to avoid jQuery.ui in the first place. To be fair, before jQuery.ui came along, that tool kit was REALLY impressive, and gave asp.net folks a REAL leg up on the competition. (and it tends to be MUCH less wiring up then say using jQuery.UI
So the AjaxToolkit in its heyday was impressive. Now, it of course showing its age, but I still use the kit, and this is especially the case for the AjaxFileUploader. And yes I do use the popups – even to this day. However, I find now that jQuery.UI dialogs are more flexible, and would be better in this case (because you want a on-the fly setup).
Also, having code behind buttons in even the jQuery.UI dialog, or in this case the ajax popup? Well, only the action button can run code behind. The cancel button of course will just dismiss the dialog. However, any button in the dialog that WILL run code behind? Well, that's ok, since you have a page post back, and it actually the page postback that BLOWS out the dialog anyway.
I have a C# project in MonoDevelop.
Long story short is I found I wasnt able to apply styling/pango/markup/images if I used the Button with Label in the Stetic GUI.
The documentation i read and some code I see said to make a Label or Image and pack it into the Button.
I did that for a Label and it worked successfully to style it.
A sample of the object Build method:
private void Build()
{
box = new HBox(false, 0);
box.SetSizeRequest(40, 40);
box.BorderWidth = 2;
button = new Button();
button.Clicked += cyclepoint;
lblpoints = new Label();
lblpoints.Text = "200";
lblpoints.ModifyFg(Gtk.StateType.Normal, new Gdk.Color(237, 52, 112));
button.Add(lblpoints);
button.ShowAll();
box.Add(this.button);
box.ShowAll();
this.Add(box);
}
So that is fine. Now I'm trying to attach the Signal so that whenever the Button is click it would change the Label Text for the particularly clicked button (as there will be multiple of the form.
Code I have for the signal:
private void cyclepoint(object sender, EventArgs args)
{
Console.WriteLine("Button Pressed!");
Console.WriteLine((Label)(((Button)sender).Child).Text);
}
Actual Output When I build the GUI and click the button
Button Pressed!
GtkLabel
Yet when I try to below to do a change the MonoDevelop IDe wont compile with error:
Error CS1061: 'Widget' does not contain a definition for 'Text' and no accessible extension method 'Text' accepting a first argument of type 'Widget' could be found (are you missing a using directive or an assembly reference?) (CS1061) (shump)
So I dont seem to be able to access the Text property of the Gtk.Label to change it's display Text. What is the correct way to go about doing this?
So in my regard. I dont believe I can access the "Child" element of the Gtk.Button object.
However, I've since discovered that I can access and edit the GTK.Label.Text property of the child Gtk.Label object directly from within the class. This will automatically update the Label with no necessary calls to Show() functions so will reflect the update immediately to boot! Furthermore, it also keeps the pango styling properties I had applied.
The end goal was to have a stylized countdown type of Button object that reduces the number as clicked and goal was accomplished.
private void cyclepoint(object sender, EventArgs args)
{
// Points is a different array that holds the increments of values
if (this.lblpoints.Text == points[10])
{
lblpoints.Text = points[0];
}
else
{
int nextval;
nextval = (Array.IndexOf(points, this.lblpoints.Text) + 1);
this.lblpoints.Text = points[nextval];
}
}
I cannot for the life of me get a custom control updating itself. In the below example, "test label1" works and outputs as expected. However when I try and get it to do the same thing later on, it will not. I've tried calling Update and Invalidate, but still nothing. Am I missing something?
Swapping the points around doesn't work either, so it doesn't look like it's just off screen for example.
public partial class AdvancedListControl : UserControl
{
public void createAnotherLabel()
{
Controls.Add(new Label { Location = new Point(14, 14), AutoSize = true, Text = "test label2 - test" }); // this won't create
// me.Controls.Add(new Label { Location = new Point(14, 14), AutoSize = true, Text = "test label2 - test" });
// Update();
// Invalidate();
}
private AdvancedListControl me;
public AdvancedListControl()
{
InitializeComponent();
me = this;
Controls.Add(new Label { Location = new Point(26, 26), AutoSize = true, Text = "test label1" });
}
}
Thanks.
The control method has to be called by a control driven by a end user action, like a event handler for a button, causing a page postback, this will cause the control to trigger the method and render the page with the update control state.
Meh! Noob mistake. I did the same as Jashaszun and created a new project, and yes it did work. Turns out I had two copies of the advanced control on a form, and the one that was hiding was the one I was hitting. It's been a long day. ;-)
so, I have a form that is dynamically populated with textboxes and buttons.
How can I create EventHandlers for each of those buttons dynamically (ex: it generates 20 buttons, I need 20 eventhandlers). Each button will have the same function (to delete something from a database) but I need the program to know whenever any one of them is clicked to trigger that code.
// also, the button creation code is within a while() so I can't use it ouside that while (just pointing that out)
Code:
public void LoadElements()
{
//more code here
while(some condition)
{
// more code above
Button b = new Button();
b.Text = "Delete";
b.Name = "button" + j;
b.Location = new Point(240, Y);
Controls.Add(b);
// more code bellow
}
// more code here
}
Assign them like you would for any other event in your code. You can simply add an event handler doing something like:
b.Click += b_Click
Add in the loop:
b.Click+=New Eventhandler(b_Click);
(Just press TAB twice after typing b.Click+=).
Define the function b_Click outside of the loop. It will be invoked when anyone of those button is clicked.
I want to create many button by Foreach,and these Buttons print Uri's Properties. but I don't know how to do it. can you tell me how to do it ?
this is my code:
private void CreateButtons()
{
Uri uri = new Uri("/Pages/PageTest.xaml?Name=Stephen&Age=17",UriKind.Relative);
foreach(var pi in typeof(Uri).GetProperties())
{
//create button
Button btn = new Button();
btn.Content = pi.Name;
btn.Margin = new Thickness(0,0,0,12);
//i think this is bug, but i don't know,how to do
btn.Click += (se,ev)=>
{
MessageBox.Show(pi.GetValue(btn,null).ToString());
}
stackPanel.Children.Add(btn);
}
}
You are using wrong target for
pi.GetValue(btn,null)
You are quering URI properties but your target is button object.
You need something like:
MessageBox.Show(pi.GetValue(uri, null).ToString());
I'm guessing you are getting an exception in the event handler.
Try enabling breaking when exceptions are thrown (Debug|Exceptions -> check "Thrown" next to Common Language Runtime Exceptions").
Then run with debugger attached and see what happens when you click a button. I suspect a NullPointerException