ASP.NET Custom Web control - asp.net

I'm experiencing some problems and right now I don't know how to solve it. The web control simply updates a clock represented by a label every second. My issue is that the web control exposes a property called 'Formato' where the user can select to display in format 12 or 24 hours. This is done with an enum type where in spanish Doce means 12 and Veinticuatro means 24. This is the code for the server control:
namespace Ejercicio2RelojControl
{
public enum _FormatoHora
{
Doce,
Veinticuatro
}
[DefaultProperty("FormatoHora")]
[ToolboxData("<{0}:Ejercicio2RelojControl runat=server></{0}:Ejercicio2RelojControl>")]
[ToolboxBitmap(typeof(Ejercicio2RelojControl), "Ejercicio2RelojControl.Ejercicio2RelojControl.ico")]
//[Designer("Ejercicio2RelojControl.Ejercicio2RelojControlDesigner, Ejercicio2RelojControl")]
public class Ejercicio2RelojControl : WebControl
{
public Ejercicio2RelojControl()
{
}
[
//Bindable(true),
Category("Appearance"),
//DefaultValue(_FormatoHora.Doce),
Description(""),
]
public virtual _FormatoHora FormatoHora
{
get
{
//object t = ViewState["FormatoHora"];
//return (t == null) ? _FormatoHora.Doce : (_FormatoHora)t;
object obj2 = this.ViewState["_FormatoHora"];
if (obj2 != null)
{
return (_FormatoHora)obj2;
}
return _FormatoHora.Doce;
}
set
{
ViewState["_FormatoHora"] = value;
}
}
//Create one TimerControl
Timer timer = new Timer();
private Label clockLabel = new Label();
// Declare one Updatepanel
UpdatePanel updatePanel = new UpdatePanel();
// Now override the Load event of Current Web Control
protected override void OnLoad(EventArgs e)
{
//Text = "hh:mm:ss";
// Create Ids for Control
timer.ID = ID + "_tiker";
clockLabel.ID = ID + "_l";
// get the contentTemplate Control Instance
Control controlContainer = updatePanel.ContentTemplateContainer;
// add Label and timer control in Update Panel
controlContainer.Controls.Add(clockLabel);
controlContainer.Controls.Add(timer);
// Add control Trigger in update panel on Tick Event
updatePanel.Triggers.Add(new AsyncPostBackTrigger() { ControlID = timer.ID, EventName = "Tick" });
updatePanel.ChildrenAsTriggers = true;
// Set default clock time in label
clockLabel.Text = DateTime.Now.ToString("h:mm:ss tt");
//clockLabel.Text = DateTime.Now.ToString("H:mm:ss");
// Set Interval
timer.Interval = 1000;
// Add handler to timer
timer.Tick += new EventHandler<EventArgs>(timer_Tick);
updatePanel.RenderMode = UpdatePanelRenderMode.Block;
//Add update panel to the base control collection.
base.Controls.Add(updatePanel);
}
protected override void RenderContents(HtmlTextWriter output)
{
output.Write(FormatoHora);
}
void timer_Tick(object sender, EventArgs e)
{
// Set current date time in label to move current at each Tick Event
clockLabel.Text = DateTime.Now.ToString("h:mm:ss tt");
//clockLabel.Text = DateTime.Now.ToString("H:mm:ss");
}
}
}
Now it's time to test the custom control in an asp.net web application.
<cc1:Ejercicio2RelojControl ID="Ejercicio2RelojControl1" runat="server" />
Works great! BUT when I add the property "Formato" fails at compile time:
<cc1:Ejercicio2RelojControl ID="Ejercicio2RelojControl1" runat="server" Formato="Doce" />
Compiler Error Message: CS0117: 'Ejercicio2RelojControl.Ejercicio2RelojControl' does not contain a definition for 'FormatoHora'
Why is the property Formato making the web app crash at compile time?
Thanks a lot.
EDIT:
namespace Ejercicio2RelojControl
{
public enum FormatoHora
{
Doce,
Veinticuatro
}
[DefaultProperty("FormatoHora")]
[ToolboxData("<{0}:Ejercicio2RelojControl runat=server></{0}:Ejercicio2RelojControl>")]
public class Ejercicio2RelojControl : WebControl, INamingContainer
{
public FormatoHora FormatoHora
{
get
{
object obj2 = this.ViewState["FormatoHora"];
if (obj2 != null)
{
return (FormatoHora)obj2;
}
return FormatoHora.Doce;
}
set
{
ViewState["FormatoHora"] = value;
}
}
As you can see I've changed the public property. Now the error has changed. Is the following:
Compiler Error Message: CS0120: An object reference is required for the non-static field, method, or property 'Ejercicio2RelojControl.Ejercicio2RelojControl.FormatoHora.get'
Any help appreciated. Thanks
EDIT 2:
I've discovered that the problem is on the set {}. If I comment it, all is working fine but then I cannot change FormatoHora between 12 and 24 because of is read only due to only get{} is implemented. Any help with the implementation of set{} ?

I am here for giving you the solution:
You are using the same name for the namespace and for the webcontrol (Ejercicio2RelojControl) . Simply change that and your code will work fine.
Hope it helps, despite the fact some years have passed :)

Related

Prism6 shared service and dependency property

I use Prism6 + Unity container for desktop application developing.
This is a long-read, sorry. So I ask at top: Prism SetProperty() function is not rising property changed event if input value is Unity singleton. And I understand why: because input value and save value have same reference to singleton instance. RaisePropertyChanged() don't help in this situation.
Long-read is statring...
So, I have a dependency property in my UserControl component:
public static readonly DependencyProperty WorksheetDataProperty =
DependencyProperty.Register("WorksheetData", typeof(WorksheetDataModel), typeof(SheetUserControl),
new PropertyMetadata(new WorksheetDataModel(), WorksheetDataPropertyChanged));
public WorksheetDataModel WorksheetData {
get { return (WorksheetDataModel)GetValue(WorksheetDataProperty); }
set { SetValue(WorksheetDataProperty, value); }
}
private void WorksheetDataPropertyChanged(WorksheetDataModel worksheetData) {
if (worksheetData == null)
return;
SheetGrid.Model.ActiveGridView.BeginInit();
this.ClearWorksheetModel();
this.ResizeWorksheetModel();
SheetGrid.Model.ActiveGridView.EndInit();
}
private static void WorksheetDataPropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e) {
((SheetUserControl)d).WorksheetDataPropertyChanged((WorksheetDataModel)e.NewValue);
}
It's important for me to invoke actions from WorksheetDataPropertyChanged() function.
And scheme without shared service (singleton) is working well: this function is called.
But now I want to share data between several modules. How I see it: I have some "parent" module, which load\save data from storage and shared this data with several other modules, which can modificate shared data, but can't save it.
And EventAggregator is not convenient for me: I don't want to create copies of data and then collect it again after modifications.
So I register my "shared service" as singleton:
_container.RegisterInstance(new WorksheetDataModel());
Now I can load data from database in "parent" viewmodel to singleton object created in previous step:
var data = _container.Resolve<WorksheetDataModel>();
data.Header = args.Header;
data.User = args.User;
data.RowHeader = new WorksheetRowHeader(_model.ReadRowHeader(data.Header.WshCode));
data.ColHeader = new WorksheetColHeader(_model.ReadColHeader(data.Header.WshCode));
data.Cells = _model.ReadCells(data.Header.WshCode);
Further, I notify child viewmodels about new data in singleton:
data.OnDataChanged?.Invoke();
And now most important code from child viewmodel.
In delegate handler I "apply" new value:
WorksheetData = _container.Resolve<WorksheetDataModel>();
WorksheetData is:
private WorksheetDataModel _worksheetData;
public WorksheetDataModel WorksheetData {
get { return _worksheetData; }
set { SetProperty(ref _worksheetData, value); }
}
And problem in this line:
set { SetProperty(ref _worksheetData, value); }
It works only once at first call, because _worksheetData is null. But then refernce of _worksheetData (pointer) setted to singleton and in all next call value and _worksheetData are identical for SetProperty() and, as result, it just quit.
I tried next code:
set {
SetProperty(ref _worksheetData, value);
RaisePropertyChanged("WorksheetData")
}
But no effect. WorksheetDataPropertyChanged() callback in UserControl component is not calling.
So, I don't know now how to better share some data between several modules.
Thanks for any advice.
WorksheetData does not change, the contents of the WorksheetDataModel instance change.
So to update your bindings,
either WorksheetDataModel implements INotifyPropertyChanged and/or uses INotifyCollectionChanged-implementing collections
or you let the view model listen to WorksheetDataModel.OnDataChanged and raise its own PropertyChanged to update all bindings to WorksheetData.
Example:
private WorksheetDataModel _worksheetData;
public WorksheetDataModel WorksheetData
{
get { return _worksheetData; }
set
{
if (_worksheetData != null)
_worksheetData.OnDataChanged -= DataChangedHandler;
SetProperty(ref _worksheetData, value);
if (_worksheetData != null)
_worksheetData.OnDataChanged += DataChangedHandler;
}
}
private void DataChangedHandler( object sender, DataChangedEventArgs args )
{
RaisePropertyChanged( nameof( WorksheetData ) );
}

Populate List when web page load

I'm a newbie in ASP.NET and I'm stuck at populating a List when web page load.
I have a class that, when instantiated, will add some objects into a list. This class has a method to add objects to a list (I made this a method because I need to reuse it later). This is the code for this class:
public class Task()
{
private List<ObjectA> objList;
....// other variables and properties
public Task()
{
objList = new List<ObjectA>();
// first add 2 obj into the list
AddObjToList(objList);
AddObjToList(objList);
}
public void AddObjToList(List<ObjectA> objList)
{
bool exist = false;
ObjectA obj = new ObjectA(); // each obj has unique properties
foreach(var o in objList)
{
if(o.objName == obj.objName)
{
exist = true;
break;
}
}
if(!exist)
{
objList.add(obj);
}
}
}
I have a web page that, when loaded, I want it to populate the objList with 2 starting objects. This is my code behind for the web page:
protected void Page_Load(object sender, EventArgs e)
{
Task newTask = new Task();
// the following label control is to check the number of
// elements in the List
lblMSG.Text = newTask.getObjList.Count.ToString();
}
The problem is, when I run in debug mode, the list is populated with 2 objects just as I wanted. But when not in debug mode, when the page loaded, the label showed that the list has only 1 object. I tried to call the new Task() with isPostBack but the result is the same. What did I do wrong? What could I do to make it work?

DevExpress: Customize the XAF Scheduler List View Display Text

I have a business object in my XAF application which inherits from the standard Scheduler'Event' class. In the list view I get the default scheduler list view where the boxes display the descriptive text. I want to display additional text in those boxes. I looked around and found "ScheduleControl.InitAppointmentDisplayText" event but could not figure out how to implement it within my class.
You can implement the following code within a view controller within the Module.Win project.
namespace Project.Module.Win.Controllers{
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Scheduler.Win;
using DevExpress.XtraScheduler;
public partial class SchedulerViewController : ObjectViewController<ListView, Project.Module.BusinessObjects.Event>
{
public SchedulerViewController()
{
this.InitializeComponent();
this.RegisterActions(this.components);
}
protected override void OnViewControlsCreated()
{
base.OnViewControlsCreated();
SchedulerListEditor listEditor = View.Editor as SchedulerListEditor;
if (listEditor != null)
{
SchedulerControl scheduler = listEditor.SchedulerControl;
if (scheduler != null)
{
scheduler.InitAppointmentDisplayText += new AppointmentDisplayTextEventHandler(this.SchedulerControl_InitAppointmentDisplayText);
}
}
}
private void SchedulerControl_InitAppointmentDisplayText(object sender, AppointmentDisplayTextEventArgs e)
{
MyEventObject myEventObject = this.ObjectSpace.GetObjectByKey<MyEventObject>(e.Appointment.Id);
if (myEventObject != null)
{
e.Text = string.Concat("Text Goes Here - ", myEventObject.FieldValue);
}
}
}

how to update Visual Studio UI when using DynamicItemStart inside a vsix package

I'm implementing a DynamicItemStart button inside a Menu Controller. I'm loading the dynamic items for this button when Visual Studio starts. Everything is loaded correctly so the initialize method is called an I see all the new items in this Dynamic button. After the package is completely loaded I want to add more items to this Dynamic button, but since the package is already loaded the initialize method is not called again and I cannot see the new items in this Dynamic button. I only see the ones that were loaded when VS started.
Is there any way that I can force the update of this Dynamic button so it shows the new items?. I want to be able to update the VS UI after I added more items but outside the Initialize method.
The implementation I did is very similar to the one showed on this msdn example:
http://msdn.microsoft.com/en-us/library/bb166492.aspx
Does anyone know if an Update of the UI can be done by demand?
Any hints are greatly appreciated.
I finally got this working. The main thing is the implementation of a derived class of OleMenuCommand that implements a new constructor with a Predicate. This predicate is used to check if a new command is a match within the DynamicItemStart button.
public class DynamicItemMenuCommand : OleMenuCommand
{
private Predicate<int> matches;
public DynamicItemMenuCommand(CommandID rootId, Predicate<int> matches, EventHandler invokeHandler, EventHandler beforeQueryStatusHandler)
: base(invokeHandler, null, beforeQueryStatusHandler, rootId)
{
if (matches == null)
{
throw new ArgumentNullException("Matches predicate cannot be null.");
}
this.matches = matches;
}
public override bool DynamicItemMatch(int cmdId)
{
if (this.matches(cmdId))
{
this.MatchedCommandId = cmdId;
return true;
}
this.MatchedCommandId = 0;
return false;
}
}
The above class should be used when adding the commands on execution time. Here's the code that creates the commands
public class ListMenu
{
private int _baselistID = (int)PkgCmdIDList.cmdidMRUList;
private List<IVsDataExplorerConnection> _connectionsList;
public ListMenu(ref OleMenuCommandService mcs)
{
InitMRUMenu(ref mcs);
}
internal void InitMRUMenu(ref OleMenuCommandService mcs)
{
if (mcs != null)
{
//_baselistID has the guid value of the DynamicStartItem
CommandID dynamicItemRootId = new CommandID(GuidList.guidIDEToolbarCmdSet, _baselistID);
DynamicItemMenuCommand dynamicMenuCommand = new DynamicItemMenuCommand(dynamicItemRootId, isValidDynamicItem, OnInvokedDynamicItem, OnBeforeQueryStatusDynamicItem);
mcs.AddCommand(dynamicMenuCommand);
}
}
private bool IsValidDynamicItem(int commandId)
{
return ((commandId - _baselistID) < connectionsCount); // here is the place to put the criteria to add a new command to the dynamic button
}
private void OnInvokedDynamicItem(object sender, EventArgs args)
{
DynamicItemMenuCommand invokedCommand = (DynamicItemMenuCommand)sender;
if (null != invokedCommand)
{
.....
}
}
private void OnBeforeQueryStatusDynamicItem(object sender, EventArgs args)
{
DynamicItemMenuCommand matchedCommand = (DynamicItemMenuCommand)sender;
bool isRootItem = (matchedCommand.MatchedCommandId == 0);
matchedCommand.Enabled = true;
matchedCommand.Visible = true;
int indexForDisplay = (isRootItem ? 0 : (matchedCommand.MatchedCommandId - _baselistID));
matchedCommand.Text = "Text for the command";
matchedCommand.MatchedCommandId = 0;
}
}
I had to review a lot of documentation since it was not very clear how the commands can be added on execution time. So I hope this save some time whoever has to implement anything similar.
The missing piece for me was figuring out how to control the addition of new items.
It took me some time to figure out that the matches predicate (the IsValidDynamicItem method in the sample) controls how many items get added - as long as it returns true, the OnBeforeQueryStatusDynamicItem gets invoked and can set the details (Enabled/Visible/Checked/Text etc.) of the match to be added to the menu.

Get Selected Text asp.net custom server control

I need to get the latest text set in the custom control by javascript. When i tried to get the selected text from server control, it is always returning the default text & not the modified text. How to retain the latest value set by the javascript in servercontrol? Below is the complete code for your reference..
ServerControl1.cs
[assembly: WebResource("ServerControl1.Scripts.JScript1.js", "text/javascript")]
namespace ServerControl1
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")]
public class ServerControl1 : WebControl
{
public List<string> ListItems
{
get
{
return ViewState["items"] as List<string>;
}
set
{
ViewState["items"] = value;
}
}
public string Text
{
get
{
return (FindControl("middleDiv").FindControl("anchorID") as HtmlAnchor).InnerText;
}
set
{
((FindControl("middleDiv").FindControl("anchorID") as HtmlAnchor)).InnerText = value;
}
}
protected override void CreateChildControls()
{
base.CreateChildControls();
HtmlGenericControl selectedTextContainer = new HtmlGenericControl("div");
selectedTextContainer.ClientIDMode = System.Web.UI.ClientIDMode.Static;
selectedTextContainer.ID = "middleDiv";
HtmlAnchor selectedTextAnchor = new HtmlAnchor();
selectedTextAnchor.ClientIDMode = System.Web.UI.ClientIDMode.Static;
selectedTextAnchor.ID = "anchorID";
selectedTextAnchor.HRef = "";
selectedTextContainer.Controls.Add(selectedTextAnchor);
HtmlGenericControl unList = new HtmlGenericControl("ul");
foreach (string item in ListItems)
{
HtmlGenericControl li = new HtmlGenericControl("li");
HtmlAnchor anchor = new HtmlAnchor();
anchor.HRef = "";
anchor.Attributes.Add("onclick", "updateData()");
anchor.InnerText = item;
li.Controls.Add(anchor);
unList.Controls.Add(li);
}
selectedTextContainer.Controls.Add(unList);
Controls.Add(selectedTextContainer);
ChildControlsCreated = true;
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
string resourceName = "ServerControl1.Scripts.JScript1.js";
ClientScriptManager cs = this.Page.ClientScript;
cs.RegisterClientScriptResource(typeof(ServerControl1), resourceName);
}
}
}
JScript1.js
function updateData() {
var evt = window.event || arguments.callee.caller.arguments[0];
var target = evt.target || evt.srcElement;
var anchor = document.getElementById("anchorID");
anchor.innerText = target.innerText;
return false;
}
TestPage Codebehind
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
List<string> items = GetDataSource();
ServerControl1.ListItems = items;
ServerControl1.Text = "Select ..";
}
}
protected void ClientButton_Click(object sender, EventArgs e)
{
string selectedText = ServerControl1.Text;
}
The server won't get your client changes unless you POST the changes to him. Your HtmlAnchors are being rendered in HTML as <a> controls, and these type of controls won't POST anything to the server.
You're going to need an <input> control to input the changes into the server (that's why they're called input controls after all). I suggest an <input type=hidden> to hold the value of the anchor.innerText and keeps its state.
Your Javascript function needs to be modified so it updates the anchor.innerText AND updates the hidden input value as well. This way when the page gets posted back to the server you can retrieve the updated and client-modified value from the hidden field.
First you need to define as private fields your selectedTextAnchor and the hiddenField you are going to insert. This is because you need to access them in your CreateChildControls method as well as in the getter and setter of yout Text property. Much in the way the partial designer classes define the controls you want to have available in code-behind.
ServerControl.cs
private HtmlAnchor selectedTextAnchor;
private HtmlInputHidden hiddenField;
In the CreateChildControls method you need to insert the hidden field.
You'll notice I removed the use of ClientIDMode.Static. Using that mode would make your client controls to have the same fixed IDs and Javascript might get confused when you have multiple copies of your ServerControl in a page, and thus losing the reusable purpose of a custom control.
Instead, you need to provide your Javascript function with the ClientID's of the controls it needs to modify. The key here is that you need to attach your controls to the Control's hierarchy BEFORE you try to get their ClientID's.
As soon as you do this.Controls.Add(dummyControl), you're making dummyControl to become a part of the Page and its dummyControl.ClientID will be suddenly changed to reflect the hierarchy of the page you're attaching it into.
I changed the order at which your controls are attached to the Control's collection so we can grab their ClientID's at the time we build the onclick attribute and pass the parameters so your Javascript function knows which anchor and hiddenField to affect.
ServerControl.cs
protected override void CreateChildControls()
{
base.CreateChildControls();
// Instantiate the hidden input field to include
hiddenField = new HtmlInputHidden();
hiddenField.ID = "ANCHORSTATE";
// Insert the hiddenfield into the Control's Collection hierarchy
// to ensure that hiddenField.ClientID contains all parent's NamingContainers
Controls.Add(hiddenField);
HtmlGenericControl selectedTextContainer = new HtmlGenericControl("div");
// REMOVED: selectedTextContainer.ClientIDMode = System.Web.UI.ClientIDMode.Static;
selectedTextContainer.ID = "middleDiv";
selectedTextAnchor = new HtmlAnchor();
// REMOVED: selectedTextAnchor.ClientIDMode = System.Web.UI.ClientIDMode.Static;
selectedTextAnchor.ID = "anchorID";
selectedTextAnchor.HRef = "";
selectedTextContainer.Controls.Add(selectedTextAnchor);
// Insert the selectedTextContainer (and its already attached selectedTextAnchor child)
// into the Control's Collection hierarchy
// to ensure that selectedTextAnchor.ClientID contains all parent's NamingContainers
Controls.Add(selectedTextContainer);
HtmlGenericControl unList = new HtmlGenericControl("ul");
foreach (string item in ListItems)
{
HtmlGenericControl li = new HtmlGenericControl("li");
HtmlAnchor anchor = new HtmlAnchor();
anchor.HRef = "";
// The updateData function is provided with parameters that will help
// to know who's triggering and to find the anchor and the hidden field.
// ClientID's are now all set and resolved at this point.
anchor.Attributes.Add("onclick", "updateData(this, '" + selectedTextAnchor.ClientID + "', '" + hiddenField.ClientID + "')");
anchor.InnerText = item;
li.Controls.Add(anchor);
unList.Controls.Add(li);
}
selectedTextContainer.Controls.Add(unList);
}
Note the use of the keyword this in the updateData function, it'll help us to grab the object that is triggering the action. Also note that both Id's are passed as strings (with single quotes)
The Javascript function would need to be modified so it updates the anchor and the hidden input field.
JScript1.js
function updateData(sender, anchorId, hidFieldId) {
// Update the anchor
var anchor = document.getElementById(anchorId);
anchor.innerText = sender.innerText;
// Update the hidden Input Field
var hidField = document.getElementById(hidFieldId);
hidField.value = sender.innerText;
return false;
}
The last thing to do is change the way you are setting and getting your Text property.
When you GET the property you need to check if it's a Postback, and if it is, then you want to check if among all the info that comes from the browser there is your HiddenInputField. You can grab all the info coming from the client right at the Request object, more specifically, in the Request.Form.
All enabled input controls on your page will be part of the Request.Form collection, and you can get their values by using Request.Form[anyInputControl.UniqueID]. Note that the key used for this object is the UniqueID, NOT ClientID.
Once you get your client-modified value from the hidden input, you assign its value to the selectedTextAnchor, otherwise it'll go back to the original "Select..." text.
When you SET the property, you just need to assign it to the selectedTextAnchor.
In both GET and SET you need to call EnsureChildControls(), which will actually call your CreateChildControls() to make sure that your selectedTextAnchor and hiddenField controls are instantiated before you try to get some of their properties. Pretty much the same way that it's done in Composite Controls.
ServerControl.cs
public string Text
{
get
{
EnsureChildControls();
if (this.Page.IsPostBack)
{
string HiddenFieldPostedValue = Context.Request.Form[hiddenField.UniqueID];
// Assign the value recovered from hidden field to the Anchor
selectedTextAnchor.InnerText = HiddenFieldPostedValue;
return HiddenFieldPostedValue;
}
else
{
return selectedTextAnchor.InnerText;
}
}
set
{
EnsureChildControls();
selectedTextAnchor.InnerText = value;
}
}
This way you can have a control that recognizes the changes made in client. Remember that server won't know any change in client unless you notice him.
Another approach would be to notice the server everytime you click a link through an ajax request, but this would require a whole new different code.
Good luck!

Resources