Enable Disable save button during Validation using IDataErrorInfo - c#

How to disable/enable a button while doing validation using IDataErrorInfo?
I am using MVVM using GalaSoft light Framework. In my Model class I have implemented IDataErrorInfo to display the error messages.
public string this[string columnName]
{
get
{
Result = null;
if (columnName == "FirstName")
{
if (String.IsNullOrEmpty(FirstName))
{
Result = "Please enter first name";
}
}
else if (columnName == "LastName")
{
if (String.IsNullOrEmpty(LastName))
{
Result = "Please enter last name";
}
}
else if (columnName == "Address")
{
if (String.IsNullOrEmpty(Address))
{
Result = "Please enter Address";
}
}
else if (columnName == "City")
{
if (String.IsNullOrEmpty(City))
{
Result = "Please enter city";
}
}
else if (columnName == "State")
{
if (State == "Select")
{
Result = "Please select state";
}
}
else if (columnName == "Zip")
{
if (String.IsNullOrEmpty(Zip))
{
Result = "Please enter zip";
}
else if (Zip.Length < 6)
{
Result = "Zip's length has to be at least 6 digits!";
}
else
{
bool zipNumber = Regex.IsMatch(Zip, #"^[0-9]*$");
if (zipNumber == false)
{
Result = "Please enter only digits in zip";
}
}
}
else if (columnName == "IsValid")
{
Result = true.ToString();
}
return Result;
}
}
Screenshot: http://i.stack.imgur.com/kwEI8.jpg
How to disable/enable save button. Kindly suggest?
Thanks

The Josh Smith Way of doing this is to create the following methods in the Model:
static readonly string[] ValidatedProperties =
{
"Foo",
"Bar"
};
/// <summary>
/// Returns true if this object has no validation errors.
/// </summary>
public bool IsValid
{
get
{
foreach (string property in ValidatedProperties)
{
if (GetValidationError(property) != null) // there is an error
return false;
}
return true;
}
}
private string GetValidationError(string propertyName)
{
string error = null;
switch (propertyName)
{
case "Foo":
error = this.ValidateFoo();
break;
case "Bar":
error = this.ValidateBar();
break;
default:
error = null;
throw new Exception("Unexpected property being validated on Service");
}
return error;
}
The ViewModel then contains a CanSave Property that reads the IsValid property on the Model:
/// <summary>
/// Checks if all parameters on the Model are valid and ready to be saved
/// </summary>
protected bool CanSave
{
get
{
return modelOfThisVM.IsValid;
}
}
Finally, if you are using RelayCommand, you can set the predicate of the command to the CanSave property, and the View will automatically enable or disable the button. In the ViewModel:
/// <summary>
/// Saves changes Command
/// </summary>
public ICommand SaveCommand
{
get
{
if (_saveCommand == null)
_saveCommand = new RelayCommand(param => this.SaveChanges(), param => this.CanSave);
return _saveCommand;
}
}
And in the View:
<Button Content="Save" Command="{Binding Path=SaveCommand}"/>
And that's it!
PS: If you haven't read Josh Smith's article yet, it will change your life.

you can add add a boolean property CanSave and set it at the end of your valiation method. Bind the IsEnabled from your button to IsValid.
Somthing like this:
public bool CanSave
{
get{ return canSave; }
set{ canSave = value; RaisePropertyChanged( "CanSave" ); }
}
private bool canSave;
public string this[string columnName]
{
//....
CanSave = Result == String.Empty;
}
//xaml
<Button IsEnabled={Binding Path=CanSave}>Save</Button>

Here is my way of doing it using a combination of IDataErrorInfo interface, ValidationErrors Dictionary, and MVVM-Light messaging system. Straight forward and works like charm:
Model Class
public Dictionary<string, string> ValidationErrors = new Dictionary<string, string>();
public string this[string columnName]
{
get
{
// Remove Property error from ValidationErrors prior to any validation
ValidationErrors.Remove(propertyName);
//----------------------------------------
string Result = null;
if (columnName == "FirstName")
{
if (String.IsNullOrEmpty(FirstName))
{
// Add Property error to ValidationErrors Dic
ValidationErrors[propertyName] = Result = "Please enter first name";
//----------------------------------------
}
}
else if (columnName == "LastName")
{
if (String.IsNullOrEmpty(LastName))
{
// Add Property error to ValidationErrors Dic
ValidationErrors[propertyName] = Result = "Please enter last name";
//----------------------------------------
}
}
// Send MVVM-Light message and receive it in the Code Behind or VM
Messenger.Default.Send<PersonInfoMsg>(new PersonInfoMsg());
//----------------------------------------
return Result;
}
}
View Code Behind
public partial class PersonInfoView : UserControl
{
public PersonInfoView()
{
InitializeComponent();
Messenger.Default.Register<PersonInfoMsg>(this, OnErrorMsg);
}
private void OnErrorMsg(PersonInfoMsg)
{
// In case of DataGrid validation
foreach (PersonInfoModel p in GridName.ItemsSource)
{
if (p.ValidationErrors.Count == 0)
SaveBtn.IsEnabled = true;
else
SaveBtn.IsEnabled = false;
}
}
}

Related

Add ToolTip to "Word" in a context menu

I want to add a tooltip to a menu item. On the menu there is the word "DELETE" and when the mouse hovers over the word I want a tooltip displayed. I though of using 'ToolTipService.SetToolTip();'.
This is where the items contained in the menu are set...
protected virtual void SetContextMenuItems()
{
// -- Add condition for ReadOnly + ReadOnly Attribute to AreaEntity
if (this.ViewMode == Common.Core.ViewModes.RealTime)
{
AreaEntity ae = viewModel.EntityViewContext as AreaEntity;
if (((UserContext.Instance.IsAdmin() && (ae.Scope.Value == "global" || ae.Scope.Value == string.Empty)) ||
ae.OwnerPosition.Value == CoreServices.Instance.CurrentPosition.Configuration.Name)
&& !((this.MapInstance.Parent as Grid).Parent is PIPMap))
{
menuItem = new ContextMenuItem();
//menuItem.DisplayText = "Delete"; // -- Could be dynamic based off type "Edit Polygon (Circle, etc.)"
menuItem.DisplayText = CoreServices.Instance.GetString("Delete");
cmd = new MR.CommandBridge.Common.Command.DelegateCommand(DeleteShape, CanDelete);
menuItem.Command = cmd;
this.ContextMenu.MenuItems.Add(menuItem);
}
}
}
Methods 'DeleteShape' and 'CanDelete':
public void DeleteShape(object param)
{
EntityStore.Instance.DeleteEntity(this.ViewModel.EntityViewContext);
}
public bool CanDelete(object param)
{
GetRulesForShape();
bool isInFilter = false;
EntityCollection<Entity> lists = EntitySync.Instance.Cache["entityCollection"];
foreach (Entity list in lists)
{
isInFilter = (list as ListEntity).FilterList.Filters.Count(a => (a.FilterType == FilterTypes.WithinZone && a.Value == this.viewModel.EntityViewContext.Uri) ||
(a.FilterType == FilterTypes.MultipleFilter && a.Filters.Count(b => b.FilterType == FilterTypes.WithinZone && b.Value == this.viewModel.EntityViewContext.Uri) > 0)) > 0;
if (isInFilter) break;
}
return !HasRules && !CoreServices.Instance.ZoneFilters.Contains(this.viewModel.Area.Uri) && gfEditor.dm != GeofenceEditor.DrawMode.DrawEdit && !isInFilter;
}
Ok I made some adjustments to your class.
Somehow I got the feeling your mixing up things like control and bindings.
We'll see. ;)
I've also made some comments, maybe you can shed some light over then.
public class ContextMenuItem : MenuItem
{
public ContextMenuItem()
:base()
{
}
//Replace by Header
//
//public string DisplayText { get; set; }
//Can this be replaced by build in CommandParameter
//
private Dictionary<string, object> _parameters = new Dictionary<string, object>();
private Func<ContextMenuItem, List<ContextMenuItem>> _getMenuItems = null;
//Already available
//public DelegateCommand Command { get; set; }
//What does this function do?
public Func<ContextMenuItem, List<ContextMenuItem>> GetMenuItems
{
get
{
return _getMenuItems;
}
set
{
_getMenuItems = value;
}
}
public Dictionary<string, object> Parameters
{
get
{
return _parameters;
}
}
//Can be replaced by base Items
//
//private List<ContextMenuItem> _menuItems = new List<ContextMenuItem>();
//public List<ContextMenuItem> ChildMenuItems
//{
// get
// {
// return _menuItems;
// }
//}
private bool _isChecked = false;
public bool IsChecked
{
get { return _isChecked; }
set { _isChecked = value; }
}
// -- Command or implementer could provide a handler for all commands - might be simpler for now
// -- I think there could be a better way to route commands but I'll thin on it.
Could this simply be done in .css?
.yourclass:hover{
cursor:pointer;
}
or target it with jquery?
Have you tried this?
menuitem.ToolTip = "Delete";
Normally a contextmenu can exist of regular MenuItems. I used it often.
;)
Context menu items have the ToolTipText property:
menuItem.ToolTipText = "ToolTip Text Here";

use string value of mvvm message as variable in viewmodel

I'm filtering a listcollectionview in viewModel 1 based on selectionchanged of datagrid in viewModel 2. To do this I use mvvm messaging. Each time the selection of the datagrid changes a message is send to update my listcollectionview. This all works well.
Now I need to use the string value of this message to pass into the filter. The problem is that I can only use the string value in the updateShotList method but not into the bool IsMatch. How can I make this work, or how can I use the string value of the message as a variable in my viewmodel.
This is how my viewmodel looks like.
private ObservableCollection<Shot> _allShots = new ObservableCollection<Shot>();
public ObservableCollection<Shot> AllShots
{
get { return _allShots; }
set { _allShots = value; RaisePropertyChanged();}
}
private ListCollectionView _allShotsCollection;
public ListCollectionView AllShotsCollection
{
get
{
if (_allShotsCollection == null)
{
_allShotsCollection = new ListCollectionView(this.AllShots);
}
return _allShotsCollection;
}
set
{
_allShotsCollection = value; RaisePropertyChanged();
}
}
private void UpdateShotList(string SceneNr) // value sceneNr comes from message from viewmodel 2
{
_allShotsCollection.IsLiveFiltering = true;
_allShotsCollection.Filter = new Predicate<object>(IsMatchFound);
}
bool IsMatchFound(object obj)
{
=====> if (obj as Shot != null && (obj as Shot).SceneNumber == "?????") // Here I need the value of string ScenNr that comes from the message.
{
return true;
}
return false;
}
public ShotListViewModel()
{
Messenger.Default.Register<string>(this, "UpdateShotList", UpdateShotList);
}
You can create your predicate as a lambda expression and close over SceneNr to capture it:
_allShotsCollection.Filter = o =>
{
var shot = o as Shot;
return shot != null && shot.SceneNumber == SceneNr;
};
Alternatively, simply introduce an instance variable to contain your filter string and update it each time you receive a message:
private string _sceneNr;
private void UpdateShotList(string sceneNr)
{
// ...
_sceneNr = sceneNr;
}
bool IsMatchFound(object obj)
{
var shot = obj as Shot;
return shot != null && shot.SceneNumber == _sceneNr;
}

How can I clear/prevent binding to datagrid if duplicate exists?

Here's my property that determines if I should bind the other properties or not:
public string LotNumber {
get {
return lotNumber;
}
set {
using (var db = new DDataContext()) {
lotNumber = value;
// Check for duplicates
bool isDuplicate =
db.LotInformation.Any(r => r.lot_number == lotNumber);
if (isDuplicate == true) {
ComponentsList = null;
FamiliesList = null;
ExpirationDate = null;
LotNumber = null;
lotNumber = null;
// Inform user that the lot_number already exists
errorWindow.Message =
LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(
LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(
LanguageResources.Resource.Lot_Exists_Already);
return;
} else {
lotNumber = value;
}
RaisePropertyChanged("LotNumber");
}
}
}
My problem right now is if I upload a file and if the lot number already exists in the database, the boolean returns true and an error message is thrown. However, after that,it loops again and then the boolean is set to false since now the value is null and it still binds the data afterward. How can I break out of the loop and just make it stop running/clear/prevent binding when bool is true in the case above?
I assume you have some code like this:
LotNumber = "ABC5"; // ABC5 already exists in the database - uh oh!
And then you're trying to figure everything out in the "setter". It's already too late by that point. Instead, move your logic into separate methods:
private bool LotNumberExists(string lotNumber)
{
using (var db = new DDataContext())
{
return db.LotInformation.Any(r => r.lot_number == lotNumber);
}
}
private void ClearFields()
{
ComponentsList = null;
FamiliesList = null;
ExpirationDate = null;
LotNumber = null;
}
private void InformUserOfDuplicate()
{
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
}
Then check the return value of that method before setting LotNumber.
private void SomeOtherMethod()
{
string someLotNumber = "ABC5";
if (LotNumberExists(someLotNumber)
{
ClearFields();
InformUserOfDuplicate();
return;
}
LotNumber = someLotNumber;
}
Turn your setter back into a simple setter without a ton of logic wrapped up in it:
public string LotNumber
{
get { return lotNumber; }
set
{
lotNumber = value;
RaisePropertyChanged("LotNumber");
}
}

RaisePropertyChanged not updating UI

I'm having trouble getting my UI to update Two Listboxes' to update properly when my ViewModel changes.
First, the basic logic behind the page:
Movie is an object with a title, and a variety of MovieDetails. Some MovieDetail are different than others, as they are detailed which is a glorified way of saying they're more Important.
I use two ListBoxes to separate these MovieDetails into stacked ListBoxes, one for 'Detailed' and one for 'NotDetailed'
If a movie has no 'Detailed' attributes, the corresponding list is Hidden via a BooleanToVisibilityConverter (and vice-versa)
When I navigate to the page, I set the Movie the page corresponds to, and it should RaisePropertyChanged to alert the AllMoviesDetail ObservableCollection that it should re-get Movies.MovieDetailFetchedList.
From there, AllMoviesDetail would alert the two ObservableCollections (Detailed, NotDetailed) they should be re-get.
In fact, RaisePropertyChanged on NotDetailedMovieDetails or DetailedMovieDetails does not seem to do anything either. (And the corresponding HasNotDetailedMovieDetails, Has...)
What does work, however, is if I add more items into the list, the CollectionChanged event seems to fire and reactivate the list. I have also been able to do this by instantiating the ObservableCollections in code first var temp = DetailedMoviesDetail;
public class MoviesDetailViewModel : ViewModelBase
{
#region Property Names
public const string MoviePropertyString = "Movie";
public const string AllMoviesDetailPropertyString = "AllMoviesDetail";
public const string DetailedMoviesDetailPropertyString = "DetailedMoviesDetail";
public const string NotDetailedMoviesDetailPropertyString = "NotDetailedMoviesDetail";
public const string HasNotDetailedMoviesDetailPropertyString = "HasNotDetailedMoviesDetail";
public const string HasDetailedMoviesDetailPropertyString = "HasDetailedMoviesDetail";
public const string NotDetailedHeaderPropertyString = "NotDetailedHeader";
#endregion
public MoviesDetailViewModel()
{
if (IsInDesignMode)
{
Movie = DesignDataStore.MovieList[0];
Movie.Category = Category.DDA;
}
}
private Movie _Movie;
/// <summary>
/// The Movie for which to browse MoviesDetail. It is expected when setting this property, that MoviesDetail for it have been downloaded previously.
/// </summary>
/// <remarks>The 'Master' property for this ViewModel. All properties are Dependent on this and the underlying property MoviesDetailList</remarks>
/// <seealso cref="MovieDetailFetchedList"/>
public Movie Movie
{
get { return _Movie; }
set
{
if (_Movie != value)
{
if (_Movie != null)
_Movie.MovieDetailFetchedList.CollectionChanged -= MoviesDetailListChanged;
_Movie = value;
RaisePropertyChanged(MoviePropertyString);
RaisePropertyChanged(StatementPeriodAvailablePropertyString);
RaisePropertyChanged(NotDetailedMoviesDetailPropertyString);
Movie.MovieDetailFetchedList.CollectionChanged += MoviesDetailListChanged;
RaisePropertyChanged(AllMoviesDetailPropertyString);
RaisePropertyChanged(DetailedMoviesDetailPropertyString);
RaisePropertyChanged(NotDetailedHeaderPropertyString);
}
}
}
private void MoviesDetailListChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (var item in e.NewItems)
{
if (((MovieDetail) item).IsDetailed())
DetailedMoviesDetail.Add(item as MovieDetail);
else
NotDetailedMoviesDetail.Add(item as MovieDetail);
}
}
else
{
RaisePropertyChanged(AllMoviesDetailPropertyString);
RaisePropertyChanged(DetailedMoviesDetailPropertyString);
RaisePropertyChanged(NotDetailedMoviesDetailPropertyString);
}
}
#endregion
private MovieDetailFetchedList _allMoviesDetail;
public MovieDetailFetchedList AllMoviesDetail
{
get
{
if (Movie == null)
return new MovieDetailFetchedList();
return _allMoviesDetail ?? (AllMoviesDetail = Movie.MovieDetailFetchedList);
}
set
{
if (_allMoviesDetail != value)
{
if (_allMoviesDetail != null)
_allMoviesDetail.CollectionChanged -= MoviesDetailListChanged;
_allMoviesDetail = value;
_allMoviesDetail.CollectionChanged += MoviesDetailListChanged;
RaisePropertyChanged(AllMoviesDetailPropertyString);
//force update
DetailedMoviesDetail = NotDetailedMoviesDetail = null;
RaisePropertyChanged(DetailedMoviesDetailPropertyString);
RaisePropertyChanged(HasDetailedMoviesDetailPropertyString);
RaisePropertyChanged(NotDetailedMoviesDetailPropertyString);
RaisePropertyChanged(HasNotDetailedMoviesDetailPropertyString);
}
}
}
public bool HasNotDetailedMoviesDetail { get { return NotDetailedMoviesDetail != null && NotDetailedMoviesDetail.Count > 0; } }
private ObservableCollection<MovieDetail> _notDetailedMoviesDetail;
public ObservableCollection<MovieDetail> NotDetailedMoviesDetail
{
get
{
if (Movie == null) return new ObservableCollection<MovieDetail>();
return AllMoviesDetail;
return _notDetailedMoviesDetail ?? //make sure RaisePropertyChanged happens by using property setter
(NotDetailedMoviesDetail = AllMoviesDetail.Where(mem => !mem.IsDetailed()).ToObservableCollection());
}
set
{
_notDetailedMoviesDetail = value;
RaisePropertyChanged(NotDetailedMoviesDetailPropertyString);
RaisePropertyChanged(HasNotDetailedMoviesDetailPropertyString);
}
}
public bool HasDetailedMoviesDetail
{ get { return DetailedMoviesDetail != null && DetailedMoviesDetail.Count > 0; } }
private ObservableCollection<MovieDetail> _DetailedMoviesDetail;
public ObservableCollection<MovieDetail> DetailedMoviesDetail
{
get
{
if (Movie == null) return new ObservableCollection<MovieDetail>();
return AllMoviesDetail;
return _DetailedMoviesDetail ?? //make sure RaisePropertyChanged happens by using property setter
(DetailedMoviesDetail = AllMoviesDetail.Where(mem => mem.IsDetailed()).ToObservableCollection());
}
set
{
_DetailedMoviesDetail = value;
RaisePropertyChanged(DetailedMoviesDetailPropertyString);
RaisePropertyChanged(HasDetailedMoviesDetailPropertyString);
}
}
private string _DetailedHeader;
public string DetailedHeader
{
get { return _DetailedHeader ?? (_DetailedHeader = AppResources.in_available); }
set { _DetailedHeader = value; }
}
public string NotDetailedHeader
{
get { return (Movie != null && Movie.Category == Category.DRAMA) ? AppResources.Movie_MoviesDetail : AppResources.not_in_available; }
}
}
All of your property getters (except AllMoviesDetail) have two return statements. Since only the first will be executed, the values are not being assigned and the PropertyChanged events are not being twiggered.

ASP.NET Server control with an additional bindable field

I have created a custom server control, deriving from System.Web.Contols.CheckBoxList to customize how a CheckBoxList is rendered. I also wanted to add another bindable field and get the value of the field within the CheckBoxList.RenderItem() method. The field I want to create, should contain a value specifying whether a CheckBoxListItem is checked. I've read some articles regarding custom DataFields, but it never gets explained in detail.
I've included a portion of my class to better explain what I can't seem to understand.
public class ListedCheckBoxList : CheckBoxList
{
protected override void RenderItem(ListItemType itemType, int repeatIndex, RepeatInfo repeatInfo, HtmlTextWriter writer)
{
if (itemType != ListItemType.Item)
return;
var item = base.Items[repeatIndex];
string cbxHtml = string.Format("<input type=\"checkbox\" value=\"{0}\" name=\"{1}\" /> {2}",
item.Value,
string.Concat(this.ClientID, repeatIndex),
item.IsChecked, // <-- My custom bindable field
item.Text);
writer.Write(cbxHtml);
}
}
When using this control in the .aspx page, I'm attempting to bind it like this
<abc:ListedCheckBoxList ID="cbxList" runat="server"
DataValueField="UserId"
DataTextField="UserFullName"
DataIsCheckedField="UserIsActive" />
Here is a version I wrote a year or so ago. I wanted to be able to bind the checked status as well as a tooltip for the individual items. Hope it helps...
public class CheckBoxList_Extended : CheckBoxList
{
/// <summary>
/// Gets or sets the name of the data property to bind to the tooltip attribute of the individual CheckBox.
/// </summary>
[DefaultValue("")]
public string DataTooltipField
{
get
{
string value = base.ViewState["DataTooltipField"] as string;
if (value == null)
value = "";
return value;
}
set
{
if (value == null || value.Trim() == "")
{
base.ViewState.Remove("DataTooltipField");
}
else
{
base.ViewState["DataTooltipField"] = value.Trim();
}
}
}
/// <summary>
/// Gets or sets the name of the data property to bind to the Checked property of the individual CheckBox.
/// </summary>
[DefaultValue("")]
public string DataCheckedField
{
get
{
string value = base.ViewState["DataCheckedField"] as string;
if (value == null)
value = "";
return value;
}
set
{
if (value == null || value.Trim() == "")
{
base.ViewState.Remove("DataCheckedField");
}
else
{
base.ViewState["DataCheckedField"] = value.Trim();
}
}
}
protected override void PerformDataBinding(System.Collections.IEnumerable dataSource)
{
if (dataSource != null)
{
string dataSelectedField = this.DataCheckedField;
string dataTextField = this.DataTextField;
string dataTooltipField = this.DataTooltipField;
string dataValueField = this.DataValueField;
string dataTextFormatString = this.DataTextFormatString;
bool dataBindingFieldsSupplied = (dataTextField.Length != 0) || (dataValueField.Length != 0);
bool hasTextFormatString = dataTextFormatString.Length != 0;
bool hasTooltipField = dataTooltipField.Length != 0;
bool hasSelectedField = dataSelectedField.Length != 0;
if (!this.AppendDataBoundItems)
this.Items.Clear();
if (dataSource is ICollection)
this.Items.Capacity = (dataSource as ICollection).Count + this.Items.Count;
foreach (object dataItem in dataSource)
{
ListItem item = new ListItem();
if (dataBindingFieldsSupplied)
{
if (dataTextField.Length > 0)
{
item.Text = DataBinder.GetPropertyValue(dataItem, dataTextField, null);
}
if (dataValueField.Length > 0)
{
item.Value = DataBinder.GetPropertyValue(dataItem, dataValueField, null);
}
}
else
{
if (hasTextFormatString)
{
item.Text = string.Format(CultureInfo.CurrentCulture, dataTextFormatString, new object[] { dataItem });
}
else
{
item.Text = dataItem.ToString();
}
item.Value = dataItem.ToString();
}
if (hasSelectedField)
{
item.Selected = (bool)DataBinder.GetPropertyValue(dataItem, dataSelectedField);
}
if (hasTooltipField)
{
string tooltip = DataBinder.GetPropertyValue(dataItem, dataTooltipField, null);
if (tooltip != null && tooltip.Trim() != "")
{
item.Attributes["title"] = tooltip;
}
}
this.Items.Add(item);
}
}
base.PerformDataBinding(null);
}
}
Checkbox already has a property for that, "Checked"
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.checkbox.checked.aspx
You can add a custom fairly easily though, just add a new public property. You can then set it programatically or in the aspx code.
public class ListedCheckBoxList : CheckBoxList
{
public string CustomTag { get; set; }
//...snip
}
<myControls:myCheckBox runat='server' Checked='True' CustomTag="123test" />

Categories

Resources