Label in ListViewDataItem setting property affects all labels - asp.net

I have a listview called "lvQuestions" which has a label (called lblMissingField) on it and a couple of other fields. When the user tries to postback the page, I call a routine that first sets ALL lvlMissingField.Text = string.empty and then validates that "the other" fields in the ListViewDataItem have been filled in. If they haven't been filled I set the particular listview's lvlMissingField.Text = "*" to visually mark them with a "*" (Note: standard asp.net validators are not used).
So, what happens? Every lblMissingField.Visible is displayed, what am I missing here?
foreach(ListViewDataItem question in unansweredQuestions)
{
((Label)question.FindControl("lblMissingField")).Text = "*";
}
I've made that the unansweredQuestions are only those ListViewDataItems that I want, and still am suprised to see the output HTML sets all of them to "*"...?
I've also tried Setting Visible = true instead of setting the Text to '*' (which was my original preference) without any luck.
Going a little mad here...

When I have needed to do functionality similar to what you are doing and the updates / checks were done server side, I have implemented a DataBinding method for each control I want to customize. When something changes, I rebind to my model with the bits and pieces updated that need to be changed.
So for example in your case assume I am binding to an list of objects (could be DataTable or whatever) that has the following details:
public class MyData
{
public string Question { get; set; }
public string Answer { get; set; }
}
public List<MyData> listOfMyData = GetListOfMyData();
Then my databinding event on the label would be implemented
<asp:Label ID="lblMissingField" runat="server" OnDataBinding="lblMissingField_DataBinding" />
Then the implementation of the databinding:
protected void lblMissingField_DataBinding(object sender, System.EventArgs e)
{
Label lbl = (Label)(sender);
lbl.Text = (Eval("Answer").ToString() == string.Empty) ? "*" : string.Empty;
}
This gives you a lot of control on every single item in your template. Instead of dealing with all your controls after your data is loaded, this lets you have the data modified as the data is being bound.
I know it's not a solution to your exact question but maybe just a suggestion of another way to approach the problem.

Related

Observable value in validation controlsfx

I have the following code
public ValidationResult notNull(Control control, String content) {
boolean condition = content.length() <=0;
return ValidationResult.fromMessageIf(control, "Field is empty!", Severity.WARNING, condition);
}
it checks if there is any character in text field,
the i call it like this
validator = new ValidationSupport();
validator.registerValidator(itemIdTf,vals::notNull);
and finally is do this
validator.invalidProperty().addListener((observable, oldValue, newValue) -> {
itemIdTf.pseudoClassStateChanged(PseudoClass.getPseudoClass("negative"), oldValue);});
And this works, it sets the pseudo class for certain controll, how ever when i have few text field controls on same validatior it must wait for all of them to be validated before changing the pseudoclass.
so i thought to perhaps do it in the ValidationResult method, because i think using many validators is probably not good. How ever i dont know if that is possible, i need some listener that is unique to every control, not for validation result.
well i found something that works, but it still leaves some unanswered questions :
public ValidationResult notNull(Control control, String content) {
boolean condition = content.length() <=0;
control.pseudoClassStateChanged(positive,!condition);
return ValidationResult.fromMessageIf(control, "Field is empty!", Severity.ERROR, condition);
}
i am using css styling validator, so the things that come from this line do not work (at least not all of them)
return ValidationResult.fromMessageIf(control, "Field is empty!", Severity.ERROR, bp.getValue());
Here above there is string "Field is empty" which should be tooltip for control, however its never set, so i just created my own tooltip inside validation and i add it to control.
then the whole thing looks something like this :
PseudoClass positive = PseudoClass.getPseudoClass("positive");
final Tooltip notNullTooltip = new Tooltip("Must have some value");
public ValidationResult notNull(Control control, String content) {
boolean condition = content.length() <=0;
control.pseudoClassStateChanged(positive,!condition);
control.setTooltip(notNullTooltip);
return ValidationResult.fromMessageIf(control, "Field is empty!", Severity.ERROR, condition);
}
And it does work, if anyone has more elegant solution i would be thankfull.

Silverlight Custom Control and Databinding doesn't work properly in WP7

I'm trying to create my calendar control with databinding.
public partial class Calendar : UserControl
{
public static readonly DependencyProperty DateProperty =
DependencyProperty.Register("Date", typeof(DateTime),
typeof(Calendar), null);
public object Date
{
get { return GetValue(DateProperty); }
set
{
SetValue(DateProperty, value);
OnPropertyChanged("Date");
}
}
public Calendar()
{
// Required to initialize variables
InitializeComponent();
DayText.Text = ((DateTime)Date).ToString("dd");
MonthText.Text = ((DateTime)Date).ToString("MMM");
this.Loaded += new RoutedEventHandler(Calendar_Loaded);
this.GotFocus += new RoutedEventHandler(Calendar_Loaded);
}
void Calendar_Loaded(object sender, RoutedEventArgs e)
{
DayText.Text = ((DateTime)Date).ToString("dd");
MonthText.Text = ((DateTime)Date).ToString("MMM");
}
}
But When I create the listbox with this control, same calndar have the wrong date. I'm sure that the Date passed thorough databinding is correct but I don't understand why same calender show a different day (I'm noticed that is the day of a previous calendar control intance)
Thank you for supporting!
Hmm ... where do we start? Here's a few things I've noticed:
If you're using a dependency property, there's no need to call OnPropertyChanged from the Date property setter.
The dependency property declares the type as DateTime, but your public exposed property is of type object, which then requires you to cast it elsewhere.
If Calendar_Loaded is to be called in more situations than in response to the Loaded event (such as the GotFocus event, then I'd recommend that you call it something else, or create a method with a relevant name (e.g. UpdateDateParts) and call it from properly named separate event handlers.
Using fixed format specifiers when processing date strings does not localize well.
In addition to that, I'd suggest that you could implement the user interface in a manner that supports databinding (and re-templating) by using bindings and exposing the date parts of the Date dependency property instead of manually updating the Text property of some text blocks/boxes in event handlers. In fact, if you derive from Control instead of UserControl then you can create and actuall lookless control that has it's user interface defined by a style in themes\generic.xaml that can be re-defined by users of your control.
As for why the date is incorrect in different instances of your calendar control, we'd need to see some of your XAML/code to see how the control is being used and initialized to be able to provide a better answer. However, I thought the above was worth putting in an Answer, instead of trying to say it in a Comment.

Why is this DropDownList data binding to a List<String> not working?

I'm trying to bind a List<String> to a DropDownList in a user control. I think I'm doing the right thing, but it seems that after my code executes the bindings are cleared. Here's the code for review!
User control:
<asp:DropDownList ID="subjectNameDropDown" runat="server"/>
<asp:DropDownList ID="yearLevelDropDown" runat="server"/>
Auto-generated designed code-behind:
public partial class NewSiteMetadataUserControl {
protected global::System.Web.UI.WebControls.DropDownList subjectNameDropDown;
protected global::System.Web.UI.WebControls.DropDownList yearLevelDropDown;
}
Code-behind:
public partial class NewSiteMetadataUserControl : UserControl
{
protected override void CreateChildControls()
{
subjectNameDropDown = new DropDownList();
yearLevelDropDown = new DropDownList();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
EnsureChildControls();
// Attempt 1
List<String> subjectNames = GetSubjectValues();
foreach (var subjectName in subjectNames)
subjectNameDropDown.Items.Add(subjectName);
subjectNameDropDown.DataBind();
// Attempt 2
List<String> yearLevels = GetYearLevelValues();
yearLevelDropDown.DataSource = yearLevels;
yearLevelDropDown.DataBind();
}
}
Should this approach work?
If it should, how can I debug what happens after the code executes?
Yes, this approach should work, here's why it currently isn't,
A DropDownList done with DataBind needs a DataSource. This is why Attempt #1 is not working.
If you're binding to a List<string>, there is no clear key/value pair to bind to. This is why when binding to a List<Person> (for example), you need to override .ToString() in the Person class to provide the key/value binding, or manually set the DataTextField, DataValueField.
There is no way for ASP.NET to work out a key/value pair for a string.
Think about what HTML you want. What should be the key/value for a simple string? Doesn't make sense does it.
Since you don't really care about the "key" (only what is displayed), i suggest you bind to a Dictionary<TKey,TValue> instead.
Either make your method return that, or iterate through the list and add them to the dictionary with an index.
The problem here was CreateChildControls. Somewhere in my attempts to make this work I added this method that initialises the controls. This isn't necessary and in fact caused the data bindings to be wiped out, as it was automatically called by the framework after OnLoad.
The solution was to remove this method and the call to EnsureChildControls.

ListBox doesn't show changes to DataSource

I thought this was a simple problem, but I can't find any information on the web. I'm binding a ListBox to a List using BindingSource like so:
List<Customer> customers = MyMethodReturningList();
BindingSource customersBindingSource = new BindingSource();
customersBindingSource.DataSource = customers;
customersListBox.DataSource = customersBindingSource;
Now, when I add or delete from customers list, my ListBox gets updated (even without using ResetBindings on BindingSource), but if I change any of the customer objects in the list, it does not. Calling ResetBindings has no effect. I even implemented my own BindingList, but the behaviour hasn't changed.
The Customer class uses properties for accessing and modification of data. Its ToString() content is displayed in the list.
I'm using C# in .Net 2.0.
Any ideas?
Thanks
If you use a BindingList you don't even need the BindingSource:
BindingList<Customer> customers = new BindingList<Customer>(MyMethodReturningList());
customersListBox.DataSource = customers;
OK, here's a dirty fix: wenever you need to refresh the box contents set datasource = null, then rebind it.
the reason it doesn't update is because the objects in the list haven't changed and it only checks the refrences of the object rather than their contents.
There is also a bug in the list box which can cause this problem. If you set the SelectionMode to None this problem appears.
As a work around I set the selection mode to One and then back to None when updating the datasource.
I got around this problem by converting data to array when updating source. Please see UpdateData method. This way you can update your combo box without losing ComboBox Settings.
class Person {
public int Id {get; set; }
public string FirstName{ get; set; }
public string SurName {get; set; }
}
public Form1()
{
InitializeComponent();
comboBox1.DisplayMember = "FirstName";
comboBox1.ValueMember = "Id";
comboBox1.DataSource = m_PersonList;
}
public void UpdateData() {
m_PersonList[0].FirstName = "Firstname1";
comboBox1.DataSource = m_PersonList.ToArray<Person>();
}
I understand that this question was asked almost 6 years ago but other than work-arounds I do not see a correct answer here.
When you change property of an item in a collection the event gets raised for the element (object) but not the collection. So the collection does not see a change and will not refresh bound controls. Elements inside all binding collections and most generic collections like List<> receive 2 events, PropertyChanging and PropertyChanged. When a property of the element inside collection is changed, the event gets triggered. All you need to do is add an event handler that would trigger either re-binding or raise an event on the Collection.

Vary by control properties using PartialCaching in ASP.NET

I am using the PartialCaching attribute on the base class of a user control.
I would like the cached controls to vary based on the properties set on the control instance.
For example:
<mycontrols:control1 runat="server" param1="10" param2="20" />
...output would be cached separately from a control instance with different properties:
<mycontrols:control1 runat="server" param1="15" param2="20" />
...and this control would be cached separately as well:
<mycontrols:control1 runat="server" param1="10" param2="25" />
However, if two control instances on two separate pages had identical param1 and param2 properties, I'd like them to cache as one object (so that cached control would be shared).
Can the above use case be achieved with PartialCaching attribute? What settings would I use? varyByControl?
Also, is it possible to make the cache duration variable at runtime?
Thanks.
To answer your first Q, let me first tell you that your question itself has the answer ;). 'Shared' ... yes that's the keyword :) To have a single instance in cache for the user control across all the pages, set Shared='true' in the #OutputCache directive. This should be set at the user control level i.e. in the ascx page.
To cache the user control based on user control properties, you should specify the fully qualified name of the properties in the varyByControls section of the PartialCachingAttribute. Multiple properties if any should be separated by semi-colons.
<%# Control Language="C#" AutoEventWireup="true"
CodeFile="WebUserControl.ascx.cs"
Inherits="UC_WebUserControl" %>
<%# OutputCache Duration="60"
VaryByControl="UC_WebUserControl.param1;UC_WebUserControl.param2"
VaryByParam="none" Shared="true" %>
or you can also include the PartialCache attribute for the user control:
[PartialCaching(60, null, "UC_WebUserControl.param1;UC_WebUserControl.param2", null, true)]
public partial class UC_WebUserControl : System.Web.UI.UserControl
{
public string param1 { get; set; }
public string param2 { get; set; }
}
OR another way to cache the control on the combination of both values would be:
[PartialCaching(60, null, "UC_WebUserControl.BothParams", null, true)]
public partial class UC_WebUserControl : System.Web.UI.UserControl
{
public string param1 { get; set; }
public string param2 { get; set; }
public string BothParams
{
get { return String.Concat(param1, param2); }
}
}
The last parameter (true) specifies shared. Duration is specified by 60. Refer to the link How to: Cache Multiple Versions of a User Control Based on Parameters
To answer your second Q, to make the cache duration for the user control variable at run time, you can do it in two ways:
Assign it in the user control code behind:
[PartialCaching(60, null, "UC_WebUserControl.BothParams", null, true)]
public partial class WebUserControl1 : System.Web.UI.UserControl
{
...
protected void Page_Load(object sender, EventArgs e)
{
this.CachePolicy.Duration = new TimeSpan(0, 0, 60);
}
}
You can assign it in the code behind of the page where user control is referenced using the ID of the user control.
e.g. If the user control on the aspx is:
<mycontrols:control1 ID="ucControl1" runat="server" param1="15" param2="20" />
then in the code behind of aspx, you should write:
this.ucControl1.CachePolicy.Duration = new TimeSpan(0, 0, 60);
FYI, if both the user control and page are cached: If the page output cache duration is less than that of a user control, the user control will be cached until its duration has expired, even after the remainder of the page is regenerated for a request. For example, if page output caching is set to 50 seconds and the user control's output caching is set to 100 seconds, the user control expires once for every two times the rest of the page expires.
What you need is Caching Multiple Versions of a User Control by Using Declarative Attributes.
I'm posting a new answer to this very old question because the accepted answer is woefully inaccurate.
This correct answer is:
There is no built-in way to vary by control property value. VaryByControl only works for controls.
When a cached version is served, your control field will be null. You can't change the cache duration in code - you would get a NullReferenceException.
There is a bug that varies cache by control ID and NamingContainer ID(s) if VaryByControl is set to any value. That is why it appears to work sometimes. The bug is right here: http://referencesource.microsoft.com/#System.Web/xsp/system/Web/UI/PartialCachingControl.cs#530
I blogged about this recently right here: http://tabeokatech.blogspot.be/2014/09/outputcache-on-user-controls.html .
A way you could make this work is call the builder method for the PartialCachingControl yourself in code-behind, and embed the property value you want to vary by in the guid parameter:
// PhControls is an asp:PlaceHolder
protected void Page_Init(object sender, EventArgs e)
{
for (int i = 0; i < 3; i++)
{
System.Web.UI.StaticPartialCachingControl.BuildCachedControl(PhControls, String.Format("Test{0}", i), String.Format("1234567{0}", i), 180, null, null, null, null, new System.Web.UI.BuildMethod(GetBuilderDelegate(i)), null);
}
}
public Func<Control> GetBuilderDelegate(int number)
{
return delegate()
{
var control = (UserControls.Test)LoadControl("~/UserControls/Test.ascx");
control.Number = number;
return control;
};
}
That neatly takes care of varying caching duration in code-behind as well. Make sure you remove the OutputCache directive from the markup in the ascx though when you do this. Otherwise the LoadControl gets you another PartialCachingControl and the cast fails.

Resources