I have a control : composite control that defines a button. The button calls a Command event and sets the value of another control's property, before making the control visible.
Both of these controls are instantiated within a container control before hand. I need to grab the value of the property in the second form within the CreateChildControls() method, however, this is not possible, why?
Scenario:
public class Main : CompositeControl
{
#region fields
private StepPersonal _stepPersonal;
private StepFinancial _stepFinancial;
#endregion
protected override CreateChildControls()
{
this._stepPersonal = new StepPersonal { ID = "StepPersonal1", Visible = true };
this.Controls.Add(this._stepPersonal);
this._stepFinancial = new StepFinancial { ID = "StepFinancial1", Visible = false };
this.Controls.Add(this._stepFinancial);
}
protected override Render(HtmlTextWriter writer)
{
this._stepPersonal.RenderControl(writer);
this._stepFinancial.RenderControl(writer);
}
}
StepPersonal:
public class StepPersonal : CompositeControl
{
#region fields
private Button _checkDetails;
#endregion
protected override CreateChildControls()
{
this._checkDetails = new Button { ID = "CheckDetailsButton", Text = "CheckDetails", CommandName = "DetailsConfirmation" }
this._checkDetails.Command += new CommandEventHandler(this.OnCheckDetails);
this.Controls.Add(this._checkDetails);
}
protected override Render(HtmlTextWriter writer)
{
this._checkDetail.RenderControl(writer);
}
protected void OnCheckDetails(object sender, CommandEventArgs args)
{
string argument = args.CommandArgs.ToString();
this.Visible = false;
Main owner = (sender as Button).FindParent<Main>(); // custom extension method
StepFinancial sf = owner.FindControl<StepFinancial>("StepFinancial1");
sf.Argument = argument;
sf.Visible = false;
}
}
Then lastly, and this is where my problem lies, the StepFinancial control
public class StepFinancial : CompositeControl
{
#region fields
private TextBox _argumentTextBox;
#endregion
#region properties
public string Argument { get; set; }
#endregion
protected override CreateChildControls()
{
string argument = this.Argument; // this is always null
// I have buttons in here (not being displayed) who's events are not wiring up
// if I move this into a method, and call the method in Render(), as it seems
// to allow me access to the properties there, but I need the value here?
}
protected override Render(HtmlTextWriter writer)
{
string argument = this.Argument; // this contains the value of the property set previously
this._argumentTextBox.RenderControl(writer);
}
}
I've tried adding the value to a StateBag, nothing. I tried adding a QueryString in the CommandEventHanlder, but get a collection is locked error message. I tried cache and Sessions (sessions won't work cause this will be deployed to SharePoint) so that's out of the question. I've tried Googling, Bing'ing even Yahoo! this but no luck.
Update
I failed to mention, I can't add the controls to the collection under OnPreRender as it does not wire up any of the events. I can't use EnsureChildControls either as it screws up other parts of the application. I can, however, add the value from the property at this level, to a statebag, but I feel this is a very bad way of doing it.
I did not really resolve this in a matter that I would feel comfortable with, but I have a NoSQL solution underlying this, and store the values between the controls in there and dispose of it when the application closes.
Related
I have created an activity to mimic the sequence activity in WPF using the below code with help from Windows Workflow Custom Sequence Activity
using System.Activities;
using System.Activities.Statements;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace Custom_Activities
{
[Designer("System.Activities.Core.Presentation.SequenceDesigner, System.Activities.Core.Presentation")]
public class Scoped_Activity_Scope : NativeActivity
{
private string TestVariable = "testing testing";
private Sequence innerSequence = new Sequence();
[Browsable(false)]
public Collection<Activity> Activities
{
get
{
return innerSequence.Activities;
}
}
[Browsable(false)]
public Collection<Variable> Variables
{
get
{
return innerSequence.Variables;
}
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddImplementationChild(innerSequence);
}
protected override void Execute(NativeActivityContext context)
{
System.Console.WriteLine("Scope Executing");
context.ScheduleActivity(innerSequence);
}
}
}
I also have created a second custom activity below
using System.Activities;
namespace Custom_Activities
{
public sealed class Scoped_Activity : Scoped_Activity_Template
{
protected override void Execute(CodeActivityContext context)
{
System.Console.WriteLine("Scope Activity Executing");
//System.Console.WriteLine(testVariable);
}
}
}
When I run the code as below
I get the desired output
Scope Executing
Scope Activity Executing
How can I pass the variable testVariable from the class Scoped_Activity_Scope for use within Scoped_Activity as per the line of code commented out?
First you create a property for the inner activity:
public string TestVariable { get; set; }
Then you can access that property through the Activities collection.
There are 2 situations where you need to update the value of TestVariable: 1, When TestVariable changes, and 2, When you add a new Scoped_Activity to Scoped_Activity_Scope.
Number 1 is easy, just change all Scoped_Activity.TestVariables every time you change the parent's TestVariable. Number 2 is a little more difficult. You need to be able to catch the CollectionChanged event which is fired every time you change Activities from the UI. This is possible through the designer of the activity. I assume you have probably written a custom designer for the activity.
public Scoped_Activity_Scope_Designer()
{
InitializeComponent();
this.Loaded += AddCollectionChangedHandler;
}
private void AddCollectionChangedHandler(object sender, RoutedEventArgs e)
{
var ownactivities =
ModelItem.Properties[nameof(Scoped_Activity_Scope.Activities)].Collection;
ownactivities.CollectionChanged += AddTestVariable;
}
Inside AddTestVariable, just update the TestVariable property of every activity in ownActivities, which you need to retrieve again.
As an aside, you can also access TestVariable by calling the Parent, but then again, you still need to do it inside of Scoped_Activity_Designer, which is the one that actually has access to the parent. However, Scoped_Activity_Designer doesn't know when TestVariable has been updated, and it doesn't know when execute is called in order to retrieve the most up to date value. Using CollectionChanged is probably the best way to do it.
This is how you access TestVariable through the parent:
var dataContext =
Parent.GetValue(Scoped_Activity_Scope_Designer.DataContextProperty);
if (dataContext != null)
{
var designer = (Scoped_Activity_Scope_Designer) dataContext;
var scoped_activity_scope = (Scoped_Activity_Scope) designer.ModelItem.GetCurrentValue();
var scoped_activity = (Scoped_Activity) ModelItem.GetCurrentValue();
scoped_activity.TestVariable = scoped_activity_scope.TestVariable;
}
You should put this code in the handler for the Loaded event inside Scoped_Activity_Designer.
In the Scoped_Activity_Scope within the Execute method I added the following code to iterate through the activities within innerSequence
foreach(Activity a in innerSequence.Activities)
{
if (a.GetType().IsSubclassOf(typeof(UiPath_Activities_Templates.Scoped_Activity_Template)))
{
Scoped_Activity_Template vet = null;
vet = (Scoped_Activity_Template) a;
vet.UpdateTestVariable("changed");
}
}
And in the class 'Scoped_Activity_Template' which 'Scoped_Activity' inherits from (this class was previously empty) I added the following code
public static string TestVariable = "testing";
public void UpdateTestVariable(string newValue)
{
TestVariable = newValue;
}
That way the inherited class Scoped_Activity has access to the variable TestVariable.
When the loop iterates over the activities in innerSequence it checks if it inherits from Scoped_Activity_Template it calls the method UpdateTestVariable to update the variable.
When the class Scoped_Activity is then executed it will have the updated variable.
I have written a user control, MenuItem, which inherits from a Form Label.
I have a backgroundworker thread whose IsBusy property is exposed through a property in the MainForm as IsBackgroundBusy.
How do I read this property from the MenuItem usercontrol? I am currently using Application.UseWaitCursor and I set that in the backgroundworker and it works perfectly, however I do not want the cursor to change. That's why I figured a property that I could set would be much better.
Here is the code in my MainForm:
public partial class MainForm : Form
{
public bool IsBackgroundBusy
{
get
{
return bwRefreshGalleries.IsBusy;
}
}
Here is the code for my usercontrol:
public partial class MenuItem: Label
{
private bool _disableIfBusy = false;
[Description("Change color if Application.UseWaitCursor is True")]
public bool DisableIfBusy
{
get
{
return _disableIfBusy;
}
set
{
_disableIfBusy = value;
}
}
public MenuItem()
{
InitializeComponent();
}
protected override void OnMouseEnter( EventArgs e )
{
if ( Application.UseWaitCursor && _disableIfBusy )
{
this.BackColor = SystemColors.ControlDark;
}
else
{
this.BackColor = SystemColors.Control;
}
base.OnMouseEnter( e );
}
(Note: it's not clear to me whether you have an actual UserControl here or not. The MenuItem class you show inherits Label, not UserControl. You should probably avoid using the term "usercontrol" or "user control" when you are not actually dealing with a UserControl object).
Absent a complete code example, it's hard to know exactly what the right solution here is. However, assuming you are using the BackgroundWorker in a typical fashion, then you simply need for the owner of the control (i.e. the containing Form) to pass the necessary state to the control as it changes. E.g.:
class MenuItem : Label
{
public bool IsParentBusy { get; set; }
}
// I.e. some method where you are handling the BackgroundWorker
void button1_Click(object sender, EventArgs e)
{
// ...some other initialization...
bwRefreshGalleries.RunWorkerCompleted += (sender1, e1) =>
{
menuItem1.IsParentBusy = false;
};
menuItem1.ParentIsBusy = true;
bwRefreshGalleries.RunAsync();
}
If you already have a handler for the RunWorkerCompleted event, then just put the statement to set the IsParentBusy property there instead of adding another handler.
Then instead of using the Application.UseWaitCursor property, you can just look at the IsParentBusy property.
There are other mechanisms you could use; I do agree with the general sentiment that the MenuItem control should not be tied to your specific Form sub-class. If for some reason the above doesn't work in your case, you need to elaborate on your question: provide a good code example and explain exactly why simply having the container of the control manage its state directly doesn't work for you
I would like to set the one template for edit/insert and view in my custom FormView control . But i got these odd exception
Unable to cast object of type 'System.Web.UI.LiteralControl' to type 'System.Web.UI.WebControls.Table'.
public class CustomFormView : FormView
{
[PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(FormView), BindingDirection.TwoWay)]
public IBindableTemplate FormTemplate { get; set; }
protected override void OnInit(EventArgs e)
{
ChangeMode(FormViewMode.Edit);
if (FormTemplate != null)
{
if (CurrentMode == FormViewMode.Edit)
{
FormTemplate.InstantiateIn(this);
}
}
base.OnInit(e);
}
}
edited :
in the first step , I created the new user control and added a formview ("FV")
public partial class Form : UserControl
{
private IBindableTemplate _template = null;
[PersistenceMode(PersistenceMode.InnerProperty),
TemplateContainer(typeof(FormView), System.ComponentModel.BindingDirection.TwoWay)]
public IBindableTemplate FormTemplate { set;get }
protected void Page_Init()
{
if (FormTemplate != null)
{
FV.InsertItemTemplate = FV.EditItemTemplate = FormTemplate;
if (!IsPostBack) FormTemplate.InstantiateIn(FV);
}
}
}
Now , I want to convert this user control to web control .
I would appreciate it if you could reply my question.
What exactly are you trying to do??
Whatever it is you're trying to do, you're doing it wrong.
TemplateContainer(typeof(FormView)) This is not possible.
You need to provide your own type there, inheriting from IDataItemContainer.
Edit:
I wouldn't recommend putting all this effort just because you want to have 1 template for edit and insert. Better put the same contents in both templates. Experience learns that over time you will want distinct functionallity for edit and insert.
I was just wondering if I'm doing this the correct way. I have 2 forms a parent form and a child form (options dialog). To change a property in my parent form from my child form I use code like this:
// Create an array of all rich textboxes on the parent form.
var controls = this.Owner.Controls.OfType<RichTextBox>();
foreach (var item in controls) {
if (chkDetectUrls.Checked)
((RichTextBox)item).DetectUrls = true;
else
((RichTextBox)item).DetectUrls = false;
}
I only have one RichTextBox on my form. It seems silly to have to loop through a array of 1 control. Is this the correct way to do it or is there an easier way?
It's not appropriate to change properties in a parent form at all. Instead, your child form should raise an event which the parent form listens for, and changes its own value accordingly.
Manipulating the parent form from the child creates a two-way coupling - the parent form owns the child, but the child also has intimate knowledge and dependency on the parent form. Bubbling is the established solution to this, as it allows information to flow upwards ('bubbling') while avoiding any strict coupling.
Here is the most basic example of eventing. It does not include passing specific information in the event (which is what you may need) but covers the concept.
In your child form:
//the event
public event EventHandler SomethingHappened;
protected virtual void OnSomethingHappened(EventArgs e)
{
//make sure we have someone subscribed to our event before we try to raise it
if(this.SomethingHappened != null)
{
this.SomethingHappened(this, e);
}
}
private void SomeMethod()
{
//call our method when we want to raise the event
OnSomethingHappened(EventArgs.Empty);
}
And in your parent form:
void OnInit(EventArgs e)
{
//attach a handler to the event
myChildControl.SomethingHappened += new EventHandler(HandleSomethingHappened);
}
//gets called when the control raises its event
private void HandleSomethingHappened(object sender, EventArgs e)
{
//set the properties here
}
As I said above, you probably need to pass some specific information in your event. There's a few ways we can do this, but the simplest one is to create your own EventArgs class and your own delegate. It looks like you need to specify whether some value is set to true or false, so let's use that:
public class BooleanValueChangedEventArgs : EventArgs
{
public bool NewValue;
public BooleanValueChangedEventArgs(bool value)
: base()
{
this.NewValue = value;
}
}
public delegate void HandleBooleanValueChange(object sender, BooleanValueChangedEventArgs e);
We can change our event to use these new signatures:
public event HandleBooleanValueChange SomethingHappened;
And we pass our custom EventArgs object:
bool checked = //get value
OnSomethingHappened(new BooleanValueChangedEventArgs(checked));
And we change our event handling in the parent accordingly:
void OnInit(EventArgs e)
{
//attach a handler to the event
myChildControl.SomethingHappened += new HandleBooleanValueChange(HandleSomethingHappened);
}
//gets called when the control raises its event
private void HandleSomethingHappened(object sender, BooleanValueChangedEventArgs e)
{
//set the properties here
bool value = e.NewValue;
}
This works, but is it the proper way to do it???
I have a custom server control that has an [input] box on it. I want it to kinda mimic the ASP.NET TextBox, but not completely. When the textbox is rendered i have a javascript that allows users to select values that are then placed in that input box.
I have a public text property on the control. In the get/set i get/set the viewstate for the control - that part is easy, but when the control is populated via the javascript, the Text get is not actually called, what is the proper way to set this exposed property using JavaScript (or even if the user just types in the box) ?
Edit:
In the OnInit i ensure the state is maintained by reaching into the form values.
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (HttpContext.Current.Request.Form["MyInputBoxValue"] != "")
{
ViewState["MyInputBoxValue"]
= HttpContext.Current.Request.Form["MyInputBoxValue"];
}
}
Then to get the value actually back in place in the HtmlTextWrite, i do this:
protected override void RenderContents(HtmlTextWriter output)
{
// There is an input control here and i set its value property
// like this using the Text internal defined.
output.Write("<input value=" + Text + ">.....
}
thanks
I find using IStateManager works the best.
For example:
partial class MyControl : System.Web.UI.UserControl, IStateManager
{
[Serializable()]
protected struct MyControlState
{
public bool someValue;
public string name;
}
protected MyControlState state;
public bool someValue {
get { return state.someValue; }
set { state.someValue = value; }
}
public bool IsTrackingViewState {
get { return true; }
}
protected override void LoadViewState(object state)
{
if ((state != null) && state is MyControlState) {
this.state = state;
}
}
protected override object SaveViewState()
{
return state;
}
protected override void TrackViewState()
{
base.TrackViewState();
}
}
getDefaultState() would just load some sane defaults into a new state struct. state gets tracked in the viewstate of the page, and ASP will take care of bring it in/out for you.
(above code ported from VB and not checked, hopefully I didn't make any errors but it should get the point across anyways)
If you need to maintain state on postback, you must provide your own methods
of recording what the user has done with your control on the client side
and either update the server control later on the server with
the changes, or redo the changes on the client side when the page refreshes.
Hmm I wonder if we can use the existing ASP.NET persistence... try inheriting from the most basic post-back state persisting control (I'm thinking asp:hidden). Then just override the render and add all the jazz you want.
Let me know if it works, then I won't have to test :)