I have a custom control. It has an image. I have exposed the ImageURL attribute in the custom control code by creating a new attribute called ButtonIconImgSrc as follows:
[Category("Appearance")]
[Description("Gets or sets the logo image path")]
public String ButtonIconImgSrc
{
get
{
EnsureChildControls();
return iconImg.ImageUrl;
}
set
{
if (value != null)
{
iconImg.ImageUrl = value.ToString();
}
}
}
I compile the customeControl code to create a dll and then add the dll to my web site solution so i can drag and drop it onto my designer view or dynamically create it. Everything seems to work great in designer, i drop it on, set my custom atributes and looks good.
..... but the img does not show when i compile and run the site in a browser. nothing gets set correctly, its all lost by the time it gets back to the calling code - labels and textboxes and widths and heights etc. I want to create this customcontrol dynamically, not use the designer but same issue.
Below is the code that calls the above 'set' method, except after it comes back from th eset method its still blank.
protected void Page_Load(object sender, EventArgs e)
{
myCustomButton tb = new myCustomButton();
tb.ButtonIconImgSrc = "~/imgs/target_logo.png";
pnlButtons.Controls.Add(tb);
}
I see the code above being hit and the string "~/imgs/ibc_foh.png" being set in the myCustomButton code and that code exits and everything looks good. When the debugger gets back to the calling class (my websites Page_Load) the attribute tb.ButtonIconImgSrc is still blank, "". And so the image does not appear.
Updated: problem solved. I misunderstood the lifecycle of the control, the image was being overwritten in the createChildControls method
Create/Add a Generic HttpHandler page to your project.
in the function 'ProcessRequest'. there will be a HttpContext object (called context).
do as followed:
public class YouHandlerPage : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// REVIEW AND PROCESS THE REQUEST (i.e. ...
// context.Request.QueryString
// context.Request.RequestContext.RouteData.Values;
// context.Request.Form
string fDirectory = #"C:\Users\john\Desktop\";
string fileName = "ibc";
string fileExt = "png";
context.Response.StatusCode = 200;
context.Response.ContentType = "Image/" + fileExt;
//let context.Response.ContentLength be specified by the following WriteFile method
context.Response.WriteFile(Format.String("{0}{1}.{2}", fDirectory, fileName, fileExt));
}
public bool IsReusable { get { return false;} }
}
now, run your web application.. and goto localhost:<portassigned>/<handlerpagefilename>.ashx
Where locahost:<portassigned> is your domain (or IIS Express assigned), and <handlerpagefilename> is whatever your named you added GenericHandlerPage (which should end with .ashx).
When you visit this page, you should get your image..
Further Review
review Registering Routes to map to your HandlerPage.
review HttpModules as an alternative to httphandler pages
problem solved. I misunderstood the lifecycle of the control, the image was being overwritten in the createChildControls method
Related
I'm am a little bit stuck in the ASP.Net's page lifecycle. This is my first ASP.Net project after many years of doing React so I might be missing something;)
Simplified code:
protected void Page_Load(object sender, EventArgs e)
{
BuildView();
}
private void BuildView()
{
switch (pageViewMode.Value)
{
case "Overview": BuildOverview(); break;
case "Runs": BuildRunsOverview(); break;
}
}
private void BuildOverview()
{
var tilesContainer = new TilesContainer();
tilesContainer.OnTileClicked += (InfoTile targetTile) =>
{
pageViewMode.Value = targetTile.Value;
BuildView();
};
rootElement.Controls.Add(tilesContainer);
}
The problem is that the "OnTileClicked" event works only on the first load and not after the postback. I believe it has something to do with the page lifecycle and registering the events after the Control events ( https://learn.microsoft.com/en-us/previous-versions/aspnet/ms178472(v=vs.100)?redirectedfrom=MSDN ).
If it is really the case, how do I then dynamically build pages from the code behind? Should I really create all the controls ( BuildOverview() and BuildRunsOverview()) and then conditionally show or hide them?
'Should I really create all the controls ( BuildOverview() and BuildRunsOverview()) and then conditionally show or hide them?'
Answer is: yes.
You don't dynamically build pages from code behind - at least its not that well supported in asp.net pages.
In your case you need the TilesContainer on every postback and attach the event handler to it, else the event won't be called. So it would be easier to put all your controls in the markup (.aspx) and just set them to Visible = false/true depending on your code. Controls you set to Visible = false won't be rendered on the client side, so at least no overhead there.
If you use custom-controls (I assume your TilesContainer is a custom-control), then you need to implement the Visible-property the right way, e.g. if your TilesContainers main control is a Panel, override Visible and set the value there:
public override bool Visible
{
get { return base.Visible; }
// set all neccessary controls visibility here
set { this.pnlMain.Visible = base.Visible = value; }
}
I'm in the process of developing an app to display booking information via a client's API in Xamarin.Forms. They wanted a section to display several blocks of information, and that information is often styled with HTML tags (we have no control over how this data is returned). As a quick way to throw a view together to display this information, I created a view which consists of a series of individual webviews to display this information inside a scrollview.
To make this page look less horrendous, I have made a custom renderer for the webview that, when content has been loaded, will resize the view to encapsulate all of the content without scrolling.
Now the actual problem:
To achieve this, inside the custom renderer, in the OnAttachedToWindow() callback, get the webview's ViewTreeObserver and call the AddOnPreDrawListener() method to make use of the OnPreDraw() callback;
The resize is performed in that callback, if the ContentHeight is greater than 0 then it resized the page and returns true.
Then in the OnDetachedFromWindow() callback, I check that the VTO is alive, and unsubscribe from the OnPreDraw listener.
Simple, Right?
The page works exactly as expected when navigating to it, however when trying to navigate away, It ALWAYS throws this error:
System.NotSupportedException: Unable to activate instance of type ExtendedWebViewRenderer from native handle 0xbe9f6eac (key_handle 0xf883b13).
The error does not occur if I remove all the ViewTreeObserver event subscription stuff, but if I do that, then the page will not resize accordingly.
Could someone please point me in the right direction? I have included the brief source code for this. I would appreciate any help on this:
Source
public class ExtendedWebViewRenderer : WebViewRenderer, ViewTreeObserver.IOnPreDrawListener
{
ExtendedWebView _xwebView = null;
WebView _webView;
protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged (e);
_xwebView = e.NewElement as ExtendedWebView;
_webView = Control;
}
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
// Here assign a ViewTreeObserver to monitor when the view's children need to be resized.
var _vto = _webView.ViewTreeObserver;
_vto.AddOnPreDrawListener(this);
}
protected override void OnDetachedFromWindow()
{
var _vto = _webView.ViewTreeObserver;
if (_vto.IsAlive)
{
_vto.RemoveOnPreDrawListener(this);
}
this.Control.ClearCache(true);
this.Control.ClearAnimation();
this.Control.StopLoading();
base.OnDetachedFromWindow();
}
public bool OnPreDraw()
{
if (_webView != null)
{
// We get the view's content height and apply that to the parent view.
int contentHeight = _webView.ContentHeight;
if (contentHeight != 0 && contentHeight != _xwebView.HeightRequest)
{
int desiredHeight = contentHeight + _webView.PaddingBottom + _webView.PaddingTop;
_xwebView.HeightRequest = desiredHeight;
}
}
return true;
}
}
I'm trying to create a simple listbox with ObjectListView (WinForm, C#). The goal is to have a single value (a double) and a check box.
I want to be able to edit the double value by Single Click, so here are the relevant lines of code from my MyWindow.Designer.cs file (i've left out the default values for efficiency):
this.olvDepths = new BrightIdeasSoftware.ObjectListView();
this.olvColumn1 = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn()));
...
this.olvDepths.CellEditActivation = BrightIdeasSoftware.ObjectListView.CellEditActivateMode.SingleClick;
this.olvDepths.CheckBoxes = true;
this.olvDepths.CheckedAspectName = "IsDefault";
this.olvDepths.FullRowSelect = true;
//
// olvColumn1
//
this.olvColumn1.AspectName = "Depth";
this.olvColumn1.Text = "";
this.olvColumn1.IsEditable = true;
I then create a list of my class (ShieldingEntry) and use the olvDepths.SetObjects() with the list. My ShieldingEntry class looks like this:
public class ShieldingEntry
{
public double Depth { get; set; }
public bool IsDefault { get; set; }
}
However, when I click the field, it doesn't go into edit mode. I've also tried the DoubleClick, SingleClickAlways, and F2Only modes and they don't work either.
The Checkbox works fine.
************** I have additional information *********************
I've pulled and build the ObjectListView source, so I could step through it.
I put a breakpoint in the OLV StartCellEdit method and it gets called and appears to setup and select the control appropriately. It just never appears...
As I noted in the comments on the answer below, I've got this control on a tabbed dialog, and if I switch to another tab, then back, the control works fine.
What am I missing?
I've used ObjectListView before, and here is what I had to do:
Handle the CellEditStarting event. This event is raised when the cell goes into edit mode. Since OLV doesn't really have built-in editors, you have to make your own. Then handle the CellEditFinishing event to validate the data before putting it back into your model.
So first, handling the CellEditStarting event:
private void objlv_CellEditStarting(object sender, CellEditEventArgs e)
{
//e.Column.AspectName gives the model column name of the editing column
if (e.Column.AspectName == "DoubleValue")
{
NumericUpDown nud = new NumericUpDown();
nud.MinValue = 0.0;
nud.MaxValue = 1000.0;
nud.Value = (double)e.Value;
e.Control = nud;
}
}
This creates your editing control. If you want to make sure the size is right, you can set the size of the control (in this case a NumericUpDown) to the cell bounds using e.CellBounds from the event object.
This will show the editor when you click in the cell. Then you can handle the editor finished event to validate the data:
private void objlv_CellEditFinishing(object sender, CellEditEventArgs e)
{
if (e.Column.AspectName == "DoubleValue")
{
//Here you can verify data, if the data is wrong, call
if ((double)e.NewValue > 10000.0)
e.Cancel = true;
}
}
I don't think handling it is required, but its good practice to validate data from the user.
The editing control in the CellEditStarting event can be any control, even a user defined one. I've used a lot of user defined controls (like textboxes with browse buttons) in the cell editor.
[Edit]
I uploaded an example here dropbox link that seems to work. Might not be in the exact view as needed, but seems to do the job.
For anyone else with this problem. I had it specifically when trying to edit a 'null' value in a decimal? on the OLV on a tab page. Solution for me was to set UseCustomSelectionColors to 'False'. I didn't look elsewhere to see if it was reported as a bug. Seems like a bug.
I have one image button in the custom control like below.
public string SearchTableName = string.Empty;
public string SearchColumnName = string.Empty;
public string SiteURL = string.Empty;
ImageButton _imgbtn;
protected override void OnInit(EventArgs e)
{
_imgbtn = new ImageButton();
_imgbtn.ImageUrl = ImageURL;
_imgbtn.OnClientClick = "ShowSearchBox('" + SiteURL +"/_layouts/CustomSearch/SearchPage/Searchpage.aspx?table_name=" + SearchTableName + " &column_name=" + SearchColumnName + "')";
}
On Clicking of the image button I want to migrate to the another window which is a popup. For this I written a javascript function. I am setting the SearchTableName and SearchColumnName in the web page in which we are consuming this custom control like below. Before consuming I registered this control in web page with register tag.
<ncc:SearchControl runat="server" ID="txtSearchControl" /> In code behind file of this webpage I am using following code to set the values.
protected void Page_Load(object sender, EventArgs e)
{
txtSearchControl.ImageURL = "_layouts/Images/settingsicon.gif";
txtSearchControl.SearchTableName = "Employees";
txtSearchControl.SearchColumnName = "LastName";
txtSearchControl.SiteURL = "http://Sp2010:8787";
}
Now coming to the problem, when I click the image button the SearchTableName and SearchColumnName values are not coming. I think I am calling OnClientClick function, thats why the values are not being set. But how to set the values for the custom control based on the values setting in the webpage. If I use the Click function will it serve my purpose? If so, how to call that javascript function from this click event.
Finally got solution. I am initializing the values in the page init method in the custom control. Thats why the values i am setting in the visual webpart page are not being captured. Now I changed the initializing the values in CreateChildControl method. Now it works perfectly. Thank you.
I've encountered an odd problem that doesn't make any sense to me. I am trying to dynamically set up MasterPage Content controls on a page. I have it working nicely with the following code:
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
MasterPageFile = "~/MasterPages/Default.master";
string existantContentPlaceHolderID = "ContentPlaceHolder1";
string nonExistantContentPlaceHolderID = "foo";
//Control c = Master.FindControl(existantContentPlaceHolderID);
//Control c1 = Master.FindControl(nonExistantContentPlaceHolderID);
TextBox t = new TextBox
{
Text = "Text"
};
ITemplate iTemplate = new GenericITemplate(container => container.Controls.Add(t));
AddContentTemplate(existantContentPlaceHolderID, iTemplate);
}
public delegate void InstantiateTemplateDelegate(Control container);
public class GenericITemplate : ITemplate
{
private readonly InstantiateTemplateDelegate m_instantiateTemplate;
public void InstantiateIn(Control container)
{
m_instantiateTemplate(container);
}
public GenericITemplate(InstantiateTemplateDelegate instantiateTemplate)
{
m_instantiateTemplate = instantiateTemplate;
}
}
This works great, except I want to be able to double-check that the contentPlaceHolderIDs exist on the MasterPage before calling AddContentTemplate as the Page will throw an error if you add a Content control that points to a non-existing ContentPlaceHolder.
The problem I am having is that in the above example when I call one of the commented Master.FindControl lines, the TextBox no longer renders.
Does anyone have any ideas why this might be... I cannot makes heads or tails of what is going on.
Thanks,
Max
The problem is that AddContentTemplate just records its parameters in a hashtable ready to be combined with the master page instance when it is created. Calling it after the master page has been created won't do anything, and reading the Master property causes the master page to be created.
The best way I can see around this is to create a separate instance of the master page with LoadControl, which you can inspect without affecting the page's own Master property...
MasterPage testMaster = (MasterPage) LoadControl( MasterPageFile );
Control c = testMaster.FindControl(existantContentPlaceHolderID);
There's some overhead in creating a second instance, but it's not immediately obvious to me whether it will be worth worrying about.